From 8fdb58917860d7d7dfaadf2676c437158aed8ec4 Mon Sep 17 00:00:00 2001 From: Angelo Date: Thu, 26 Oct 2023 06:15:54 +0100 Subject: [PATCH 1/2] Commit --- ui/v2.5/src/components/List/ItemList.tsx | 64 +++- ui/v2.5/src/components/List/ListFilter.tsx | 2 +- .../components/List/ListOperationButtons.tsx | 6 + .../List/ListToggleConfigButtons.tsx | 339 ++++++++++++++++++ ui/v2.5/src/components/List/styles.scss | 7 + ui/v2.5/src/components/Studios/StudioList.tsx | 46 +++ ui/v2.5/src/components/Tags/TagList.tsx | 6 + ui/v2.5/src/locales/en-GB.json | 7 + ui/v2.5/src/models/list-filter/types.ts | 6 + 9 files changed, 481 insertions(+), 2 deletions(-) create mode 100644 ui/v2.5/src/components/List/ListToggleConfigButtons.tsx diff --git a/ui/v2.5/src/components/List/ItemList.tsx b/ui/v2.5/src/components/List/ItemList.tsx index 8b3aa5898b1..5cd226f4b3d 100644 --- a/ui/v2.5/src/components/List/ItemList.tsx +++ b/ui/v2.5/src/components/List/ItemList.tsx @@ -29,11 +29,16 @@ import { EditFilterDialog } from "src/components/List/EditFilterDialog"; import { ListFilter } from "./ListFilter"; import { FilterTags } from "./FilterTags"; 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, @@ -91,6 +96,7 @@ interface IItemListProps { selectable?: boolean; alterQuery?: boolean; defaultZoomIndex?: number; + configOperations?: IItemListOperation[]; otherOperations?: IItemListOperation[]; renderContent: ( result: T, @@ -142,6 +148,7 @@ export function makeItemList({ persistState, zoomable, selectable, + configOperations, otherOperations, renderContent, renderEditDialog, @@ -362,6 +369,9 @@ export function makeItemList({ result.refetch(); } + function refetch() { + result.refetch(); + }; function onDelete() { setIsDeleteDialogOpen(true); } @@ -430,6 +440,51 @@ 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; @@ -477,10 +532,17 @@ 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} /> + = ({ 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 c279020e9e0..dd3cf86f019 100644 --- a/ui/v2.5/src/components/List/ListOperationButtons.tsx +++ b/ui/v2.5/src/components/List/ListOperationButtons.tsx @@ -16,6 +16,7 @@ import { faTrash, } from "@fortawesome/free-solid-svg-icons"; + interface IListFilterOperation { text: string; onClick: () => void; @@ -31,6 +32,8 @@ interface IListOperationButtonsProps { onDelete?: () => void; itemsSelected?: boolean; otherOperations?: IListFilterOperation[]; + configOperations?: IListFilterOperation[]; + } export const ListOperationButtons: React.FC = ({ @@ -43,6 +46,8 @@ export const ListOperationButtons: React.FC = ({ }) => { const intl = useIntl(); + +//alert(JSON.stringify(location.pathname)); useEffect(() => { Mousetrap.bind("s a", () => onSelectAll?.()); Mousetrap.bind("s n", () => onSelectNone?.()); @@ -68,6 +73,7 @@ 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 new file mode 100644 index 00000000000..db914c88b6c --- /dev/null +++ b/ui/v2.5/src/components/List/ListToggleConfigButtons.tsx @@ -0,0 +1,339 @@ +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 { 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 { ConfigurationContext } from "src/hooks/Config"; +import { IconDefinition } from "@fortawesome/fontawesome-svg-core"; + +interface Page { + page: string; +} +interface IButtonItem { + name: string; + pages: Page[]; + 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", + iconDisabled: faVolumeXmark, + iconEnabled: faVolumeHigh, + mode: "audio", + configString: "config", + }, + { + name: "studios", + pages: [{ page: "studios/" }], + tooltipDisabled: "studios-disabled", + tooltipEnabled: "studios-enabled", + iconDisabled: faSitemap, + iconEnabled: faSitemap, + mode: "studios", + configString: ["config.data?.configuration?.ui?.showChildStudioContent"], + }, + { + name: "tags", + pages: [{ page: "tags/" }], + tooltipDisabled: "tags-disabled", + tooltipEnabled: "tags-enabled", + iconDisabled: faSitemap, + iconEnabled: faSitemap, + mode: "tags", + configString: "config", + }, + { + name: "hover", + pages: [ + { page: "scenes" }, + { page: "images" }, + { page: "galleries" }, + { page: "performers" }, + { 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, +}) => { + const intl = useIntl(); + const { configuration, loading } = React.useContext(ConfigurationContext); + const [buttonItems, setButtonItems] = 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 [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; + + useEffect(() => { + if (audio) { + + } + if (childStudio) { + setChildActive(true); + } + // setActivePage("tags") + if (childTag) { + setChildActive(true); + } + }, [childStudio, childTag]); + + function oTs(mode: number, updatedConfig: string) { + switch (mode) { + case 1: + updatedConfig.ui.showTagCardOnHover = !childActive; + setChildActive(!childActive); + onSetConfigMode(); + break; + case 2: + updatedConfig.ui.showTagCardOnHover = !childActive; + setChildActive(!childActive); + onSetConfigMode(); + break; + default: + break; + } + } + function onSetToggleChildren(mode: string) { + // Clone the config object to avoid mutating the original + const updatedConfig = { ...config.data?.configuration.ui }; + + const updatedConfigs = { ...config.data?.configuration.interface }; + + switch (mode) { + case "audio": + updatedConfigs.soundOnPreview = !childActive; + setChildActive(!childActive); + onSetConfigMode(); + break; + case "studios": + alert("dd") + updatedConfig.showChildStudioContent = !childActive; + setChildActive(!childActive); + onSetConfigMode(); + break; + case "tags": + updatedConfig.ui.showChildTagContent = !childActive; + setChildActive(!childActive); + onSetConfigMode(); + break; + case "tagsHover": + updatedConfig.ui.showTagCardOnHover = !childActive; + setChildActive(!childActive); + onSetConfigMode(); + break; + default: + break; + } + // Now, save the updated config object + saveUI({ + variables: { + input: { + ...config.data?.configuration.ui, + ...updatedConfig, + }, + }, + }); + } + 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 evaluateVariable(key: [key: string]) { + if (config[key] !== undefined && typeof config[key] === 'boolean') { + return config[key]; + } else { + return false; // Return false for any other cases + } + } + + // Filter the buttonItems to include only those with matching pages + const matchingPages = buttonItems.filter((item) => + item.pages.some((page) => location.pathname.includes(page.page)) + ); + + if (matchingPages.length > 0) { + return ( + <> + + {matchingPages.map((matchingPage) => ( + + {intl.formatMessage({ + id: `config_mode.${matchingPage.tooltipEnabled}`, + })} + + } + > + + + ))} + + + ); + } else { + return null; + } + } + + 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 || "")}; +}; diff --git a/ui/v2.5/src/components/List/styles.scss b/ui/v2.5/src/components/List/styles.scss index 1c6a390f411..1a716e292c5 100644 --- a/ui/v2.5/src/components/List/styles.scss +++ b/ui/v2.5/src/components/List/styles.scss @@ -359,3 +359,10 @@ 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/Studios/StudioList.tsx b/ui/v2.5/src/components/Studios/StudioList.tsx index 19710724b86..4640774ef03 100644 --- a/ui/v2.5/src/components/Studios/StudioList.tsx +++ b/ui/v2.5/src/components/Studios/StudioList.tsx @@ -6,6 +6,8 @@ import Mousetrap from "mousetrap"; import * as GQL from "src/core/generated-graphql"; import { queryFindStudios, + useConfiguration, + useConfigureUI, useFindStudios, useStudiosDestroy, } from "src/core/StashService"; @@ -47,6 +49,19 @@ 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 = [ { @@ -108,6 +123,36 @@ 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, @@ -191,6 +236,7 @@ export const StudioList: React.FC = ({ 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 98035f12ec0..932d1562c32 100644 --- a/ui/v2.5/src/components/Tags/TagList.tsx +++ b/ui/v2.5/src/components/Tags/TagList.tsx @@ -78,6 +78,12 @@ 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/locales/en-GB.json b/ui/v2.5/src/locales/en-GB.json index 073ce90432b..55aef4da172 100644 --- a/ui/v2.5/src/locales/en-GB.json +++ b/ui/v2.5/src/locales/en-GB.json @@ -13,6 +13,8 @@ "backup": "Backup", "browse_for_image": "Browse for imageā€¦", "cancel": "Cancel", + "child_studios_scenes_include": "Include child studios scenes", + "child_studios_scenes_exclude": "Exclude child studios scenes", "clean": "Clean", "clear": "Clear", "clear_back_image": "Clear back image", @@ -906,6 +908,11 @@ "dimensions": "Dimensions", "director": "Director", "disambiguation": "Disambiguation", + "config_mode": { + "studios": "Toggle display of child studios", + "tags": "Toggle display of child tags", + "hover": "Show tag card on hover" + }, "display_mode": { "grid": "Grid", "list": "List", diff --git a/ui/v2.5/src/models/list-filter/types.ts b/ui/v2.5/src/models/list-filter/types.ts index bd5aa783b39..368c9db9a3a 100644 --- a/ui/v2.5/src/models/list-filter/types.ts +++ b/ui/v2.5/src/models/list-filter/types.ts @@ -197,3 +197,9 @@ export type CriterionType = | "code" | "disambiguation" | "has_chapters"; + + export enum ConfigMode { + Studios, + Tags, + Hover, + } \ No newline at end of file From 0c24cc927b62dd7ebb8def7d9fc7ea2d89dee74e Mon Sep 17 00:00:00 2001 From: Angelo Date: Thu, 26 Oct 2023 11:12:16 +0100 Subject: [PATCH 2/2] Added Buttons to subnav bar --- ui/v2.5/src/components/List/ItemList.tsx | 65 +--- ui/v2.5/src/components/List/ListFilter.tsx | 1 - .../components/List/ListOperationButtons.tsx | 6 - .../List/ListToggleConfigButtons.tsx | 362 +++++++----------- ui/v2.5/src/components/List/styles.scss | 3 +- .../SettingsInterfacePanel.tsx | 7 + ui/v2.5/src/components/Studios/StudioList.tsx | 46 --- ui/v2.5/src/components/Tags/TagList.tsx | 6 - ui/v2.5/src/core/config.ts | 4 +- ui/v2.5/src/locales/en-GB.json | 17 +- ui/v2.5/src/models/list-filter/types.ts | 6 - 11 files changed, 175 insertions(+), 348 deletions(-) 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