Customize recommendations (#2592)

* refactored common code in recommendation row
* Implement front page options in config
* Allow customisation from front page
* Rename recommendations to front page
* Add generic UI settings
* Support adding premade filters

Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
This commit is contained in:
CJ
2022-06-13 19:34:04 -05:00
committed by GitHub
parent ff724d82cc
commit 9264c15540
38 changed files with 1549 additions and 292 deletions

View File

@@ -8,6 +8,7 @@ import React, {
useRef,
} from "react";
import { Spinner } from "react-bootstrap";
import { IUIConfig } from "src/core/config";
import * as GQL from "src/core/generated-graphql";
import {
useConfiguration,
@@ -16,6 +17,7 @@ import {
useConfigureGeneral,
useConfigureInterface,
useConfigureScraping,
useConfigureUI,
} from "src/core/StashService";
import { useToast } from "src/hooks";
import { withoutTypename } from "src/utils";
@@ -29,6 +31,7 @@ export interface ISettingsContextState {
defaults: GQL.ConfigDefaultSettingsInput;
scraping: GQL.ConfigScrapingInput;
dlna: GQL.ConfigDlnaInput;
ui: IUIConfig;
// apikey isn't directly settable, so expose it here
apiKey: string;
@@ -38,6 +41,7 @@ export interface ISettingsContextState {
saveDefaults: (input: Partial<GQL.ConfigDefaultSettingsInput>) => void;
saveScraping: (input: Partial<GQL.ConfigScrapingInput>) => void;
saveDLNA: (input: Partial<GQL.ConfigDlnaInput>) => void;
saveUI: (input: IUIConfig) => void;
}
export const SettingStateContext = React.createContext<ISettingsContextState>({
@@ -48,12 +52,14 @@ export const SettingStateContext = React.createContext<ISettingsContextState>({
defaults: {},
scraping: {},
dlna: {},
ui: {},
apiKey: "",
saveGeneral: () => {},
saveInterface: () => {},
saveDefaults: () => {},
saveScraping: () => {},
saveDLNA: () => {},
saveUI: () => {},
});
export const SettingsContext: React.FC = ({ children }) => {
@@ -92,6 +98,10 @@ export const SettingsContext: React.FC = ({ children }) => {
>();
const [updateDLNAConfig] = useConfigureDLNA();
const [ui, setUI] = useState({});
const [pendingUI, setPendingUI] = useState<{} | undefined>();
const [updateUIConfig] = useConfigureUI();
const [updateSuccess, setUpdateSuccess] = useState<boolean | undefined>();
const [apiKey, setApiKey] = useState("");
@@ -121,6 +131,7 @@ export const SettingsContext: React.FC = ({ children }) => {
setDefaults({ ...withoutTypename(data.configuration.defaults) });
setScraping({ ...withoutTypename(data.configuration.scraping) });
setDLNA({ ...withoutTypename(data.configuration.dlna) });
setUI({ ...withoutTypename(data.configuration.ui) });
setApiKey(data.configuration.general.apiKey);
}, [data, error]);
@@ -387,6 +398,56 @@ export const SettingsContext: React.FC = ({ children }) => {
});
}
// saves the configuration if no further changes are made after a half second
const saveUIConfig = useMemo(
() =>
debounce(async (input: IUIConfig) => {
try {
setUpdateSuccess(undefined);
await updateUIConfig({
variables: {
input,
},
});
setPendingUI(undefined);
onSuccess();
} catch (e) {
setSaveError(e);
}
}, 500),
[updateUIConfig, onSuccess]
);
useEffect(() => {
if (!pendingUI) {
return;
}
saveUIConfig(pendingUI);
}, [pendingUI, saveUIConfig]);
function saveUI(input: IUIConfig) {
if (!ui) {
return;
}
setUI({
...ui,
...input,
});
setPendingUI((current) => {
if (!current) {
return input;
}
return {
...current,
...input,
};
});
}
function maybeRenderLoadingIndicator() {
if (updateSuccess === false) {
return (
@@ -401,7 +462,8 @@ export const SettingsContext: React.FC = ({ children }) => {
pendingInterface ||
pendingDefaults ||
pendingScraping ||
pendingDLNA
pendingDLNA ||
pendingUI
) {
return (
<div className="loading-indicator">
@@ -432,11 +494,13 @@ export const SettingsContext: React.FC = ({ children }) => {
defaults,
scraping,
dlna,
ui,
saveGeneral,
saveInterface,
saveDefaults,
saveScraping,
saveDLNA,
saveUI,
}}
>
{maybeRenderLoadingIndicator()}