This commit is contained in:
2025-07-22 16:54:15 +02:00
parent 476762a30f
commit 8d6ef2388c
6 changed files with 165 additions and 25 deletions

View File

@@ -0,0 +1,35 @@
import {ReactNode, useContext, useState} from "react";
import {SettingsContext, SettingsItem} from "./Settings.tsx";
import {ApiContext} from "../../apiClient/ApiContext.tsx";
import {ColorPaletteProp, Input} from "@mui/joy";
import * as React from "react";
import MarkdownPreview from "@uiw/react-markdown-preview";
export default function () : ReactNode {
const settings = useContext(SettingsContext);
const Api = useContext(ApiContext);
const [scheme, setScheme] = useState<ColorPaletteProp>("neutral");
const timerRef = React.useRef<ReturnType<typeof setTimeout>>(undefined);
const schemeChanged = (e : React.ChangeEvent<HTMLInputElement>) => {
clearTimeout(timerRef.current);
setScheme("warning");
timerRef.current = setTimeout(() => {
Api.settingsChapterNamingSchemePartialUpdate(e.target.value)
.then(response => {
if (response.ok)
setScheme("success");
else
setScheme("danger");
})
.catch(() => setScheme("danger"));
}, 1000);
}
return (
<SettingsItem title={"Chapter Naming Scheme"}>
<MarkdownPreview style={{backgroundColor: "transparent"}} source={"Placeholders:\n * %M Obj Name\n * %V Volume\n * %C Chapter\n * %T Title\n * %A Author (first in list)\n * %I Chapter Internal ID\n * %i Obj Internal ID\n * %Y Year (Obj)\n *\n * ?_(...) replace _ with a value from above:\n * Everything inside the braces will only be added if the value of %_ is not null"} />
<Input color={scheme} value={settings?.chapterNamingScheme as string} placeholder={"Scheme"} onChange={schemeChanged} />
</SettingsItem>
);
}

View File

@@ -0,0 +1,33 @@
import {ReactNode, useContext, useState} from "react";
import {SettingsContext, SettingsItem} from "./Settings.tsx";
import {ApiContext} from "../../apiClient/ApiContext.tsx";
import {ColorPaletteProp, Input} from "@mui/joy";
import * as React from "react";
export default function () : ReactNode {
const settings = useContext(SettingsContext);
const Api = useContext(ApiContext);
const [color, setColor] = useState<ColorPaletteProp>("neutral");
const timerRef = React.useRef<ReturnType<typeof setTimeout>>(undefined);
const languageChanged = (e : React.ChangeEvent<HTMLInputElement>) => {
clearTimeout(timerRef.current);
setColor("warning");
timerRef.current = setTimeout(() => {
Api.settingsDownloadLanguagePartialUpdate(e.target.value)
.then(response => {
if (response.ok)
setColor("success");
else
setColor("danger");
})
.catch(() => setColor("danger"));
}, 1000);
}
return (
<SettingsItem title={"Download Language"}>
<Input color={color} value={settings?.downloadLanguage as string} placeholder={"Language code (f.e. 'en')"} onChange={languageChanged} />
</SettingsItem>
);
}

View File

@@ -0,0 +1,33 @@
import {ReactNode, useContext, useState} from "react";
import {SettingsContext, SettingsItem} from "./Settings.tsx";
import {ColorPaletteProp, Input} from "@mui/joy";
import * as React from "react";
import {ApiContext} from "../../apiClient/ApiContext.tsx";
export default function () : ReactNode {
const settings = useContext(SettingsContext);
const Api = useContext(ApiContext);
const [uriColor, setUriColor] = useState<ColorPaletteProp>("neutral");
const timerRef = React.useRef<ReturnType<typeof setTimeout>>(undefined);
const uriChanged = (e : React.ChangeEvent<HTMLInputElement>) => {
clearTimeout(timerRef.current);
setUriColor("warning");
timerRef.current = setTimeout(() => {
Api.settingsFlareSolverrUrlCreate(e.target.value)
.then(response => {
if (response.ok)
setUriColor("success");
else
setUriColor("danger");
})
.catch(() => setUriColor("danger"));
}, 1000);
}
return (
<SettingsItem title={"FlareSolverr"}>
<Input color={uriColor} value={settings?.flareSolverrUrl as string} type={"url"} placeholder={"URL"} onChange={uriChanged} />
</SettingsItem>
);
}

View File

@@ -0,0 +1,13 @@
import {ReactNode, useContext} from "react";
import {SettingsContext, SettingsItem} from "./Settings.tsx";
import {Slider} from "@mui/joy";
export default function () : ReactNode {
const settings = useContext(SettingsContext);
return (
<SettingsItem title={"Image Compression"}>
<Slider sx={{marginTop: "20px"}} valueLabelDisplay={"auto"} defaultValue={settings?.imageCompression}></Slider>
</SettingsItem>
);
}

View File

@@ -0,0 +1,16 @@
import {ReactNode} from "react";
import {SettingsItem} from "./Settings.tsx";
import {Button} from "@mui/joy";
import NotificationConnectors from "./AddNotificationConnector.tsx";
import * as React from "react";
export default function () : ReactNode {
const [notificationConnectorsOpen, setNotificationConnectorsOpen] = React.useState(false);
return (
<SettingsItem title={"Notification Connectors"}>
<Button onClick={() => setNotificationConnectorsOpen(true)}>Add Notification Connector</Button>
<NotificationConnectors open={notificationConnectorsOpen} setOpen={setNotificationConnectorsOpen} />
</SettingsItem>
);
}

View File

@@ -10,28 +10,31 @@ import {
} from "@mui/joy";
import './Settings.css';
import * as React from "react";
import {createContext, Dispatch, useContext, useEffect, useState} from "react";
import {createContext, Dispatch, ReactNode, useContext, useEffect, useState} from "react";
import {Article} from '@mui/icons-material';
import {TrangaSettings} from "../../apiClient/data-contracts.ts";
import {ApiContext} from "../../apiClient/ApiContext.tsx";
import NotificationConnectors from "./AddNotificationConnector.tsx";
import NotificationConnectors from "./NotificationConnectors.tsx";
import {SxProps} from "@mui/joy/styles/types";
import ImageCompression from "./ImageCompression.tsx";
import FlareSolverr from "./FlareSolverr.tsx";
import DownloadLanguage from "./DownloadLanguage.tsx";
import ChapterNamingScheme from "./ChapterNamingScheme.tsx";
export const SettingsContext = createContext<TrangaSettings>({});
export const SettingsContext = createContext<TrangaSettings|undefined>(undefined);
export default function Settings({setApiUri} : {setApiUri: Dispatch<React.SetStateAction<string>>}) {
const Api = useContext(ApiContext);
const [settings, setSettings] = useState<TrangaSettings>({});
const [settings, setSettings] = useState<TrangaSettings>();
const [open, setOpen] = React.useState(false);
const [apiUriColor, setApiUriColor] = useState<ColorPaletteProp>("neutral");
const timerRef = React.useRef<ReturnType<typeof setTimeout>>(undefined);
const [apiUriAccordionOpen, setApiUriAccordionOpen] = React.useState(true);
useEffect(() => {
Api.settingsList().then((response) => {
setSettings(response.data)
setSettings(response.data);
});
}, []);
@@ -44,15 +47,13 @@ export default function Settings({setApiUri} : {setApiUri: Dispatch<React.SetSta
}, 1000);
}
const [notificationConnectorsOpen, setNotificationConnectorsOpen] = React.useState(false);
const ModalStyle : SxProps = {
width: "80%",
height: "80%"
}
return (
<SettingsContext value={settings}>
<SettingsContext.Provider value={settings}>
<Button onClick={() => setOpen(true)}>Settings</Button>
<Modal open={open} onClose={() => setOpen(false)}>
<ModalDialog sx={ModalStyle}>
@@ -60,21 +61,19 @@ export default function Settings({setApiUri} : {setApiUri: Dispatch<React.SetSta
<DialogTitle>Settings</DialogTitle>
<DialogContent>
<AccordionGroup>
<Accordion expanded={apiUriAccordionOpen} onChange={(_e, expanded) => setApiUriAccordionOpen(expanded)}>
<AccordionSummary>ApiUri</AccordionSummary>
<AccordionDetails>
<Input
color={apiUriColor}
placeholder={"http(s)://"}
type={"url"}
defaultValue={Api.baseUrl}
onChange={apiUriChanged} />
</AccordionDetails>
</Accordion>
<Accordion>
<Button onClick={() => setNotificationConnectorsOpen(true)}>Add Notification Connector</Button>
<NotificationConnectors open={notificationConnectorsOpen} setOpen={setNotificationConnectorsOpen} />
</Accordion>
<SettingsItem title={"ApiUri"}>
<Input
color={apiUriColor}
placeholder={"http(s)://"}
type={"url"}
defaultValue={Api.baseUrl}
onChange={apiUriChanged} />
</SettingsItem>
<ImageCompression />
<FlareSolverr />
<DownloadLanguage />
<ChapterNamingScheme />
<NotificationConnectors />
</AccordionGroup>
<Stack spacing={2} direction="row">
<Link target={"_blank"} href={Api.baseUrl + "/swagger"}><Article />Swagger Doc</Link>
@@ -82,6 +81,17 @@ export default function Settings({setApiUri} : {setApiUri: Dispatch<React.SetSta
</DialogContent>
</ModalDialog>
</Modal>
</SettingsContext>
</SettingsContext.Provider>
);
}
export function SettingsItem({title, children} : {title: string, children: ReactNode}) {
return (
<Accordion>
<AccordionSummary>{title}</AccordionSummary>
<AccordionDetails>
{children}
</AccordionDetails>
</Accordion>
);
}