mirror of
https://github.com/stashapp/stash.git
synced 2026-01-03 22:24:41 +01:00
Use local storage for UI configuration
This commit is contained in:
parent
9098699249
commit
e5b9db2821
8 changed files with 137 additions and 10 deletions
|
|
@ -17,6 +17,7 @@
|
|||
"bulma": "0.7.4",
|
||||
"formik": "1.5.1",
|
||||
"graphql": "14.1.1",
|
||||
"localforage": "1.7.3",
|
||||
"lodash": "4.17.11",
|
||||
"node-sass": "4.11.0",
|
||||
"query-string": "6.2.0",
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import React, { FunctionComponent, useEffect, useState } from "react";
|
|||
import { IBaseProps } from "../../models";
|
||||
import { SettingsAboutPanel } from "./SettingsAboutPanel";
|
||||
import { SettingsConfigurationPanel } from "./SettingsConfigurationPanel";
|
||||
import { SettingsInterfacePanel } from "./SettingsInterfacePanel";
|
||||
import { SettingsLogsPanel } from "./SettingsLogsPanel";
|
||||
import { SettingsTasksPanel } from "./SettingsTasksPanel/SettingsTasksPanel";
|
||||
|
||||
|
|
@ -39,6 +40,7 @@ export const Settings: FunctionComponent<IProps> = (props: IProps) => {
|
|||
defaultSelectedTabId={getTabId()}
|
||||
>
|
||||
<Tab id="configuration" title="Configuration" panel={<SettingsConfigurationPanel />} />
|
||||
<Tab id="interface" title="Interface Configuration" panel={<SettingsInterfacePanel />} />
|
||||
<Tab id="tasks" title="Tasks" panel={<SettingsTasksPanel />} />
|
||||
<Tab id="logs" title="Logs" panel={<SettingsLogsPanel />} />
|
||||
<Tab id="about" title="About" panel={<SettingsAboutPanel />} />
|
||||
|
|
|
|||
35
ui/v2/src/components/Settings/SettingsInterfacePanel.tsx
Normal file
35
ui/v2/src/components/Settings/SettingsInterfacePanel.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import {
|
||||
Checkbox,
|
||||
FormGroup,
|
||||
H4,
|
||||
} from "@blueprintjs/core";
|
||||
import _ from "lodash";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { useInterfaceLocalForage } from "../../hooks/LocalForage";
|
||||
|
||||
interface IProps {}
|
||||
|
||||
export const SettingsInterfacePanel: FunctionComponent<IProps> = () => {
|
||||
const {data, setData} = useInterfaceLocalForage();
|
||||
|
||||
return (
|
||||
<>
|
||||
<H4>User Interface</H4>
|
||||
<FormGroup
|
||||
label="Scene / Marker Wall"
|
||||
helperText="Configuration for wall items"
|
||||
>
|
||||
<Checkbox
|
||||
checked={!!data ? data.wall.textContainerEnabled : true}
|
||||
label="Display title and tags"
|
||||
onChange={() => {
|
||||
if (!data) { return; }
|
||||
const newSettings = _.cloneDeep(data);
|
||||
newSettings.wall.textContainerEnabled = !data.wall.textContainerEnabled;
|
||||
setData(newSettings);
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
@ -55,8 +55,6 @@ export const Stats: FunctionComponent = () => {
|
|||
This is still an early version, some things are still a work in progress.
|
||||
|
||||
* Filters for performers and studios only supports one item, even though it's a multi select.
|
||||
* All of the task buttons in settings do work, but provide no feedback in the UI currently.
|
||||
* The tasks tab is the only tab with content in the settings menu.
|
||||
|
||||
TODO:
|
||||
* List view for scenes / performers
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@
|
|||
.scene-wall-item-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
// align-items: center;
|
||||
// overflow: hidden; // Commented out since it shows gaps in the wall
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import _ from "lodash";
|
|||
import React, { FunctionComponent, useRef, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import * as GQL from "../../core/generated-graphql";
|
||||
import { useInterfaceLocalForage } from "../../hooks/LocalForage";
|
||||
import { VideoHoverHook } from "../../hooks/VideoHover";
|
||||
import { TextUtils } from "../../utils/text";
|
||||
|
||||
|
|
@ -16,6 +17,8 @@ interface IWallItemProps {
|
|||
export const WallItem: FunctionComponent<IWallItemProps> = (props: IWallItemProps) => {
|
||||
const [videoPath, setVideoPath] = useState<string | undefined>(undefined);
|
||||
const videoHoverHook = VideoHoverHook.useVideoHover({resetOnMouseLeave: true});
|
||||
const interfaceSettings = useInterfaceLocalForage();
|
||||
const showTextContainer = !!interfaceSettings.data ? interfaceSettings.data.wall.textContainerEnabled : true;
|
||||
|
||||
function onMouseEnter() {
|
||||
VideoHoverHook.onMouseEnter(videoHoverHook);
|
||||
|
|
@ -101,12 +104,14 @@ export const WallItem: FunctionComponent<IWallItemProps> = (props: IWallItemProp
|
|||
ref={videoHoverHook.videoEl}
|
||||
/>
|
||||
<img src={previewSrc} />
|
||||
<div className="scene-wall-item-text-container">
|
||||
<div style={{lineHeight: 1}}>
|
||||
{title}
|
||||
</div>
|
||||
{tags}
|
||||
</div>
|
||||
{showTextContainer ?
|
||||
<div className="scene-wall-item-text-container">
|
||||
<div style={{lineHeight: 1}}>
|
||||
{title}
|
||||
</div>
|
||||
{tags}
|
||||
</div> : undefined
|
||||
}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
67
ui/v2/src/hooks/LocalForage.ts
Normal file
67
ui/v2/src/hooks/LocalForage.ts
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import localForage from "localforage";
|
||||
import _ from "lodash";
|
||||
import React from "react";
|
||||
|
||||
interface IInterfaceWallConfig {
|
||||
textContainerEnabled: boolean;
|
||||
}
|
||||
export interface IInterfaceConfig {
|
||||
wall: IInterfaceWallConfig;
|
||||
}
|
||||
|
||||
type ValidTypes = IInterfaceConfig | undefined;
|
||||
|
||||
interface ILocalForage<T> {
|
||||
data: T;
|
||||
setData: React.Dispatch<React.SetStateAction<T>>;
|
||||
error: Error | null;
|
||||
}
|
||||
|
||||
export function useInterfaceLocalForage(): ILocalForage<IInterfaceConfig | undefined> {
|
||||
const result = useLocalForage("interface");
|
||||
// Set defaults
|
||||
React.useEffect(() => {
|
||||
if (result.data === undefined) {
|
||||
result.setData({
|
||||
wall: {
|
||||
textContainerEnabled: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function useLocalForage(item: string): ILocalForage<ValidTypes> {
|
||||
const [json, setJson] = React.useState<ValidTypes>(undefined);
|
||||
|
||||
const prevJson = React.useRef<ValidTypes>(undefined);
|
||||
React.useEffect(() => {
|
||||
async function runAsync() {
|
||||
if (typeof json !== "undefined" && !_.isEqual(json, prevJson.current)) {
|
||||
await localForage.setItem(item, JSON.stringify(json));
|
||||
}
|
||||
prevJson.current = json;
|
||||
}
|
||||
runAsync();
|
||||
});
|
||||
|
||||
const [err, setErr] = React.useState(null);
|
||||
React.useEffect(() => {
|
||||
async function runAsync() {
|
||||
try {
|
||||
const serialized = await localForage.getItem<any>(item);
|
||||
const parsed = JSON.parse(serialized);
|
||||
if (typeof json === "undefined" && !Object.is(parsed, null)) {
|
||||
setErr(null);
|
||||
setJson(parsed);
|
||||
}
|
||||
} catch (error) {
|
||||
setErr(error);
|
||||
}
|
||||
}
|
||||
runAsync();
|
||||
});
|
||||
|
||||
return {data: json, setData: setJson, error: err};
|
||||
}
|
||||
|
|
@ -5448,6 +5448,11 @@ ignore@^4.0.6:
|
|||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
|
||||
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
|
||||
|
||||
immediate@~3.0.5:
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
|
||||
integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=
|
||||
|
||||
immer@1.10.0:
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d"
|
||||
|
|
@ -6751,6 +6756,13 @@ levn@^0.3.0, levn@~0.3.0:
|
|||
prelude-ls "~1.1.2"
|
||||
type-check "~0.3.2"
|
||||
|
||||
lie@3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
|
||||
integrity sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=
|
||||
dependencies:
|
||||
immediate "~3.0.5"
|
||||
|
||||
listr-silent-renderer@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e"
|
||||
|
|
@ -6838,6 +6850,13 @@ loader-utils@1.2.3, loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.
|
|||
emojis-list "^2.0.0"
|
||||
json5 "^1.0.1"
|
||||
|
||||
localforage@1.7.3:
|
||||
version "1.7.3"
|
||||
resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.7.3.tgz#0082b3ca9734679e1bd534995bdd3b24cf10f204"
|
||||
integrity sha512-1TulyYfc4udS7ECSBT2vwJksWbkwwTX8BzeUIiq8Y07Riy7bDAAnxDaPU/tWyOVmQAcWJIEIFP9lPfBGqVoPgQ==
|
||||
dependencies:
|
||||
lie "3.1.1"
|
||||
|
||||
locate-path@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
|
||||
|
|
|
|||
Loading…
Reference in a new issue