diff --git a/ui/v2.5/src/components/List/ItemList.tsx b/ui/v2.5/src/components/List/ItemList.tsx index 5cd226f4b3d..e230b811fe8 100644 --- a/ui/v2.5/src/components/List/ItemList.tsx +++ b/ui/v2.5/src/components/List/ItemList.tsx @@ -32,13 +32,9 @@ import { ListViewOptions } from "./ListViewOptions"; import { ListToggleConfigButtons } from "./ListToggleConfigButtons"; import { ListOperationButtons } from "./ListOperationButtons"; import { LoadingIndicator } from "../Shared/LoadingIndicator"; -import { ConfigMode } from "src/models/list-filter/types"; import { DisplayMode } from "src/models/list-filter/types"; import { ButtonToolbar } from "react-bootstrap"; -import { - useConfiguration, - useConfigureUI, -} from "src/core/StashService"; + export enum PersistanceLevel { // do not load default query or persist display mode NONE, @@ -96,7 +92,6 @@ interface IItemListProps { selectable?: boolean; alterQuery?: boolean; defaultZoomIndex?: number; - configOperations?: IItemListOperation[]; otherOperations?: IItemListOperation[]; renderContent: ( result: T, @@ -148,7 +143,6 @@ export function makeItemList({ persistState, zoomable, selectable, - configOperations, otherOperations, renderContent, renderEditDialog, @@ -170,6 +164,7 @@ export function makeItemList({ const [arePaging, setArePaging] = useState(false); const hidePagination = !arePaging && result.loading; + const { configuration: config } = React.useContext(ConfigurationContext); // useLayoutEffect to set total count before paint, avoiding a 0 being displayed useLayoutEffect(() => { @@ -369,9 +364,6 @@ export function makeItemList({ result.refetch(); } - function refetch() { - result.refetch(); - }; function onDelete() { setIsDeleteDialogOpen(true); } @@ -440,51 +432,6 @@ export function makeItemList({ ); } - const activePage = location.pathname.match(/^\/([^/]+)(?:\/[^/]+)?/)![1]; - const config = useConfiguration(); - const configSettings = { - showChildStudioContent: config.data?.configuration.ui.showChildStudioContent, - showChildTagContent: config.data?.configuration.ui.showChildTagContent, - showTagCardOnHover: config.data?.configuration.ui.showTagCardOnHover, - }; - - const [childActive, setChildActive] = useState(false); - const [saveUI] = useConfigureUI(); - - function onSetToggleChildren(mode: string) { - // Clone the config object to avoid mutating the original - const updatedConfig = { ...config.data?.configuration.ui }; - - switch (mode) { - case "studios": - updatedConfig.showChildStudioContent = !childActive; - setChildActive(!childActive); - break; - case "tags": - updatedConfig.showChildTagContent = !childActive; - setChildActive(!childActive); - break; - default: - break; - } - - // Now, save the updated config object - saveUI({ - variables: { - input: { - ...config.data?.configuration.ui, - ...updatedConfig, - }, - }, - }); - } - function onChangeConfigMode(configMode: ConfigMode) { - const newFilter = cloneDeep(filter); - //newFilter.configMode = configMode; - // updateFilter(newFilter); - // _onChangePage(1) - } - function onChangeDisplayMode(displayMode: DisplayMode) { const newFilter = cloneDeep(filter); newFilter.displayMode = displayMode; @@ -532,17 +479,11 @@ export function makeItemList({ onSelectAll={selectable ? onSelectAll : undefined} onSelectNone={selectable ? onSelectNone : undefined} otherOperations={operations} - configOperations={operations} itemsSelected={selectedIds.size > 0} onEdit={renderEditDialog ? onEdit : undefined} onDelete={renderDeleteDialog ? onDelete : undefined} /> - + {config?.ui.additionalNavButtons && } = ({ openFilterDialog, persistState, }) => { - const [customPageSizeShowing, setCustomPageSizeShowing] = useState(false); const [queryRef, setQueryFocus] = useFocus(); const [queryClearShowing, setQueryClearShowing] = useState( diff --git a/ui/v2.5/src/components/List/ListOperationButtons.tsx b/ui/v2.5/src/components/List/ListOperationButtons.tsx index dd3cf86f019..c279020e9e0 100644 --- a/ui/v2.5/src/components/List/ListOperationButtons.tsx +++ b/ui/v2.5/src/components/List/ListOperationButtons.tsx @@ -16,7 +16,6 @@ import { faTrash, } from "@fortawesome/free-solid-svg-icons"; - interface IListFilterOperation { text: string; onClick: () => void; @@ -32,8 +31,6 @@ interface IListOperationButtonsProps { onDelete?: () => void; itemsSelected?: boolean; otherOperations?: IListFilterOperation[]; - configOperations?: IListFilterOperation[]; - } export const ListOperationButtons: React.FC = ({ @@ -46,8 +43,6 @@ export const ListOperationButtons: React.FC = ({ }) => { const intl = useIntl(); - -//alert(JSON.stringify(location.pathname)); useEffect(() => { Mousetrap.bind("s a", () => onSelectAll?.()); Mousetrap.bind("s n", () => onSelectNone?.()); @@ -73,7 +68,6 @@ export const ListOperationButtons: React.FC = ({ }); function maybeRenderButtons() { - const buttons = (otherOperations ?? []).filter((o) => { if (!o.icon) { return false; diff --git a/ui/v2.5/src/components/List/ListToggleConfigButtons.tsx b/ui/v2.5/src/components/List/ListToggleConfigButtons.tsx index db914c88b6c..446dfe58461 100644 --- a/ui/v2.5/src/components/List/ListToggleConfigButtons.tsx +++ b/ui/v2.5/src/components/List/ListToggleConfigButtons.tsx @@ -1,243 +1,234 @@ import React, { useEffect, useState } from "react"; import { useLocation } from "react-router-dom"; -import { - Button, - ButtonGroup, - Form, - OverlayTrigger, - Tooltip, -} from "react-bootstrap"; -import { ConfigMode } from "src/models/list-filter/types"; +import { Button, ButtonGroup, OverlayTrigger, Tooltip } from "react-bootstrap"; import { useIntl } from "react-intl"; import { Icon } from "../Shared/Icon"; import { - faEye, - faEyeSlash, - faTree, - faCircleCheck, faSitemap, - faIoxhost, faLayerGroup, - faTags, faVolumeXmark, faVolumeHigh, } from "@fortawesome/free-solid-svg-icons"; -import { useConfiguration, useConfigureUI } from "src/core/StashService"; +import { + useConfiguration, + useConfigureInterface, + useConfigureUI, +} from "src/core/StashService"; import { ConfigurationContext } from "src/hooks/Config"; import { IconDefinition } from "@fortawesome/fontawesome-svg-core"; -interface Page { +interface IPage { page: string; } interface IButtonItem { name: string; - pages: Page[]; + pages: IPage[]; tooltipDisabled: string; tooltipEnabled: string; iconDisabled: IconDefinition; iconEnabled: IconDefinition; - mode: string; - configString: [key: string]; } const allButtonItems: IButtonItem[] = [ { - name: "audio", - pages: [ - { page: "scenes" }, - { page: "markers" }, - ], - tooltipDisabled: "studios-disabled", - tooltipEnabled: "studios-enabled", + name: "toggleAudio", + pages: [{ page: "scenes" }, { page: "markers" }], + tooltipDisabled: "audio-disabled", + tooltipEnabled: "audio-enabled", iconDisabled: faVolumeXmark, iconEnabled: faVolumeHigh, - mode: "audio", - configString: "config", }, { - name: "studios", + name: "toggleChildStudios", pages: [{ page: "studios/" }], tooltipDisabled: "studios-disabled", tooltipEnabled: "studios-enabled", iconDisabled: faSitemap, iconEnabled: faSitemap, - mode: "studios", - configString: ["config.data?.configuration?.ui?.showChildStudioContent"], }, { - name: "tags", + name: "toggleChildTags", pages: [{ page: "tags/" }], tooltipDisabled: "tags-disabled", tooltipEnabled: "tags-enabled", iconDisabled: faSitemap, iconEnabled: faSitemap, - mode: "tags", - configString: "config", }, { - name: "hover", + name: "toggleTagsHover", pages: [ { page: "scenes" }, { page: "images" }, { page: "galleries" }, { page: "performers" }, - { page: "tags" }, + { page: "studios/" }, + { page: "tags/" }, ], tooltipDisabled: "tags-hover-disabled", tooltipEnabled: "tags-hover-enabled", iconDisabled: faLayerGroup, iconEnabled: faLayerGroup, - mode: "tagsHover", - configString: "config", }, ]; - -interface IListToggleConfigSettingsProps { - activePage: string; - configMode: ConfigMode; - settings: { - showChildStudioContent: boolean | undefined; - showChildTagContent: boolean | undefined; - showTagCardOnHover: boolean | undefined; - }; - onSetConfigMode: (m: ConfigMode) => void; - configModeOptions: ConfigMode[]; -} - -export const ListToggleConfigButtons: React.FC< - IListToggleConfigSettingsProps -> = ({ - activePage, - configMode, - settings, - onSetConfigMode, - configModeOptions, -}) => { +export const ListToggleConfigButtons: React.FC = ({}) => { const intl = useIntl(); - const { configuration, loading } = React.useContext(ConfigurationContext); - const [buttonItems, setButtonItems] = useState(allButtonItems); + const { configuration } = React.useContext(ConfigurationContext); + const [buttonItems] = useState(allButtonItems); const location = useLocation(); - const pathStudios = location.pathname.includes("studios"); - const pathTags = location.pathname.includes("tags"); - useEffect(() => {}, [configuration]); - - //const [activePage, setActivePage] = useState(); const config = useConfiguration(); const [saveUI] = useConfigureUI(); + const [saveInterface] = useConfigureInterface(); - const [childActive, setChildActive] = useState(false); const [toggleAudio, setToggleAudio] = useState(false); const [toggleChildStudios, setToggleChildStudios] = useState(false); const [toggleChildTags, setToggleChildTags] = useState(false); - const [toggleTagHoverActive, setToggleTagHoverActive] = useState(false); - - const audio = config.data?.configuration?.interface?.soundOnPreview; - const childStudio = config.data?.configuration?.ui?.showChildStudioContent; - - const childTag = config.data?.configuration?.ui?.showChildTagContent; - - const tagHover = config.data?.configuration?.ui?.showTagCardOnHover; + const [toggleTagsHover, setToggleTagsHover] = useState(false); useEffect(() => { - if (audio) { - - } - if (childStudio) { - setChildActive(true); - } - // setActivePage("tags") - if (childTag) { - setChildActive(true); + const audio = configuration?.interface?.soundOnPreview; + const childStudio = configuration?.ui?.showChildStudioContent; + const childTag = configuration?.ui?.showChildTagContent; + const tagHover = configuration?.ui?.showTagCardOnHover; + + if (audio !== undefined && audio != null) { + setToggleAudio(audio); + } + if (childStudio !== undefined) { + setToggleChildStudios(childStudio); + } + if (childTag !== undefined) { + setToggleChildTags(childTag); } - }, [childStudio, childTag]); + if (tagHover !== undefined) { + setToggleTagsHover(tagHover); + } + }, [configuration]); - function oTs(mode: number, updatedConfig: string) { + function setConfigure(mode: string, updatedConfig: object) { switch (mode) { - case 1: - updatedConfig.ui.showTagCardOnHover = !childActive; - setChildActive(!childActive); - onSetConfigMode(); - break; - case 2: - updatedConfig.ui.showTagCardOnHover = !childActive; - setChildActive(!childActive); - onSetConfigMode(); + case "interface": + saveInterface({ + variables: { + input: { + ...config.data?.configuration.interface, + ...updatedConfig, + }, + }, + }); break; - default: + case "ui": + saveUI({ + variables: { + input: { + ...config.data?.configuration, + ...updatedConfig, + }, + }, + }); break; } } - function onSetToggleChildren(mode: string) { - // Clone the config object to avoid mutating the original + + function onSetToggleSetting(matchingPage: IButtonItem) { const updatedConfig = { ...config.data?.configuration.ui }; + const updatedConfigInt = { ...config.data?.configuration.interface }; - const updatedConfigs = { ...config.data?.configuration.interface }; + let mode: string = ""; + let shouldToggle: boolean = false; + let shouldToggleInt: boolean = false; - switch (mode) { - case "audio": - updatedConfigs.soundOnPreview = !childActive; - setChildActive(!childActive); - onSetConfigMode(); + switch (matchingPage.name) { + case "toggleAudio": + mode = "interface"; + shouldToggleInt = true; + updatedConfigInt.soundOnPreview = !toggleAudio; + setToggleAudio((prevToggleAudio) => !prevToggleAudio); break; - case "studios": - alert("dd") - updatedConfig.showChildStudioContent = !childActive; - setChildActive(!childActive); - onSetConfigMode(); + case "toggleChildStudios": + mode = "ui"; + shouldToggle = true; + updatedConfig.showChildStudioContent = !toggleChildStudios; + setToggleChildStudios(!toggleChildStudios); break; - case "tags": - updatedConfig.ui.showChildTagContent = !childActive; - setChildActive(!childActive); - onSetConfigMode(); + case "toggleChildTags": + mode = "ui"; + shouldToggle = true; + updatedConfig.showChildTagContent = !toggleChildTags; + // section = "showChildTagContent" + setToggleChildTags((prevToggleChildTags) => !prevToggleChildTags); break; - case "tagsHover": - updatedConfig.ui.showTagCardOnHover = !childActive; - setChildActive(!childActive); - onSetConfigMode(); + case "toggleTagsHover": + mode = "ui"; + shouldToggle = true; + updatedConfig.showTagCardOnHover = !toggleTagsHover; + setToggleTagsHover(!toggleTagsHover); break; default: break; } - // Now, save the updated config object - saveUI({ - variables: { - input: { - ...config.data?.configuration.ui, - ...updatedConfig, - }, - }, - }); + if (shouldToggle) { + setConfigure(mode, updatedConfig); + } + + if (shouldToggleInt) { + setConfigure(mode, updatedConfigInt); + } } - const ss = JSON.stringify(config.data); - function maybeRenderChildButtons(mode: string) { - let setMode: string = ""; - let childToolTip: string = ""; - switch (mode) { - case "studios": - setMode = "studios"; - childToolTip = "Toggle display of child studios"; - break; - case "tags": - setMode = "tags"; - childToolTip = "Toggle display child tags"; - break; - default: - // Handle the default case - setMode = ""; - childToolTip = ""; + function maybeRenderButtons() { + function evaluateEnabled(matchingPage: IButtonItem) { + let enabled: boolean; + + switch (matchingPage.name) { + case "toggleAudio": + enabled = toggleAudio; + break; + case "toggleChildStudios": + enabled = toggleChildStudios; + break; + case "toggleChildTags": + enabled = toggleChildTags; + break; + case "toggleTagsHover": + enabled = toggleTagsHover; + break; + default: + enabled = false; + } + return enabled; } - function evaluateVariable(key: [key: string]) { - if (config[key] !== undefined && typeof config[key] === 'boolean') { - return config[key]; - } else { - return false; // Return false for any other cases + function returnTooltip(matchingPage: IButtonItem) { + let enabled: boolean; + let returnValue: string; + + switch (matchingPage.name) { + case "toggleAudio": + enabled = !toggleAudio; + break; + case "toggleChildStudios": + enabled = !toggleChildStudios; + break; + case "toggleChildTags": + enabled = !toggleChildTags; + break; + case "toggleTagsHover": + enabled = !toggleTagsHover; + break; + default: + enabled = false; } + + const tooltipKey = enabled + ? matchingPage.tooltipEnabled + : matchingPage.tooltipDisabled; + + returnValue = `config_mode.${tooltipKey}`; + + return returnValue; } - // Filter the buttonItems to include only those with matching pages const matchingPages = buttonItems.filter((item) => item.pages.some((page) => location.pathname.includes(page.page)) ); @@ -250,22 +241,22 @@ export const ListToggleConfigButtons: React.FC< + {intl.formatMessage({ - id: `config_mode.${matchingPage.tooltipEnabled}`, + id: returnTooltip(matchingPage), })} } > @@ -278,62 +269,5 @@ export const ListToggleConfigButtons: React.FC< } } - function maybeRenderConfigModeOptions() { - function getIcon(option: ConfigMode) { - switch (option) { - case ConfigMode.Studios: - return faTree; - case ConfigMode.Tags: - return faTree; - case ConfigMode.Hover: - return faTags; - } - } - - function getLabel(option: string) { - let configModeId = "unknown"; - switch (option) { - case "studios": - configModeId = "studios"; - break; - case "tags": - configModeId = "tags"; - break; - case "hover": - configModeId = "hover"; - break; - } - return intl.formatMessage({ id: `config_mode.${configModeId}` }); - } - - if (configModeOptions.length < 2) { - return; - } - - return ( - - {configModeOptions.map((option) => ( - {getLabel(option)} - } - > - - - ))} - - ); - } - - return <>{maybeRenderChildButtons(activePage || "")}; + return <>{maybeRenderButtons()}; }; diff --git a/ui/v2.5/src/components/List/styles.scss b/ui/v2.5/src/components/List/styles.scss index 1a716e292c5..f0098d95cec 100644 --- a/ui/v2.5/src/components/List/styles.scss +++ b/ui/v2.5/src/components/List/styles.scss @@ -359,10 +359,11 @@ input[type="range"].zoom-slider { .tilted { transform: rotate(45deg); } + .input-group { padding: 0.15rem; } .btn-group-actions { margin-right: 0.5rem; -} \ No newline at end of file +} diff --git a/ui/v2.5/src/components/Settings/SettingsInterfacePanel/SettingsInterfacePanel.tsx b/ui/v2.5/src/components/Settings/SettingsInterfacePanel/SettingsInterfacePanel.tsx index c3c36417e04..e954fcedc8e 100644 --- a/ui/v2.5/src/components/Settings/SettingsInterfacePanel/SettingsInterfacePanel.tsx +++ b/ui/v2.5/src/components/Settings/SettingsInterfacePanel/SettingsInterfacePanel.tsx @@ -206,6 +206,13 @@ export const SettingsInterfacePanel: React.FC = () => { checked={ui.abbreviateCounters ?? undefined} onChange={(v) => saveUI({ abbreviateCounters: v })} /> + saveUI({ additionalNavButtons: v })} + /> diff --git a/ui/v2.5/src/components/Studios/StudioList.tsx b/ui/v2.5/src/components/Studios/StudioList.tsx index 4640774ef03..19710724b86 100644 --- a/ui/v2.5/src/components/Studios/StudioList.tsx +++ b/ui/v2.5/src/components/Studios/StudioList.tsx @@ -6,8 +6,6 @@ import Mousetrap from "mousetrap"; import * as GQL from "src/core/generated-graphql"; import { queryFindStudios, - useConfiguration, - useConfigureUI, useFindStudios, useStudiosDestroy, } from "src/core/StashService"; @@ -49,19 +47,6 @@ export const StudioList: React.FC = ({ const history = useHistory(); const [isExportDialogOpen, setIsExportDialogOpen] = useState(false); const [isExportAll, setIsExportAll] = useState(false); - const config = useConfiguration(); - const [saveUI] = useConfigureUI(); - - const configOperations = [ - { - text: intl.formatMessage({ id: "actions.child_studios_scenes_include" }), - onClick: toggleChildStudios, - }, - { - text: intl.formatMessage({ id: "actions.child_studios_scenes_exclude" }), - onClick: toggleChildStudios, - }, - ]; const otherOperations = [ { @@ -123,36 +108,6 @@ export const StudioList: React.FC = ({ setIsExportDialogOpen(true); } - async function toggleChildStudios(result: GQL.FindStudiosQueryResult, - filter: ListFilterModel - ) { - if (result.data?.findStudios) { -alert("hello") - const currentConfig = config.data?.configuration.ui.showChildStudioContent; - const updateConfig = { ...config.data?.configuration.ui }; - - switch (currentConfig) { - case true: - updateConfig.showChildStudioContent = !currentConfig; - break; - case false: - updateConfig.showChildTagContent = !currentConfig; - break; - default: - break; - } - - saveUI({ - variables: { - input: { - ...config.data?.configuration.ui, - ...updateConfig, - }, - }, - }); - } - } - function renderContent( result: GQL.FindStudiosQueryResult, filter: ListFilterModel, @@ -236,7 +191,6 @@ alert("hello") filterHook={filterHook} persistState={fromParent ? PersistanceLevel.NONE : PersistanceLevel.ALL} alterQuery={alterQuery} - configOperations={configOperations} otherOperations={otherOperations} addKeybinds={addKeybinds} renderContent={renderContent} diff --git a/ui/v2.5/src/components/Tags/TagList.tsx b/ui/v2.5/src/components/Tags/TagList.tsx index 932d1562c32..98035f12ec0 100644 --- a/ui/v2.5/src/components/Tags/TagList.tsx +++ b/ui/v2.5/src/components/Tags/TagList.tsx @@ -78,12 +78,6 @@ export const TagList: React.FC = ({ filterHook, alterQuery }) => { text: intl.formatMessage({ id: "actions.export_all" }), onClick: onExportAll, }, - { - text: intl.formatMessage({ id: "actions.child_studios_scenes_include" }), - }, - { - text: intl.formatMessage({ id: "actions.child_studios_scenes_exclude" }), - }, ]; function addKeybinds( diff --git a/ui/v2.5/src/core/config.ts b/ui/v2.5/src/core/config.ts index 55edf2d0115..ffdad73ede6 100644 --- a/ui/v2.5/src/core/config.ts +++ b/ui/v2.5/src/core/config.ts @@ -35,13 +35,13 @@ export const defaultMaxOptionsShown = 200; export interface IUIConfig { // unknown to prevent direct access - use getFrontPageContent frontPageContent?: unknown; - showChildTagContent?: boolean; showChildStudioContent?: boolean; showTagCardOnHover?: boolean; - abbreviateCounters?: boolean; + // if true a additional buttons will display on subnav bar + additionalNavButtons?: boolean; ratingSystemOptions?: RatingSystemOptions; // if true a background image will be display on header diff --git a/ui/v2.5/src/locales/en-GB.json b/ui/v2.5/src/locales/en-GB.json index 55aef4da172..19b27571517 100644 --- a/ui/v2.5/src/locales/en-GB.json +++ b/ui/v2.5/src/locales/en-GB.json @@ -530,6 +530,10 @@ "heading": "Abbreviate counters" }, "basic_settings": "Basic Settings", + "buttons_navigation": { + "description": "Show addtional control buttons on secondary navbar", + "heading": "Sub navbar controls enabled" + }, "custom_css": { "description": "Page must be reloaded for changes to take effect. There is no guarantee of compatibility between custom CSS and future releases of Stash.", "heading": "Custom CSS", @@ -909,9 +913,14 @@ "director": "Director", "disambiguation": "Disambiguation", "config_mode": { - "studios": "Toggle display of child studios", - "tags": "Toggle display of child tags", - "hover": "Show tag card on hover" + "audio-disabled": "Disable audio on Scene / Marker wall", + "audio-enabled": "Enable audio on Scene / Marker wall", + "studios-disabled": "Disable display of child studios items", + "studios-enabled": "Enable display of child studios items", + "tags-disabled": "Disable display of child tag items", + "tags-enabled": "Enable display of child tag items", + "tags-hover-disabled": "Disable tag card when hovering tag badges", + "tags-hover-enabled": "Enable tag card when hovering tag badges" }, "display_mode": { "grid": "Grid", @@ -1374,4 +1383,4 @@ "weight_kg": "Weight (kg)", "years_old": "years old", "zip_file_count": "Zip File Count" -} \ No newline at end of file +} diff --git a/ui/v2.5/src/models/list-filter/types.ts b/ui/v2.5/src/models/list-filter/types.ts index 368c9db9a3a..bd5aa783b39 100644 --- a/ui/v2.5/src/models/list-filter/types.ts +++ b/ui/v2.5/src/models/list-filter/types.ts @@ -197,9 +197,3 @@ export type CriterionType = | "code" | "disambiguation" | "has_chapters"; - - export enum ConfigMode { - Studios, - Tags, - Hover, - } \ No newline at end of file