Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sub navbar render extra optional buttons for ui & interface config settings. #4245

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ui/v2.5/src/components/List/ItemList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ 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 { DisplayMode } from "src/models/list-filter/types";
Expand Down Expand Up @@ -163,6 +164,7 @@ export function makeItemList<T extends QueryResult, E extends IDataItem>({

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(() => {
Expand Down Expand Up @@ -481,6 +483,7 @@ export function makeItemList<T extends QueryResult, E extends IDataItem>({
onEdit={renderEditDialog ? onEdit : undefined}
onDelete={renderDeleteDialog ? onDelete : undefined}
/>
{config?.ui.additionalNavButtons && <ListToggleConfigButtons />}
<ListViewOptions
displayMode={filter.displayMode}
displayModeOptions={filterOptions.displayModeOptions}
Expand Down
1 change: 0 additions & 1 deletion ui/v2.5/src/components/List/ListFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import {
Popover,
Overlay,
} from "react-bootstrap";

import { Icon } from "../Shared/Icon";
import { ListFilterModel } from "src/models/list-filter/filter";
import useFocus from "src/utils/focus";
Expand Down
273 changes: 273 additions & 0 deletions ui/v2.5/src/components/List/ListToggleConfigButtons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
import React, { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
import { Button, ButtonGroup, OverlayTrigger, Tooltip } from "react-bootstrap";
import { useIntl } from "react-intl";
import { Icon } from "../Shared/Icon";
import {
faSitemap,
faLayerGroup,
faVolumeXmark,
faVolumeHigh,
} from "@fortawesome/free-solid-svg-icons";
import {
useConfiguration,
useConfigureInterface,
useConfigureUI,
} from "src/core/StashService";
import { ConfigurationContext } from "src/hooks/Config";
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";

interface IPage {
page: string;
}
interface IButtonItem {
name: string;
pages: IPage[];
tooltipDisabled: string;
tooltipEnabled: string;
iconDisabled: IconDefinition;
iconEnabled: IconDefinition;
}

const allButtonItems: IButtonItem[] = [
{
name: "toggleAudio",
pages: [{ page: "scenes" }, { page: "markers" }],
tooltipDisabled: "audio-disabled",
tooltipEnabled: "audio-enabled",
iconDisabled: faVolumeXmark,
iconEnabled: faVolumeHigh,
},
{
name: "toggleChildStudios",
pages: [{ page: "studios/" }],
tooltipDisabled: "studios-disabled",
tooltipEnabled: "studios-enabled",
iconDisabled: faSitemap,
iconEnabled: faSitemap,
},
{
name: "toggleChildTags",
pages: [{ page: "tags/" }],
tooltipDisabled: "tags-disabled",
tooltipEnabled: "tags-enabled",
iconDisabled: faSitemap,
iconEnabled: faSitemap,
},
{
name: "toggleTagsHover",
pages: [
{ page: "scenes" },
{ page: "images" },
{ page: "galleries" },
{ page: "performers" },
{ page: "studios/" },
{ page: "tags/" },
],
tooltipDisabled: "tags-hover-disabled",
tooltipEnabled: "tags-hover-enabled",
iconDisabled: faLayerGroup,
iconEnabled: faLayerGroup,
},
];
export const ListToggleConfigButtons: React.FC = ({}) => {
const intl = useIntl();
const { configuration } = React.useContext(ConfigurationContext);
const [buttonItems] = useState<IButtonItem[]>(allButtonItems);
const location = useLocation();

const config = useConfiguration();
const [saveUI] = useConfigureUI();
const [saveInterface] = useConfigureInterface();

const [toggleAudio, setToggleAudio] = useState<boolean>(false);
const [toggleChildStudios, setToggleChildStudios] = useState<boolean>(false);
const [toggleChildTags, setToggleChildTags] = useState<boolean>(false);
const [toggleTagsHover, setToggleTagsHover] = useState<boolean>(false);

useEffect(() => {
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);
}
if (tagHover !== undefined) {
setToggleTagsHover(tagHover);
}
}, [configuration]);

function setConfigure(mode: string, updatedConfig: object) {
switch (mode) {
case "interface":
saveInterface({
variables: {
input: {
...config.data?.configuration.interface,
...updatedConfig,
},
},
});
break;
case "ui":
saveUI({
variables: {
input: {
...config.data?.configuration,
...updatedConfig,
},
},
});
break;
}
}

function onSetToggleSetting(matchingPage: IButtonItem) {
const updatedConfig = { ...config.data?.configuration.ui };
const updatedConfigInt = { ...config.data?.configuration.interface };

let mode: string = "";
let shouldToggle: boolean = false;
let shouldToggleInt: boolean = false;

switch (matchingPage.name) {
case "toggleAudio":
mode = "interface";
shouldToggleInt = true;
updatedConfigInt.soundOnPreview = !toggleAudio;
setToggleAudio((prevToggleAudio) => !prevToggleAudio);
break;
case "toggleChildStudios":
mode = "ui";
shouldToggle = true;
updatedConfig.showChildStudioContent = !toggleChildStudios;
setToggleChildStudios(!toggleChildStudios);
break;
case "toggleChildTags":
mode = "ui";
shouldToggle = true;
updatedConfig.showChildTagContent = !toggleChildTags;
// section = "showChildTagContent"
setToggleChildTags((prevToggleChildTags) => !prevToggleChildTags);
break;
case "toggleTagsHover":
mode = "ui";
shouldToggle = true;
updatedConfig.showTagCardOnHover = !toggleTagsHover;
setToggleTagsHover(!toggleTagsHover);
break;
default:
break;
}
if (shouldToggle) {
setConfigure(mode, updatedConfig);
}

if (shouldToggleInt) {
setConfigure(mode, updatedConfigInt);
}
}

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 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;
}

const matchingPages = buttonItems.filter((item) =>
item.pages.some((page) => location.pathname.includes(page.page))
);

if (matchingPages.length > 0) {
return (
<>
<ButtonGroup className="mb-2 btn-group-actions">
{matchingPages.map((matchingPage) => (
<OverlayTrigger
key={"showChildren"} // This key should be unique for each OverlayTrigger
overlay={
<Tooltip id={matchingPage.name}>
{intl.formatMessage({
id: returnTooltip(matchingPage),
})}
</Tooltip>
}
>
<Button
variant="secondary"
active={evaluateEnabled(matchingPage)}
onClick={() => onSetToggleSetting(matchingPage)}
>
{evaluateEnabled(matchingPage) ? (
<Icon icon={matchingPage.iconEnabled} color="lime" />
) : (
<Icon icon={matchingPage.iconDisabled} />
)}
</Button>
</OverlayTrigger>
))}
</ButtonGroup>
</>
);
} else {
return null;
}
}

return <>{maybeRenderButtons()}</>;
};
8 changes: 8 additions & 0 deletions ui/v2.5/src/components/List/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -359,3 +359,11 @@ input[type="range"].zoom-slider {
.tilted {
transform: rotate(45deg);
}

.input-group {
padding: 0.15rem;
}

.btn-group-actions {
margin-right: 0.5rem;
}
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,13 @@ export const SettingsInterfacePanel: React.FC = () => {
checked={ui.abbreviateCounters ?? undefined}
onChange={(v) => saveUI({ abbreviateCounters: v })}
/>
<BooleanSetting
id="buttons-navigation"
headingID="config.ui.buttons_navigation.heading"
subHeadingID="config.ui.buttons_navigation.description"
checked={ui.additionalNavButtons ?? undefined}
onChange={(v) => saveUI({ additionalNavButtons: v })}
/>
</SettingSection>

<SettingSection headingID="config.ui.desktop_integration.desktop_integration">
Expand Down
4 changes: 2 additions & 2 deletions ui/v2.5/src/core/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 17 additions & 1 deletion ui/v2.5/src/locales/en-GB.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -528,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",
Expand Down Expand Up @@ -906,6 +912,16 @@
"dimensions": "Dimensions",
"director": "Director",
"disambiguation": "Disambiguation",
"config_mode": {
"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",
"list": "List",
Expand Down Expand Up @@ -1367,4 +1383,4 @@
"weight_kg": "Weight (kg)",
"years_old": "years old",
"zip_file_count": "Zip File Count"
}
}
Loading