Advanced settings (#4378)

* Add advanced settings mode toggle
* Add advanced settings
This commit is contained in:
WithoutPants
2024-01-16 16:22:04 +11:00
committed by GitHub
parent 29677696fd
commit e7311a60d2
14 changed files with 144 additions and 56 deletions

View File

@@ -12,6 +12,7 @@ import { withoutTypename } from "src/utils/data";
import { GenerateOptions } from "../Settings/Tasks/GenerateOptions"; import { GenerateOptions } from "../Settings/Tasks/GenerateOptions";
import { SettingSection } from "../Settings/SettingSection"; import { SettingSection } from "../Settings/SettingSection";
import { faCogs, faQuestionCircle } from "@fortawesome/free-solid-svg-icons"; import { faCogs, faQuestionCircle } from "@fortawesome/free-solid-svg-icons";
import { SettingsContext } from "../Settings/context";
interface ISceneGenerateDialog { interface ISceneGenerateDialog {
selectedIds?: string[]; selectedIds?: string[];
@@ -196,6 +197,7 @@ export const GenerateDialog: React.FC<ISceneGenerateDialog> = ({
> >
<Form> <Form>
{selectionStatus} {selectionStatus}
<SettingsContext>
<SettingSection> <SettingSection>
<GenerateOptions <GenerateOptions
options={options} options={options}
@@ -203,6 +205,7 @@ export const GenerateDialog: React.FC<ISceneGenerateDialog> = ({
selection selection
/> />
</SettingSection> </SettingSection>
</SettingsContext>
</Form> </Form>
</ModalComponent> </ModalComponent>
); );

View File

@@ -5,9 +5,11 @@ import { FormattedMessage, useIntl } from "react-intl";
import { Icon } from "../Shared/Icon"; import { Icon } from "../Shared/Icon";
import { StringListInput } from "../Shared/StringListInput"; import { StringListInput } from "../Shared/StringListInput";
import { PatchComponent } from "src/pluginApi"; import { PatchComponent } from "src/pluginApi";
import { useSettings } from "./context";
interface ISetting { interface ISetting {
id?: string; id?: string;
advanced?: boolean;
className?: string; className?: string;
heading?: React.ReactNode; heading?: React.ReactNode;
headingID?: string; headingID?: string;
@@ -32,8 +34,11 @@ export const Setting: React.FC<PropsWithChildren<ISetting>> = PatchComponent(
tooltipID, tooltipID,
onClick, onClick,
disabled, disabled,
advanced,
} = props; } = props;
const { advancedMode } = useSettings();
const intl = useIntl(); const intl = useIntl();
function renderHeading() { function renderHeading() {
@@ -61,6 +66,8 @@ export const Setting: React.FC<PropsWithChildren<ISetting>> = PatchComponent(
: undefined; : undefined;
const disabledClassName = disabled ? "disabled" : ""; const disabledClassName = disabled ? "disabled" : "";
if (advanced && !advancedMode) return null;
return ( return (
<div <div
className={`setting ${className ?? ""} ${disabledClassName}`} className={`setting ${className ?? ""} ${disabledClassName}`}
@@ -172,9 +179,15 @@ export const SelectSetting: React.FC<PropsWithChildren<ISelectSetting>> = ({
value, value,
children, children,
onChange, onChange,
advanced,
}) => { }) => {
return ( return (
<Setting headingID={headingID} subHeadingID={subHeadingID} id={id}> <Setting
advanced={advanced}
headingID={headingID}
subHeadingID={subHeadingID}
id={id}
>
<Form.Control <Form.Control
className="input-control" className="input-control"
as="select" as="select"
@@ -346,8 +359,12 @@ export const ModalSetting = <T extends {}>(props: IModalSetting<T>) => {
buttonTextID, buttonTextID,
modalProps, modalProps,
disabled, disabled,
advanced,
} = props; } = props;
const [showModal, setShowModal] = useState(false); const [showModal, setShowModal] = useState(false);
const { advancedMode } = useSettings();
if (advanced && !advancedMode) return null;
return ( return (
<> <>

View File

@@ -1,11 +1,13 @@
import React, { PropsWithChildren } from "react"; import React, { PropsWithChildren } from "react";
import { Card } from "react-bootstrap"; import { Card } from "react-bootstrap";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { useSettings } from "./context";
interface ISettingGroup { interface ISettingGroup {
id?: string; id?: string;
headingID?: string; headingID?: string;
subHeadingID?: string; subHeadingID?: string;
advanced?: boolean;
} }
export const SettingSection: React.FC<PropsWithChildren<ISettingGroup>> = ({ export const SettingSection: React.FC<PropsWithChildren<ISettingGroup>> = ({
@@ -13,8 +15,12 @@ export const SettingSection: React.FC<PropsWithChildren<ISettingGroup>> = ({
children, children,
headingID, headingID,
subHeadingID, subHeadingID,
advanced,
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const { advancedMode } = useSettings();
if (advanced && !advancedMode) return null;
return ( return (
<div className="setting-section" id={id}> <div className="setting-section" id={id}>

View File

@@ -1,6 +1,6 @@
import React from "react"; import React from "react";
import { Tab, Nav, Row, Col } from "react-bootstrap"; import { Tab, Nav, Row, Col, Form } from "react-bootstrap";
import { Redirect, RouteComponentProps } from "react-router-dom"; import { Redirect } from "react-router-dom";
import { LinkContainer } from "react-router-bootstrap"; import { LinkContainer } from "react-router-bootstrap";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import { Helmet } from "react-helmet"; import { Helmet } from "react-helmet";
@@ -14,7 +14,7 @@ import { SettingsPluginsPanel } from "./SettingsPluginsPanel";
import { SettingsScrapingPanel } from "./SettingsScrapingPanel"; import { SettingsScrapingPanel } from "./SettingsScrapingPanel";
import { SettingsToolsPanel } from "./SettingsToolsPanel"; import { SettingsToolsPanel } from "./SettingsToolsPanel";
import { SettingsServicesPanel } from "./SettingsServicesPanel"; import { SettingsServicesPanel } from "./SettingsServicesPanel";
import { SettingsContext } from "./context"; import { SettingsContext, useSettings } from "./context";
import { SettingsLibraryPanel } from "./SettingsLibraryPanel"; import { SettingsLibraryPanel } from "./SettingsLibraryPanel";
import { SettingsSecurityPanel } from "./SettingsSecurityPanel"; import { SettingsSecurityPanel } from "./SettingsSecurityPanel";
import Changelog from "../Changelog/Changelog"; import Changelog from "../Changelog/Changelog";
@@ -41,9 +41,11 @@ function isTabKey(tab: string | null): tab is TabKey {
return validTabs.includes(tab as TabKey); return validTabs.includes(tab as TabKey);
} }
const Settings: React.FC<RouteComponentProps> = ({ location }) => { const SettingTabs: React.FC = () => {
const tab = new URLSearchParams(location.search).get("tab"); const tab = new URLSearchParams(location.search).get("tab");
const { advancedMode, setAdvancedMode } = useSettings();
const titleProps = useTitleProps({ id: "settings" }); const titleProps = useTitleProps({ id: "settings" });
if (!isTabKey(tab)) { if (!isTabKey(tab)) {
@@ -147,6 +149,18 @@ const Settings: React.FC<RouteComponentProps> = ({ location }) => {
</Nav.Link> </Nav.Link>
</LinkContainer> </LinkContainer>
</Nav.Item> </Nav.Item>
<Nav.Item>
<div className="advanced-switch">
<Form.Label htmlFor="advanced-settings">
<FormattedMessage id="config.advanced_mode" />
</Form.Label>
<Form.Switch
id="advanced-settings"
checked={advancedMode}
onChange={() => setAdvancedMode(!advancedMode)}
/>
</div>
</Nav.Item>
<hr className="d-sm-none" /> <hr className="d-sm-none" />
</Nav> </Nav>
</Col> </Col>
@@ -156,7 +170,6 @@ const Settings: React.FC<RouteComponentProps> = ({ location }) => {
md={{ offset: 3 }} md={{ offset: 3 }}
xl={{ offset: 2 }} xl={{ offset: 2 }}
> >
<SettingsContext>
<Tab.Content className="mx-auto"> <Tab.Content className="mx-auto">
<Tab.Pane eventKey="library"> <Tab.Pane eventKey="library">
<SettingsLibraryPanel /> <SettingsLibraryPanel />
@@ -195,11 +208,18 @@ const Settings: React.FC<RouteComponentProps> = ({ location }) => {
<SettingsAboutPanel /> <SettingsAboutPanel />
</Tab.Pane> </Tab.Pane>
</Tab.Content> </Tab.Content>
</SettingsContext>
</Col> </Col>
</Row> </Row>
</Tab.Container> </Tab.Container>
); );
}; };
export const Settings: React.FC = () => {
return (
<SettingsContext>
<SettingTabs />
</SettingsContext>
);
};
export default Settings; export default Settings;

View File

@@ -240,6 +240,7 @@ export const SettingsInterfacePanel: React.FC = () => {
/> />
<SelectSetting <SelectSetting
advanced
id="wall-preview" id="wall-preview"
headingID="config.ui.preview_type.heading" headingID="config.ui.preview_type.heading"
subHeadingID="config.ui.preview_type.description" subHeadingID="config.ui.preview_type.description"

View File

@@ -210,7 +210,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
/> />
</SettingSection> </SettingSection>
<SettingSection headingID="config.general.hashing"> <SettingSection advanced headingID="config.general.hashing">
<BooleanSetting <BooleanSetting
id="calculate-md5-and-ohash" id="calculate-md5-and-ohash"
headingID="config.general.calculate_md5_and_ohash_label" headingID="config.general.calculate_md5_and_ohash_label"
@@ -240,6 +240,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
<SettingSection headingID="config.system.transcoding"> <SettingSection headingID="config.system.transcoding">
<SelectSetting <SelectSetting
advanced
id="transcode-size" id="transcode-size"
headingID="config.general.maximum_transcode_size_head" headingID="config.general.maximum_transcode_size_head"
subHeadingID="config.general.maximum_transcode_size_desc" subHeadingID="config.general.maximum_transcode_size_desc"
@@ -282,6 +283,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
/> />
<StringListSetting <StringListSetting
advanced
id="transcode-input-args" id="transcode-input-args"
headingID="config.general.ffmpeg.transcode.input_args.heading" headingID="config.general.ffmpeg.transcode.input_args.heading"
subHeadingID="config.general.ffmpeg.transcode.input_args.desc" subHeadingID="config.general.ffmpeg.transcode.input_args.desc"
@@ -289,6 +291,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
value={general.transcodeInputArgs ?? []} value={general.transcodeInputArgs ?? []}
/> />
<StringListSetting <StringListSetting
advanced
id="transcode-output-args" id="transcode-output-args"
headingID="config.general.ffmpeg.transcode.output_args.heading" headingID="config.general.ffmpeg.transcode.output_args.heading"
subHeadingID="config.general.ffmpeg.transcode.output_args.desc" subHeadingID="config.general.ffmpeg.transcode.output_args.desc"
@@ -297,6 +300,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
/> />
<StringListSetting <StringListSetting
advanced
id="live-transcode-input-args" id="live-transcode-input-args"
headingID="config.general.ffmpeg.live_transcode.input_args.heading" headingID="config.general.ffmpeg.live_transcode.input_args.heading"
subHeadingID="config.general.ffmpeg.live_transcode.input_args.desc" subHeadingID="config.general.ffmpeg.live_transcode.input_args.desc"
@@ -304,6 +308,7 @@ export const SettingsConfigurationPanel: React.FC = () => {
value={general.liveTranscodeInputArgs ?? []} value={general.liveTranscodeInputArgs ?? []}
/> />
<StringListSetting <StringListSetting
advanced
id="live-transcode-output-args" id="live-transcode-output-args"
headingID="config.general.ffmpeg.live_transcode.output_args.heading" headingID="config.general.ffmpeg.live_transcode.output_args.heading"
subHeadingID="config.general.ffmpeg.live_transcode.output_args.desc" subHeadingID="config.general.ffmpeg.live_transcode.output_args.desc"

View File

@@ -583,6 +583,7 @@ export const DataManagementTasks: React.FC<IDataManagementTasks> = ({
<SettingSection headingID="config.tasks.migrations"> <SettingSection headingID="config.tasks.migrations">
<Setting <Setting
advanced
headingID="actions.rename_gen_files" headingID="actions.rename_gen_files"
subHeadingID="config.tasks.migrate_hash_files" subHeadingID="config.tasks.migrate_hash_files"
> >

View File

@@ -40,6 +40,7 @@ export const GenerateOptions: React.FC<IGenerateOptions> = ({
onChange={(v) => setOptions({ previews: v })} onChange={(v) => setOptions({ previews: v })}
/> />
<BooleanSetting <BooleanSetting
advanced
className="sub-setting" className="sub-setting"
id="image-preview-task" id="image-preview-task"
checked={options.imagePreviews ?? false} checked={options.imagePreviews ?? false}
@@ -88,6 +89,7 @@ export const GenerateOptions: React.FC<IGenerateOptions> = ({
onChange={(v) => setOptions({ markers: v })} onChange={(v) => setOptions({ markers: v })}
/> />
<BooleanSetting <BooleanSetting
advanced
id="marker-image-preview-task" id="marker-image-preview-task"
className="sub-setting" className="sub-setting"
checked={options.markerImagePreviews ?? false} checked={options.markerImagePreviews ?? false}
@@ -101,6 +103,7 @@ export const GenerateOptions: React.FC<IGenerateOptions> = ({
} }
/> />
<BooleanSetting <BooleanSetting
advanced
id="marker-screenshot-task" id="marker-screenshot-task"
className="sub-setting" className="sub-setting"
checked={options.markerScreenshots ?? false} checked={options.markerScreenshots ?? false}
@@ -111,6 +114,7 @@ export const GenerateOptions: React.FC<IGenerateOptions> = ({
/> />
<BooleanSetting <BooleanSetting
advanced
id="transcode-task" id="transcode-task"
checked={options.transcodes ?? false} checked={options.transcodes ?? false}
headingID="dialogs.scene_gen.transcodes" headingID="dialogs.scene_gen.transcodes"
@@ -119,6 +123,7 @@ export const GenerateOptions: React.FC<IGenerateOptions> = ({
/> />
{selection ? ( {selection ? (
<BooleanSetting <BooleanSetting
advanced
id="force-transcode" id="force-transcode"
className="sub-setting" className="sub-setting"
checked={options.forceTranscodes ?? false} checked={options.forceTranscodes ?? false}

View File

@@ -327,7 +327,7 @@ export const LibraryTasks: React.FC = () => {
</SettingGroup> </SettingGroup>
</SettingSection> </SettingSection>
<SettingSection> <SettingSection advanced>
<Setting <Setting
heading={ heading={
<> <>
@@ -349,7 +349,7 @@ export const LibraryTasks: React.FC = () => {
</Setting> </Setting>
</SettingSection> </SettingSection>
<SettingSection> <SettingSection advanced>
<SettingGroup <SettingGroup
settingProps={{ settingProps={{
heading: ( heading: (

View File

@@ -41,6 +41,7 @@ export const ScanOptions: React.FC<IScanOptions> = ({
onChange={(v) => setOptions({ scanGeneratePreviews: v })} onChange={(v) => setOptions({ scanGeneratePreviews: v })}
/> />
<BooleanSetting <BooleanSetting
advanced
id="scan-generate-image-previews" id="scan-generate-image-previews"
className="sub-setting" className="sub-setting"
headingID="config.tasks.generate_previews_during_scan" headingID="config.tasks.generate_previews_during_scan"

View File

@@ -34,6 +34,8 @@ export interface ISettingsContextState {
ui: IUIConfig; ui: IUIConfig;
plugins: PluginSettings; plugins: PluginSettings;
advancedMode: boolean;
// apikey isn't directly settable, so expose it here // apikey isn't directly settable, so expose it here
apiKey: string; apiKey: string;
@@ -44,6 +46,7 @@ export interface ISettingsContextState {
saveDLNA: (input: Partial<GQL.ConfigDlnaInput>) => void; saveDLNA: (input: Partial<GQL.ConfigDlnaInput>) => void;
saveUI: (input: Partial<IUIConfig>) => void; saveUI: (input: Partial<IUIConfig>) => void;
savePluginSettings: (pluginID: string, input: {}) => void; savePluginSettings: (pluginID: string, input: {}) => void;
setAdvancedMode: (value: boolean) => void;
refetch: () => void; refetch: () => void;
} }
@@ -91,7 +94,7 @@ export const SettingsContext: React.FC = ({ children }) => {
const [pendingDLNA, setPendingDLNA] = useState<GQL.ConfigDlnaInput>(); const [pendingDLNA, setPendingDLNA] = useState<GQL.ConfigDlnaInput>();
const [updateDLNAConfig] = useConfigureDLNA(); const [updateDLNAConfig] = useConfigureDLNA();
const [ui, setUI] = useState({}); const [ui, setUI] = useState<IUIConfig>({});
const [pendingUI, setPendingUI] = useState<{}>(); const [pendingUI, setPendingUI] = useState<{}>();
const [updateUIConfig] = useConfigureUI(); const [updateUIConfig] = useConfigureUI();
@@ -430,6 +433,12 @@ export const SettingsContext: React.FC = ({ children }) => {
}); });
} }
function setAdvancedMode(value: boolean) {
saveUI({
advancedMode: value,
});
}
// saves the configuration if no further changes are made after a half second // saves the configuration if no further changes are made after a half second
const savePluginConfig = useDebounce(async (input: PluginSettings) => { const savePluginConfig = useDebounce(async (input: PluginSettings) => {
try { try {
@@ -536,6 +545,7 @@ export const SettingsContext: React.FC = ({ children }) => {
dlna, dlna,
ui, ui,
plugins, plugins,
advancedMode: ui.advancedMode ?? false,
saveGeneral, saveGeneral,
saveInterface, saveInterface,
saveDefaults, saveDefaults,
@@ -544,6 +554,7 @@ export const SettingsContext: React.FC = ({ children }) => {
saveUI, saveUI,
refetch, refetch,
savePluginSettings, savePluginSettings,
setAdvancedMode,
}} }}
> >
{maybeRenderLoadingIndicator()} {maybeRenderLoadingIndicator()}

View File

@@ -410,3 +410,18 @@
.empty-queue-message { .empty-queue-message {
color: $text-muted; color: $text-muted;
} }
.advanced-switch {
display: flex;
justify-content: space-between;
padding: 0.5rem 1rem;
.form-label {
color: $text-muted;
margin-right: 0.5rem;
}
.custom-switch {
display: inline-block;
}
}

View File

@@ -83,6 +83,8 @@ export interface IUIConfig {
vrTag?: string; vrTag?: string;
pinnedFilters?: PinnedFilters; pinnedFilters?: PinnedFilters;
advancedMode?: boolean;
} }
interface ISavedFilterRowBroken extends ISavedFilterRow { interface ISavedFilterRowBroken extends ISavedFilterRow {

View File

@@ -215,6 +215,7 @@
"stash_wiki": "Stash {url} page", "stash_wiki": "Stash {url} page",
"version": "Version" "version": "Version"
}, },
"advanced_mode": "Advanced Mode",
"application_paths": { "application_paths": {
"heading": "Application Paths" "heading": "Application Paths"
}, },