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 { SettingSection } from "../Settings/SettingSection";
import { faCogs, faQuestionCircle } from "@fortawesome/free-solid-svg-icons";
import { SettingsContext } from "../Settings/context";
interface ISceneGenerateDialog {
selectedIds?: string[];
@@ -196,13 +197,15 @@ export const GenerateDialog: React.FC<ISceneGenerateDialog> = ({
>
<Form>
{selectionStatus}
<SettingSection>
<GenerateOptions
options={options}
setOptions={setOptions}
selection
/>
</SettingSection>
<SettingsContext>
<SettingSection>
<GenerateOptions
options={options}
setOptions={setOptions}
selection
/>
</SettingSection>
</SettingsContext>
</Form>
</ModalComponent>
);

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
import React from "react";
import { Tab, Nav, Row, Col } from "react-bootstrap";
import { Redirect, RouteComponentProps } from "react-router-dom";
import { Tab, Nav, Row, Col, Form } from "react-bootstrap";
import { Redirect } from "react-router-dom";
import { LinkContainer } from "react-router-bootstrap";
import { FormattedMessage } from "react-intl";
import { Helmet } from "react-helmet";
@@ -14,7 +14,7 @@ import { SettingsPluginsPanel } from "./SettingsPluginsPanel";
import { SettingsScrapingPanel } from "./SettingsScrapingPanel";
import { SettingsToolsPanel } from "./SettingsToolsPanel";
import { SettingsServicesPanel } from "./SettingsServicesPanel";
import { SettingsContext } from "./context";
import { SettingsContext, useSettings } from "./context";
import { SettingsLibraryPanel } from "./SettingsLibraryPanel";
import { SettingsSecurityPanel } from "./SettingsSecurityPanel";
import Changelog from "../Changelog/Changelog";
@@ -41,9 +41,11 @@ function isTabKey(tab: string | null): tab is 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 { advancedMode, setAdvancedMode } = useSettings();
const titleProps = useTitleProps({ id: "settings" });
if (!isTabKey(tab)) {
@@ -147,6 +149,18 @@ const Settings: React.FC<RouteComponentProps> = ({ location }) => {
</Nav.Link>
</LinkContainer>
</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" />
</Nav>
</Col>
@@ -156,50 +170,56 @@ const Settings: React.FC<RouteComponentProps> = ({ location }) => {
md={{ offset: 3 }}
xl={{ offset: 2 }}
>
<SettingsContext>
<Tab.Content className="mx-auto">
<Tab.Pane eventKey="library">
<SettingsLibraryPanel />
</Tab.Pane>
<Tab.Pane eventKey="interface">
<SettingsInterfacePanel />
</Tab.Pane>
<Tab.Pane eventKey="security">
<SettingsSecurityPanel />
</Tab.Pane>
<Tab.Pane eventKey="tasks">
<SettingsTasksPanel />
</Tab.Pane>
<Tab.Pane eventKey="services" unmountOnExit>
<SettingsServicesPanel />
</Tab.Pane>
<Tab.Pane eventKey="tools" unmountOnExit>
<SettingsToolsPanel />
</Tab.Pane>
<Tab.Pane eventKey="metadata-providers" unmountOnExit>
<SettingsScrapingPanel />
</Tab.Pane>
<Tab.Pane eventKey="system">
<SettingsConfigurationPanel />
</Tab.Pane>
<Tab.Pane eventKey="plugins" unmountOnExit>
<SettingsPluginsPanel />
</Tab.Pane>
<Tab.Pane eventKey="logs" unmountOnExit>
<SettingsLogsPanel />
</Tab.Pane>
<Tab.Pane eventKey="changelog" unmountOnExit>
<Changelog />
</Tab.Pane>
<Tab.Pane eventKey="about" unmountOnExit>
<SettingsAboutPanel />
</Tab.Pane>
</Tab.Content>
</SettingsContext>
<Tab.Content className="mx-auto">
<Tab.Pane eventKey="library">
<SettingsLibraryPanel />
</Tab.Pane>
<Tab.Pane eventKey="interface">
<SettingsInterfacePanel />
</Tab.Pane>
<Tab.Pane eventKey="security">
<SettingsSecurityPanel />
</Tab.Pane>
<Tab.Pane eventKey="tasks">
<SettingsTasksPanel />
</Tab.Pane>
<Tab.Pane eventKey="services" unmountOnExit>
<SettingsServicesPanel />
</Tab.Pane>
<Tab.Pane eventKey="tools" unmountOnExit>
<SettingsToolsPanel />
</Tab.Pane>
<Tab.Pane eventKey="metadata-providers" unmountOnExit>
<SettingsScrapingPanel />
</Tab.Pane>
<Tab.Pane eventKey="system">
<SettingsConfigurationPanel />
</Tab.Pane>
<Tab.Pane eventKey="plugins" unmountOnExit>
<SettingsPluginsPanel />
</Tab.Pane>
<Tab.Pane eventKey="logs" unmountOnExit>
<SettingsLogsPanel />
</Tab.Pane>
<Tab.Pane eventKey="changelog" unmountOnExit>
<Changelog />
</Tab.Pane>
<Tab.Pane eventKey="about" unmountOnExit>
<SettingsAboutPanel />
</Tab.Pane>
</Tab.Content>
</Col>
</Row>
</Tab.Container>
);
};
export const Settings: React.FC = () => {
return (
<SettingsContext>
<SettingTabs />
</SettingsContext>
);
};
export default Settings;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -34,6 +34,8 @@ export interface ISettingsContextState {
ui: IUIConfig;
plugins: PluginSettings;
advancedMode: boolean;
// apikey isn't directly settable, so expose it here
apiKey: string;
@@ -44,6 +46,7 @@ export interface ISettingsContextState {
saveDLNA: (input: Partial<GQL.ConfigDlnaInput>) => void;
saveUI: (input: Partial<IUIConfig>) => void;
savePluginSettings: (pluginID: string, input: {}) => void;
setAdvancedMode: (value: boolean) => void;
refetch: () => void;
}
@@ -91,7 +94,7 @@ export const SettingsContext: React.FC = ({ children }) => {
const [pendingDLNA, setPendingDLNA] = useState<GQL.ConfigDlnaInput>();
const [updateDLNAConfig] = useConfigureDLNA();
const [ui, setUI] = useState({});
const [ui, setUI] = useState<IUIConfig>({});
const [pendingUI, setPendingUI] = useState<{}>();
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
const savePluginConfig = useDebounce(async (input: PluginSettings) => {
try {
@@ -536,6 +545,7 @@ export const SettingsContext: React.FC = ({ children }) => {
dlna,
ui,
plugins,
advancedMode: ui.advancedMode ?? false,
saveGeneral,
saveInterface,
saveDefaults,
@@ -544,6 +554,7 @@ export const SettingsContext: React.FC = ({ children }) => {
saveUI,
refetch,
savePluginSettings,
setAdvancedMode,
}}
>
{maybeRenderLoadingIndicator()}

View File

@@ -410,3 +410,18 @@
.empty-queue-message {
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;
pinnedFilters?: PinnedFilters;
advancedMode?: boolean;
}
interface ISavedFilterRowBroken extends ISavedFilterRow {

View File

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