Refactor tasks follow up (#2061)

* Move scan options out of dialog
* Move autotag and clean options out of dialogs
* Move generate options out of dialog
* Animate button while tasks running
* Revert to earlier Tasks UI iteration
* Rearrange and clarify scan options
This commit is contained in:
WithoutPants
2021-11-30 09:45:36 +11:00
committed by GitHub
parent cf4ab843f6
commit 7c44a9c993
28 changed files with 1277 additions and 1579 deletions

View File

@@ -1,262 +0,0 @@
import React, { useState, useMemo, useEffect } from "react";
import { Button, Form } from "react-bootstrap";
import {
mutateMetadataAutoTag,
useConfiguration,
useConfigureDefaults,
} from "src/core/StashService";
import { Icon, Modal, OperationButton } from "src/components/Shared";
import { useToast } from "src/hooks";
import * as GQL from "src/core/generated-graphql";
import { FormattedMessage, useIntl } from "react-intl";
import { DirectorySelectionDialog } from "src/components/Settings/SettingsTasksPanel/DirectorySelectionDialog";
import { Manual } from "src/components/Help/Manual";
import { withoutTypename } from "src/utils";
interface IAutoTagOptions {
options: GQL.AutoTagMetadataInput;
setOptions: (s: GQL.AutoTagMetadataInput) => void;
}
const AutoTagOptions: React.FC<IAutoTagOptions> = ({
options,
setOptions: setOptionsState,
}) => {
const intl = useIntl();
const { performers, studios, tags } = options;
const wildcard = ["*"];
function toggle(v?: GQL.Maybe<string[]>) {
if (!v?.length) {
return wildcard;
}
return [];
}
function setOptions(input: Partial<GQL.AutoTagMetadataInput>) {
setOptionsState({ ...options, ...input });
}
return (
<Form.Group>
<Form.Check
id="autotag-performers"
checked={!!performers?.length}
label={intl.formatMessage({ id: "performers" })}
onChange={() => setOptions({ performers: toggle(performers) })}
/>
<Form.Check
id="autotag-studios"
checked={!!studios?.length}
label={intl.formatMessage({ id: "studios" })}
onChange={() => setOptions({ studios: toggle(studios) })}
/>
<Form.Check
id="autotag-tags"
checked={!!tags?.length}
label={intl.formatMessage({ id: "tags" })}
onChange={() => setOptions({ tags: toggle(tags) })}
/>
</Form.Group>
);
};
interface IAutoTagDialogProps {
onClose: () => void;
}
export const AutoTagDialog: React.FC<IAutoTagDialogProps> = ({ onClose }) => {
const [configureDefaults] = useConfigureDefaults();
const [options, setOptions] = useState<GQL.AutoTagMetadataInput>({
performers: ["*"],
studios: ["*"],
tags: ["*"],
});
const [paths, setPaths] = useState<string[]>([]);
const [showManual, setShowManual] = useState(false);
const [settingPaths, setSettingPaths] = useState(false);
const [animation, setAnimation] = useState(true);
const [savingDefaults, setSavingDefaults] = useState(false);
const intl = useIntl();
const Toast = useToast();
const { data: configData, error: configError } = useConfiguration();
useEffect(() => {
if (!configData?.configuration.defaults) {
return;
}
const { autoTag } = configData.configuration.defaults;
if (autoTag) {
setOptions(withoutTypename(autoTag));
}
}, [configData]);
const selectionStatus = useMemo(() => {
const message = paths.length ? (
<div>
<FormattedMessage id="config.tasks.auto_tag.auto_tagging_paths" />:
<ul>
{paths.map((p) => (
<li key={p}>{p}</li>
))}
</ul>
</div>
) : (
<span>
<FormattedMessage id="config.tasks.auto_tag.auto_tagging_all_paths" />.
</span>
);
function onClick() {
setAnimation(false);
setSettingPaths(true);
}
return (
<Form.Group className="dialog-selected-folders">
<div>
{message}
<div>
<Button
title={intl.formatMessage({ id: "actions.select_folders" })}
onClick={() => onClick()}
>
<Icon icon="folder-open" />
</Button>
</div>
</div>
</Form.Group>
);
}, [intl, paths]);
if (configError) return <div>{configError}</div>;
if (!configData) return <div />;
function makeDefaultAutoTagInput() {
const ret = options;
const { paths: _paths, ...withoutSpecifics } = ret;
return withoutSpecifics;
}
async function onAutoTag() {
try {
await mutateMetadataAutoTag({
...options,
paths: paths.length ? paths : undefined,
});
Toast.success({
content: intl.formatMessage(
{ id: "config.tasks.added_job_to_queue" },
{ operation_name: intl.formatMessage({ id: "actions.auto_tag" }) }
),
});
} catch (e) {
Toast.error(e);
} finally {
onClose();
}
}
function onShowManual() {
setAnimation(false);
setShowManual(true);
}
async function setAsDefault() {
try {
setSavingDefaults(true);
await configureDefaults({
variables: {
input: {
autoTag: makeDefaultAutoTagInput(),
},
},
});
Toast.success({
content: intl.formatMessage(
{ id: "config.tasks.defaults_set" },
{ action: intl.formatMessage({ id: "actions.auto_tag" }) }
),
});
} catch (e) {
Toast.error(e);
} finally {
setSavingDefaults(false);
}
}
if (settingPaths) {
return (
<DirectorySelectionDialog
animation={false}
allowEmpty
initialPaths={paths}
onClose={(p) => {
if (p) {
setPaths(p);
}
setSettingPaths(false);
}}
/>
);
}
if (showManual) {
return (
<Manual
animation={false}
show
onClose={() => setShowManual(false)}
defaultActiveTab="AutoTagging.md"
/>
);
}
return (
<Modal
modalProps={{ animation }}
show
icon="cogs"
header={intl.formatMessage({ id: "actions.auto_tag" })}
accept={{
onClick: onAutoTag,
text: intl.formatMessage({ id: "actions.auto_tag" }),
}}
cancel={{
onClick: () => onClose(),
text: intl.formatMessage({ id: "actions.cancel" }),
variant: "secondary",
}}
disabled={savingDefaults}
footerButtons={
<OperationButton variant="secondary" operation={setAsDefault}>
<FormattedMessage id="actions.set_as_default" />
</OperationButton>
}
leftFooterButtons={
<Button
title="Help"
className="minimal help-button"
onClick={() => onShowManual()}
>
<Icon icon="question-circle" />
</Button>
}
>
<Form>
{selectionStatus}
<AutoTagOptions options={options} setOptions={(o) => setOptions(o)} />
</Form>
</Modal>
);
};
export default AutoTagDialog;

View File

@@ -1,198 +0,0 @@
import React, { useState, useMemo } from "react";
import { Button, Form } from "react-bootstrap";
import {
mutateMetadataClean,
useConfiguration,
// useConfigureDefaults,
} from "src/core/StashService";
import { Icon, Modal } from "src/components/Shared";
import { useToast } from "src/hooks";
import * as GQL from "src/core/generated-graphql";
import { useIntl } from "react-intl";
import { Manual } from "src/components/Help/Manual";
interface ICleanOptions {
options: GQL.CleanMetadataInput;
setOptions: (s: GQL.CleanMetadataInput) => void;
}
const CleanOptions: React.FC<ICleanOptions> = ({
options,
setOptions: setOptionsState,
}) => {
const intl = useIntl();
function setOptions(input: Partial<GQL.CleanMetadataInput>) {
setOptionsState({ ...options, ...input });
}
return (
<Form.Group>
<Form.Check
id="clean-dryrun"
checked={options.dryRun}
label={intl.formatMessage({ id: "config.tasks.only_dry_run" })}
onChange={() => setOptions({ dryRun: !options.dryRun })}
/>
</Form.Group>
);
};
interface ICleanDialog {
onClose: () => void;
}
export const CleanDialog: React.FC<ICleanDialog> = ({ onClose }) => {
const [options, setOptions] = useState<GQL.CleanMetadataInput>({
dryRun: false,
});
// TODO - selective clean
// const [paths, setPaths] = useState<string[]>([]);
// const [settingPaths, setSettingPaths] = useState(false);
const [showManual, setShowManual] = useState(false);
const [animation, setAnimation] = useState(true);
const intl = useIntl();
const Toast = useToast();
const { data: configData, error: configError } = useConfiguration();
const message = useMemo(() => {
if (options.dryRun) {
return (
<p>{intl.formatMessage({ id: "actions.tasks.dry_mode_selected" })}</p>
);
} else {
return (
<p>
{intl.formatMessage({ id: "actions.tasks.clean_confirm_message" })}
</p>
);
}
}, [options.dryRun, intl]);
// const selectionStatus = useMemo(() => {
// const message = paths.length ? (
// <div>
// <FormattedMessage id="config.tasks.auto_tag.auto_tagging_paths" />:
// <ul>
// {paths.map((p) => (
// <li key={p}>{p}</li>
// ))}
// </ul>
// </div>
// ) : (
// <span>
// <FormattedMessage id="config.tasks.auto_tag.auto_tagging_all_paths" />.
// </span>
// );
// function onClick() {
// setAnimation(false);
// setSettingPaths(true);
// }
// return (
// <Form.Group className="dialog-selected-folders">
// <div>
// {message}
// <div>
// <Button
// title={intl.formatMessage({ id: "actions.select_folders" })}
// onClick={() => onClick()}
// >
// <Icon icon="folder-open" />
// </Button>
// </div>
// </div>
// </Form.Group>
// );
// }, [intl, paths]);
if (configError) return <div>{configError}</div>;
if (!configData) return <div />;
async function onClean() {
try {
await mutateMetadataClean(options);
Toast.success({
content: intl.formatMessage(
{ id: "config.tasks.added_job_to_queue" },
{ operation_name: intl.formatMessage({ id: "actions.clean" }) }
),
});
} catch (e) {
Toast.error(e);
} finally {
onClose();
}
}
function onShowManual() {
setAnimation(false);
setShowManual(true);
}
// if (settingPaths) {
// return (
// <DirectorySelectionDialog
// animation={false}
// allowEmpty
// initialPaths={paths}
// onClose={(p) => {
// if (p) {
// setPaths(p);
// }
// setSettingPaths(false);
// }}
// />
// );
// }
if (showManual) {
return (
<Manual
animation={false}
show
onClose={() => setShowManual(false)}
defaultActiveTab="Tasks.md"
/>
);
}
return (
<Modal
modalProps={{ animation }}
show
icon="cogs"
header={intl.formatMessage({ id: "actions.clean" })}
accept={{
onClick: onClean,
variant: "danger",
text: intl.formatMessage({ id: "actions.clean" }),
}}
cancel={{
onClick: () => onClose(),
text: intl.formatMessage({ id: "actions.cancel" }),
variant: "secondary",
}}
leftFooterButtons={
<Button
title="Help"
className="minimal help-button"
onClick={() => onShowManual()}
>
<Icon icon="question-circle" />
</Button>
}
>
<Form>
<CleanOptions options={options} setOptions={(o) => setOptions(o)} />
{message}
</Form>
</Modal>
);
};
export default CleanDialog;

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect, useMemo } from "react";
import { Form, Button, Collapse } from "react-bootstrap";
import { Form, Button } from "react-bootstrap";
import {
mutateMetadataGenerate,
useConfigureDefaults,
@@ -12,284 +12,7 @@ import { ConfigurationContext } from "src/hooks/Config";
// import { DirectorySelectionDialog } from "../Settings/SettingsTasksPanel/DirectorySelectionDialog";
import { Manual } from "../Help/Manual";
import { withoutTypename } from "src/utils";
interface IGenerateOptions {
options: GQL.GenerateMetadataInput;
setOptions: (s: GQL.GenerateMetadataInput) => void;
}
const GenerateOptions: React.FC<IGenerateOptions> = ({
options,
setOptions: setOptionsState,
}) => {
const intl = useIntl();
const [previewOptionsOpen, setPreviewOptionsOpen] = useState(false);
const previewOptions: GQL.GeneratePreviewOptionsInput =
options.previewOptions ?? {};
function setOptions(input: Partial<GQL.GenerateMetadataInput>) {
setOptionsState({ ...options, ...input });
}
function setPreviewOptions(input: Partial<GQL.GeneratePreviewOptionsInput>) {
setOptions({
previewOptions: {
...previewOptions,
...input,
},
});
}
return (
<Form.Group>
<Form.Group>
<Form.Check
id="preview-task"
checked={options.previews ?? false}
label={intl.formatMessage({
id: "dialogs.scene_gen.video_previews",
})}
onChange={() => setOptions({ previews: !options.previews })}
/>
<div className="d-flex flex-row">
<div></div>
<Form.Check
id="image-preview-task"
checked={options.imagePreviews ?? false}
disabled={!options.previews}
label={intl.formatMessage({
id: "dialogs.scene_gen.image_previews",
})}
onChange={() =>
setOptions({ imagePreviews: !options.imagePreviews })
}
className="ml-2 flex-grow"
/>
</div>
</Form.Group>
<Form.Group>
<Button
onClick={() => setPreviewOptionsOpen(!previewOptionsOpen)}
className="minimal pl-0 no-focus"
>
<Icon icon={previewOptionsOpen ? "chevron-down" : "chevron-right"} />
<span>
{intl.formatMessage({
id: "dialogs.scene_gen.preview_options",
})}
</span>
</Button>
<Form.Group>
<Collapse in={previewOptionsOpen}>
<Form.Group className="mt-2">
<Form.Group id="preview-preset">
<h6>
{intl.formatMessage({
id: "dialogs.scene_gen.preview_preset_head",
})}
</h6>
<Form.Control
className="w-auto input-control"
as="select"
value={previewOptions.previewPreset ?? GQL.PreviewPreset.Slow}
onChange={(e) =>
setPreviewOptions({
previewPreset: e.currentTarget.value as GQL.PreviewPreset,
})
}
>
{Object.keys(GQL.PreviewPreset).map((p) => (
<option value={p.toLowerCase()} key={p}>
{p}
</option>
))}
</Form.Control>
<Form.Text className="text-muted">
{intl.formatMessage({
id: "dialogs.scene_gen.preview_preset_desc",
})}
</Form.Text>
</Form.Group>
<Form.Group id="preview-segments">
<h6>
{intl.formatMessage({
id: "dialogs.scene_gen.preview_seg_count_head",
})}
</h6>
<Form.Control
className="col col-sm-6 text-input"
type="number"
value={previewOptions.previewSegments?.toString() ?? ""}
onChange={(e) =>
setPreviewOptions({
previewSegments: Number.parseInt(
e.currentTarget.value,
10
),
})
}
/>
<Form.Text className="text-muted">
{intl.formatMessage({
id: "dialogs.scene_gen.preview_seg_count_desc",
})}
</Form.Text>
</Form.Group>
<Form.Group id="preview-segment-duration">
<h6>
{intl.formatMessage({
id: "dialogs.scene_gen.preview_seg_duration_head",
})}
</h6>
<Form.Control
className="col col-sm-6 text-input"
type="number"
value={
previewOptions.previewSegmentDuration?.toString() ?? ""
}
onChange={(e) =>
setPreviewOptions({
previewSegmentDuration: Number.parseFloat(
e.currentTarget.value
),
})
}
/>
<Form.Text className="text-muted">
{intl.formatMessage({
id: "dialogs.scene_gen.preview_seg_duration_desc",
})}
</Form.Text>
</Form.Group>
<Form.Group id="preview-exclude-start">
<h6>
{intl.formatMessage({
id: "dialogs.scene_gen.preview_exclude_start_time_head",
})}
</h6>
<Form.Control
className="col col-sm-6 text-input"
value={previewOptions.previewExcludeStart ?? ""}
onChange={(e) =>
setPreviewOptions({
previewExcludeStart: e.currentTarget.value,
})
}
/>
<Form.Text className="text-muted">
{intl.formatMessage({
id: "dialogs.scene_gen.preview_exclude_start_time_desc",
})}
</Form.Text>
</Form.Group>
<Form.Group id="preview-exclude-start">
<h6>
{intl.formatMessage({
id: "dialogs.scene_gen.preview_exclude_end_time_head",
})}
</h6>
<Form.Control
className="col col-sm-6 text-input"
value={previewOptions.previewExcludeEnd ?? ""}
onChange={(e) =>
setPreviewOptions({
previewExcludeEnd: e.currentTarget.value,
})
}
/>
<Form.Text className="text-muted">
{intl.formatMessage({
id: "dialogs.scene_gen.preview_exclude_end_time_desc",
})}
</Form.Text>
</Form.Group>
</Form.Group>
</Collapse>
</Form.Group>
</Form.Group>
<Form.Group>
<Form.Check
id="sprite-task"
checked={options.sprites ?? false}
label={intl.formatMessage({ id: "dialogs.scene_gen.sprites" })}
onChange={() => setOptions({ sprites: !options.sprites })}
/>
<Form.Group>
<Form.Check
id="marker-task"
checked={options.markers ?? false}
label={intl.formatMessage({ id: "dialogs.scene_gen.markers" })}
onChange={() => setOptions({ markers: !options.markers })}
/>
<div className="d-flex flex-row">
<div></div>
<Form.Group>
<Form.Check
id="marker-image-preview-task"
checked={options.markerImagePreviews ?? false}
disabled={!options.markers}
label={intl.formatMessage({
id: "dialogs.scene_gen.marker_image_previews",
})}
onChange={() =>
setOptions({
markerImagePreviews: !options.markerImagePreviews,
})
}
className="ml-2 flex-grow"
/>
<Form.Check
id="marker-screenshot-task"
checked={options.markerScreenshots ?? false}
disabled={!options.markers}
label={intl.formatMessage({
id: "dialogs.scene_gen.marker_screenshots",
})}
onChange={() =>
setOptions({ markerScreenshots: !options.markerScreenshots })
}
className="ml-2 flex-grow"
/>
</Form.Group>
</div>
</Form.Group>
<Form.Group>
<Form.Check
id="transcode-task"
checked={options.transcodes ?? false}
label={intl.formatMessage({ id: "dialogs.scene_gen.transcodes" })}
onChange={() => setOptions({ transcodes: !options.transcodes })}
/>
<Form.Check
id="phash-task"
checked={options.phashes ?? false}
label={intl.formatMessage({ id: "dialogs.scene_gen.phash" })}
onChange={() => setOptions({ phashes: !options.phashes })}
/>
</Form.Group>
<hr />
<Form.Group>
<Form.Check
id="overwrite"
checked={options.overwrite ?? false}
label={intl.formatMessage({ id: "dialogs.scene_gen.overwrite" })}
onChange={() => setOptions({ overwrite: !options.overwrite })}
/>
</Form.Group>
</Form.Group>
</Form.Group>
);
};
import { GenerateOptions } from "../Settings/Tasks/GenerateOptions";
interface ISceneGenerateDialog {
selectedIds?: string[];

View File

@@ -15,7 +15,7 @@ import {
SCRAPER_PREFIX,
STASH_BOX_PREFIX,
} from "src/components/Tagger/constants";
import { DirectorySelectionDialog } from "src/components/Settings/SettingsTasksPanel/DirectorySelectionDialog";
import { DirectorySelectionDialog } from "src/components/Settings/Tasks/DirectorySelectionDialog";
import { Manual } from "src/components/Help/Manual";
import { IScraperSource } from "./constants";
import { OptionsEditor } from "./Options";

View File

@@ -1,108 +0,0 @@
import React from "react";
import { Form } from "react-bootstrap";
import * as GQL from "src/core/generated-graphql";
import { useIntl } from "react-intl";
interface IScanOptions {
options: GQL.ScanMetadataInput;
setOptions: (s: GQL.ScanMetadataInput) => void;
}
export const ScanOptions: React.FC<IScanOptions> = ({
options,
setOptions: setOptionsState,
}) => {
const intl = useIntl();
const {
useFileMetadata,
stripFileExtension,
scanGeneratePreviews,
scanGenerateImagePreviews,
scanGenerateSprites,
scanGeneratePhashes,
scanGenerateThumbnails,
} = options;
function setOptions(input: Partial<GQL.ScanMetadataInput>) {
setOptionsState({ ...options, ...input });
}
return (
<Form.Group>
<Form.Check
id="use-file-metadata"
checked={useFileMetadata ?? false}
label={intl.formatMessage({
id: "config.tasks.set_name_date_details_from_metadata_if_present",
})}
onChange={() => setOptions({ useFileMetadata: !useFileMetadata })}
/>
<Form.Check
id="strip-file-extension"
checked={stripFileExtension ?? false}
label={intl.formatMessage({
id: "config.tasks.dont_include_file_extension_as_part_of_the_title",
})}
onChange={() => setOptions({ stripFileExtension: !stripFileExtension })}
/>
<Form.Check
id="scan-generate-previews"
checked={scanGeneratePreviews ?? false}
label={intl.formatMessage({
id: "config.tasks.generate_video_previews_during_scan",
})}
onChange={() =>
setOptions({ scanGeneratePreviews: !scanGeneratePreviews })
}
/>
<div className="d-flex flex-row">
<div></div>
<Form.Check
id="scan-generate-image-previews"
checked={scanGenerateImagePreviews ?? false}
disabled={!scanGeneratePreviews}
label={intl.formatMessage({
id: "config.tasks.generate_previews_during_scan",
})}
onChange={() =>
setOptions({
scanGenerateImagePreviews: !scanGenerateImagePreviews,
})
}
className="ml-2 flex-grow"
/>
</div>
<Form.Check
id="scan-generate-sprites"
checked={scanGenerateSprites ?? false}
label={intl.formatMessage({
id: "config.tasks.generate_sprites_during_scan",
})}
onChange={() =>
setOptions({ scanGenerateSprites: !scanGenerateSprites })
}
/>
<Form.Check
id="scan-generate-phashes"
checked={scanGeneratePhashes ?? false}
label={intl.formatMessage({
id: "config.tasks.generate_phashes_during_scan",
})}
onChange={() =>
setOptions({ scanGeneratePhashes: !scanGeneratePhashes })
}
/>
<Form.Check
id="scan-generate-thumbnails"
checked={scanGenerateThumbnails ?? false}
label={intl.formatMessage({
id: "config.tasks.generate_thumbnails_during_scan",
})}
onChange={() =>
setOptions({ scanGenerateThumbnails: !scanGenerateThumbnails })
}
/>
</Form.Group>
);
};

View File

@@ -1,209 +0,0 @@
import React, { useState, useMemo, useEffect } from "react";
import { Button, Form } from "react-bootstrap";
import {
mutateMetadataScan,
useConfigureDefaults,
} from "src/core/StashService";
import { Icon, Modal, OperationButton } from "src/components/Shared";
import { useToast } from "src/hooks";
import * as GQL from "src/core/generated-graphql";
import { FormattedMessage, useIntl } from "react-intl";
import { DirectorySelectionDialog } from "src/components/Settings/SettingsTasksPanel/DirectorySelectionDialog";
import { Manual } from "src/components/Help/Manual";
import { ScanOptions } from "./Options";
import { withoutTypename } from "src/utils";
import { ConfigurationContext } from "src/hooks/Config";
interface IScanDialogProps {
onClose: () => void;
}
export const ScanDialog: React.FC<IScanDialogProps> = ({ onClose }) => {
const [configureDefaults] = useConfigureDefaults();
const [options, setOptions] = useState<GQL.ScanMetadataInput>({});
const [paths, setPaths] = useState<string[]>([]);
const [showManual, setShowManual] = useState(false);
const [settingPaths, setSettingPaths] = useState(false);
const [animation, setAnimation] = useState(true);
const [savingDefaults, setSavingDefaults] = useState(false);
const intl = useIntl();
const Toast = useToast();
const { configuration } = React.useContext(ConfigurationContext);
useEffect(() => {
if (!configuration?.defaults) {
return;
}
const { scan } = configuration.defaults;
if (scan) {
setOptions(withoutTypename(scan));
}
}, [configuration]);
const selectionStatus = useMemo(() => {
const message = paths.length ? (
<div>
<FormattedMessage id="config.tasks.scan.scanning_paths" />:
<ul>
{paths.map((p) => (
<li key={p}>{p}</li>
))}
</ul>
</div>
) : (
<span>
<FormattedMessage id="config.tasks.scan.scanning_all_paths" />.
</span>
);
function onClick() {
setAnimation(false);
setSettingPaths(true);
}
return (
<Form.Group className="dialog-selected-folders">
<div>
{message}
<div>
<Button
title={intl.formatMessage({ id: "actions.select_folders" })}
onClick={() => onClick()}
>
<Icon icon="folder-open" />
</Button>
</div>
</div>
</Form.Group>
);
}, [intl, paths]);
if (!configuration) return <div />;
function makeDefaultScanInput() {
const ret = options;
const { paths: _paths, ...withoutSpecifics } = ret;
return withoutSpecifics;
}
async function onScan() {
try {
await mutateMetadataScan({
...options,
paths: paths.length ? paths : undefined,
});
Toast.success({
content: intl.formatMessage(
{ id: "config.tasks.added_job_to_queue" },
{ operation_name: intl.formatMessage({ id: "actions.scan" }) }
),
});
} catch (e) {
Toast.error(e);
} finally {
onClose();
}
}
function onShowManual() {
setAnimation(false);
setShowManual(true);
}
async function setAsDefault() {
try {
setSavingDefaults(true);
await configureDefaults({
variables: {
input: {
scan: makeDefaultScanInput(),
},
},
});
Toast.success({
content: intl.formatMessage(
{ id: "config.tasks.defaults_set" },
{ action: intl.formatMessage({ id: "actions.scan" }) }
),
});
} catch (e) {
Toast.error(e);
} finally {
setSavingDefaults(false);
}
}
if (settingPaths) {
return (
<DirectorySelectionDialog
animation={false}
allowEmpty
initialPaths={paths}
onClose={(p) => {
if (p) {
setPaths(p);
}
setSettingPaths(false);
}}
/>
);
}
if (showManual) {
return (
<Manual
animation={false}
show
onClose={() => setShowManual(false)}
defaultActiveTab="Tasks.md"
/>
);
}
return (
<Modal
modalProps={{ animation, size: "lg" }}
show
icon="cogs"
header={intl.formatMessage({ id: "actions.scan" })}
accept={{
onClick: onScan,
text: intl.formatMessage({ id: "actions.scan" }),
}}
cancel={{
onClick: () => onClose(),
text: intl.formatMessage({ id: "actions.cancel" }),
variant: "secondary",
}}
disabled={savingDefaults}
footerButtons={
<OperationButton variant="secondary" operation={setAsDefault}>
<FormattedMessage id="actions.set_as_default" />
</OperationButton>
}
leftFooterButtons={
<Button
title="Help"
className="minimal help-button"
onClick={() => onShowManual()}
>
<Icon icon="question-circle" />
</Button>
}
>
<Form>
{selectionStatus}
<ScanOptions options={options} setOptions={(o) => setOptions(o)} />
</Form>
</Modal>
);
};
export default ScanDialog;