Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto Tag Confirmation #4016

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 7 additions & 19 deletions ui/v2.5/src/components/Dialogs/IdentifyDialog/IdentifyDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,9 @@ import {
faCogs,
faFolderOpen,
faQuestionCircle,
faTriangleExclamation,
} from "@fortawesome/free-solid-svg-icons";

const autoTagScraperID = "builtin_autotag";

interface IIdentifyDialogProps {
selectedIds?: string[];
onClose: () => void;
Expand Down Expand Up @@ -228,28 +227,11 @@ export const IdentifyDialog: React.FC<IIdentifyDialogProps> = ({
// default to first stash-box instance only
const stashBox = allSources.find((s) => s.stash_box_endpoint);

// add auto-tag as well
const autoTag = allSources.find(
(s) => s.id === `${SCRAPER_PREFIX}${autoTagScraperID}`
);

const newSources: IScraperSource[] = [];
if (stashBox) {
newSources.push(stashBox);
}

// sanity check - this should always be true
if (autoTag) {
// don't set organised by default
const autoTagCopy = { ...autoTag };
autoTagCopy.options = {
setOrganized: false,
skipMultipleMatches: true,
skipSingleNamePerformers: true,
};
newSources.push(autoTagCopy);
}

setSources(newSources);
}
}, [allSources, configData]);
Expand Down Expand Up @@ -445,6 +427,12 @@ export const IdentifyDialog: React.FC<IIdentifyDialogProps> = ({
</Button>
}
>
<div>
<p>
<Icon icon={faTriangleExclamation} size="xl" />
<FormattedMessage id="config.tasks.identify.warning" />
</p>
</div>
<Form>
{selectionStatus}
<SourcesList
Expand Down
3 changes: 3 additions & 0 deletions ui/v2.5/src/components/Dialogs/IdentifyDialog/Options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ export const OptionsEditor: React.FC<IOptionsEditor> = ({
id: "config.tasks.identify.set_organized",
})}
defaultValue={defaultOptions?.setOrganized ?? undefined}
tooltip={intl.formatMessage({
id: "config.tasks.identify.organized",
})}
{...checkboxProps}
/>
</Form.Group>
Expand Down
167 changes: 42 additions & 125 deletions ui/v2.5/src/components/Settings/Tasks/DataManagementTasks.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useState } from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { Button, Col, Form, Row } from "react-bootstrap";
import { Button, Form } from "react-bootstrap";
import {
mutateMigrateHashNaming,
mutateMetadataExport,
Expand All @@ -21,110 +21,11 @@ import { SettingSection } from "../SettingSection";
import { BooleanSetting, Setting } from "../Inputs";
import { ManualLink } from "src/components/Help/context";
import { Icon } from "src/components/Shared/Icon";
import { ConfigurationContext } from "src/hooks/Config";
import { FolderSelect } from "src/components/Shared/FolderSelect/FolderSelect";
import {
faMinus,
faPlus,
faQuestionCircle,
faTrashAlt,
} from "@fortawesome/free-solid-svg-icons";

interface ICleanDialog {
pathSelection?: boolean;
dryRun: boolean;
onClose: (paths?: string[]) => void;
}

const CleanDialog: React.FC<ICleanDialog> = ({
pathSelection = false,
dryRun,
onClose,
}) => {
const intl = useIntl();
const { configuration } = React.useContext(ConfigurationContext);

const libraryPaths = configuration?.general.stashes.map((s) => s.path);

const [paths, setPaths] = useState<string[]>([]);
const [currentDirectory, setCurrentDirectory] = useState<string>("");

function removePath(p: string) {
setPaths(paths.filter((path) => path !== p));
}

function addPath(p: string) {
if (p && !paths.includes(p)) {
setPaths(paths.concat(p));
}
}

let msg;
if (dryRun) {
msg = (
<p>{intl.formatMessage({ id: "actions.tasks.dry_mode_selected" })}</p>
);
} else {
msg = (
<p>{intl.formatMessage({ id: "actions.tasks.clean_confirm_message" })}</p>
);
}

return (
<ModalComponent
show
icon={faTrashAlt}
disabled={pathSelection && paths.length === 0}
accept={{
text: intl.formatMessage({ id: "actions.clean" }),
variant: "danger",
onClick: () => onClose(paths),
}}
cancel={{ onClick: () => onClose() }}
>
<div className="dialog-container">
<div className="mb-3">
{paths.map((p) => (
<Row className="align-items-center mb-1" key={p}>
<Form.Label column xs={10}>
{p}
</Form.Label>
<Col xs={2} className="d-flex justify-content-end">
<Button
className="ml-auto"
size="sm"
variant="danger"
title={intl.formatMessage({ id: "actions.delete" })}
onClick={() => removePath(p)}
>
<Icon icon={faMinus} />
</Button>
</Col>
</Row>
))}

{pathSelection ? (
<FolderSelect
currentDirectory={currentDirectory}
setCurrentDirectory={(v) => setCurrentDirectory(v)}
defaultDirectories={libraryPaths}
appendButton={
<Button
variant="secondary"
onClick={() => addPath(currentDirectory)}
>
<Icon icon={faPlus} />
</Button>
}
/>
) : undefined}
</div>

{msg}
</div>
</ModalComponent>
);
};
import { DirectorySelectionDialog } from "./DirectorySelectionDialog";

interface ICleanOptions {
options: GQL.CleanMetadataInput;
Expand Down Expand Up @@ -232,6 +133,45 @@ export const DataManagementTasks: React.FC<IDataManagementTasks> = ({
return <ImportDialog onClose={() => setDialogOpen({ import: false })} />;
}

function renderCleanDialog() {
if (dialogOpen.cleanAlert || dialogOpen.clean) {
let msg: string;
if (cleanOptions.dryRun) {
msg = intl.formatMessage({ id: "actions.tasks.dry_mode_selected" });
} else {
msg = intl.formatMessage({ id: "actions.tasks.clean_confirm_message" });
}

return (
<DirectorySelectionDialog
allowPathSelection={dialogOpen.clean}
message={msg}
header={intl.formatMessage({ id: "actions.clean" })}
icon={faTrashAlt}
acceptButtonText={intl.formatMessage({ id: "actions.clean" })}
acceptButtonVariant="danger"
onClose={(p) => {
// undefined means cancelled
if (p !== undefined) {
if (dialogOpen.cleanAlert) {
// don't provide paths
onClean();
} else {
onClean(p);
}
}

setDialogOpen({
clean: false,
cleanAlert: false,
});
}}
/>
);
}
return;
}
Comment on lines +145 to +173
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why couldn't this have been put into the existing CleanDialog instead of removing the CleanDialog altogether?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First I duplicated the CleanDialog code over into LibraryTasks.tsx as an AutoTagDialog.
That was almost identical code, and now a third instance of the DirectorySelectionDialog code. So I pulled CleanDialog and AutoTagDialog out into a new component file.
This new file was almost identical to DirectorySelectionDialog, except for the extra styling options, message and option to hide the directory selection.

I thought about adding the styling options to DirectorySelectionDialog and replacing the directory code in my new file with that component, but I would have to deconstruct DirectorySelectionDialog because it already has a <ModalComponent> that I couldn't wrap in my higher level <ModelComponent>.

I figured since I was already going to add all the styling options to it, adding message and the option to totally hide it was less complicated and less error prone than trying to refactor DirectorySelectionDialog and pull out the directory selection code into a sub component that could be imported into an OptionalDirectorySelectionDialog component.

I could do that if you think it would be a better approach?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First I duplicated the CleanDialog code over into LibraryTasks.tsx as an AutoTagDialog.
That was almost identical code, and now a third instance of the DirectorySelectionDialog code. So I pulled CleanDialog and AutoTagDialog out into a new component file.
This new file was almost identical to DirectorySelectionDialog, except for the extra styling options, message and option to hide the directory selection.

Please do this instead for now. We can address the duplicate code later if needed.


async function onClean(paths?: string[]) {
try {
await mutateMetadataClean({
Expand Down Expand Up @@ -380,30 +320,7 @@ export const DataManagementTasks: React.FC<IDataManagementTasks> = ({
<Form.Group>
{renderImportAlert()}
{renderImportDialog()}
{dialogOpen.cleanAlert || dialogOpen.clean ? (
<CleanDialog
dryRun={cleanOptions.dryRun}
pathSelection={dialogOpen.clean}
onClose={(p) => {
// undefined means cancelled
if (p !== undefined) {
if (dialogOpen.cleanAlert) {
// don't provide paths
onClean();
} else {
onClean(p);
}
}

setDialogOpen({
clean: false,
cleanAlert: false,
});
}}
/>
) : (
dialogOpen.clean
)}
{renderCleanDialog()}

<SettingSection headingID="config.tasks.maintenance">
<div className="setting-group">
Expand Down
58 changes: 40 additions & 18 deletions ui/v2.5/src/components/Settings/Tasks/DirectorySelectionDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
IconDefinition,
faMinus,
faPencilAlt,
faPlus,
Expand All @@ -15,12 +16,29 @@ interface IDirectorySelectionDialogProps {
animation?: boolean;
initialPaths?: string[];
allowEmpty?: boolean;
allowPathSelection?: boolean;
message?: string | React.ReactNode;
header?: string;
icon?: IconDefinition;
acceptButtonText?: string;
acceptButtonVariant?: "danger" | "primary" | "secondary";
onClose: (paths?: string[]) => void;
}

export const DirectorySelectionDialog: React.FC<
IDirectorySelectionDialogProps
> = ({ animation, allowEmpty = false, initialPaths = [], onClose }) => {
> = ({
animation,
allowEmpty = false,
initialPaths = [],
allowPathSelection = true,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This entire concept is a bit broken from a design perspective. The component is a DirectorySelectionDialog, but we have a flag to disable the entire purpose of the dialog? I think there's a better way to do this.

message,
header,
icon = faPencilAlt,
acceptButtonText,
acceptButtonVariant = "primary",
onClose,
}) => {
const intl = useIntl();
const { configuration } = React.useContext(ConfigurationContext);

Expand All @@ -43,14 +61,15 @@ export const DirectorySelectionDialog: React.FC<
<ModalComponent
show
modalProps={{ animation }}
disabled={!allowEmpty && paths.length === 0}
icon={faPencilAlt}
header={intl.formatMessage({ id: "actions.select_folders" })}
disabled={!allowEmpty && allowPathSelection && paths.length === 0}
icon={icon}
header={header ?? intl.formatMessage({ id: "actions.select_folders" })}
accept={{
onClick: () => {
onClose(paths);
},
text: intl.formatMessage({ id: "actions.confirm" }),
text: acceptButtonText ?? intl.formatMessage({ id: "actions.confirm" }),
variant: acceptButtonVariant,
}}
cancel={{
onClick: () => onClose(),
Expand Down Expand Up @@ -78,19 +97,22 @@ export const DirectorySelectionDialog: React.FC<
</Row>
))}

<FolderSelect
currentDirectory={currentDirectory}
setCurrentDirectory={(v) => setCurrentDirectory(v)}
defaultDirectories={libraryPaths}
appendButton={
<Button
variant="secondary"
onClick={() => addPath(currentDirectory)}
>
<Icon icon={faPlus} />
</Button>
}
/>
{allowPathSelection ? (
<FolderSelect
currentDirectory={currentDirectory}
setCurrentDirectory={(v) => setCurrentDirectory(v)}
defaultDirectories={libraryPaths}
appendButton={
<Button
variant="secondary"
onClick={() => addPath(currentDirectory)}
>
<Icon icon={faPlus} />
</Button>
}
/>
) : undefined}
{message}
</div>
</ModalComponent>
);
Expand Down
Loading
Loading