mirror of
https://github.com/C9Glax/tranga-website.git
synced 2025-05-07 15:42:10 +02:00
Compare commits
10 Commits
6d402357e7
...
10f48af9fa
Author | SHA1 | Date | |
---|---|---|---|
10f48af9fa | |||
5e95c34306 | |||
66dcb2f1e6 | |||
19e033995d | |||
99265bacb2 | |||
5385dfd918 | |||
d6e4d1d27f | |||
288cd77049 | |||
e605578a34 | |||
6340c5ad03 |
@ -2,7 +2,7 @@ import Sheet from '@mui/joy/Sheet';
|
||||
import './App.css'
|
||||
import Settings from "./Settings.tsx";
|
||||
import Header from "./Header.tsx";
|
||||
import {Badge, Button} from "@mui/joy";
|
||||
import {Badge, Box, Button, Card, CardContent, CardCover, Typography} from "@mui/joy";
|
||||
import {useState} from "react";
|
||||
import {ApiUriContext} from "./api/fetchApi.tsx";
|
||||
import Search from './Components/Search.tsx';
|
||||
@ -23,12 +23,29 @@ export default function App () {
|
||||
<Badge color={"danger"} invisible={apiConnected} badgeContent={"!"}>
|
||||
<Button onClick={() => setShowSettings(true)}>Settings</Button>
|
||||
</Badge>
|
||||
<Button onClick={() => setShowSearch(true)}>Search</Button>
|
||||
</Header>
|
||||
<Settings open={showSettings} setOpen={setShowSettings} setApiUri={setApiUri} setConnected={setApiConnected} />
|
||||
<Search open={showSearch} setOpen={setShowSearch} />
|
||||
<Sheet className={"app-content"}>
|
||||
<MangaList />
|
||||
<MangaList connected={apiConnected}>
|
||||
<Badge invisible>
|
||||
<Card onClick={() => setShowSearch(true)} sx={{height:"fit-content",width:"fit-content"}}>
|
||||
<CardCover sx={{margin:"var(--Card-padding)"}}>
|
||||
<img src={"/blahaj.png"} style={{height:"400px", width:"300px"}} />
|
||||
</CardCover>
|
||||
<CardCover sx={{
|
||||
background: 'rgba(234, 119, 246, 0.14)',
|
||||
backdropFilter: 'blur(6.9px)',
|
||||
webkitBackdropFilter: 'blur(6.9px)',
|
||||
}}/>
|
||||
<CardContent>
|
||||
<Box style={{height:"400px", width:"300px"}} >
|
||||
<Typography level={"h1"}>Search</Typography>
|
||||
</Box>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Badge>
|
||||
</MangaList>
|
||||
</Sheet>
|
||||
</Sheet>
|
||||
</ApiUriContext.Provider>
|
||||
|
@ -16,9 +16,9 @@ export default function LinkTag({linkId, color} : { linkId: string | undefined,
|
||||
}, [linkId]);
|
||||
|
||||
return (
|
||||
<Chip variant={"outlined"} size={"md"} color={color??"primary"}>
|
||||
<Chip variant={"soft"} size={"sm"} color={color??"primary"}>
|
||||
<Skeleton variant={"text"} loading={loading}>
|
||||
<Link href={link?.linkUrl}>{link?.linkProvider??"Load Failed"}</Link>
|
||||
<Link sx={{textDecoration:"underline"}} level={"body-xs"} href={link?.linkUrl}>{link?.linkProvider??"Load Failed"}</Link>
|
||||
</Skeleton>
|
||||
</Chip>
|
||||
);
|
||||
|
@ -108,14 +108,14 @@ export function Manga({manga, children, loading} : { manga: IManga | undefined,
|
||||
expanded ?
|
||||
<Box sx={sideSx}>
|
||||
<Skeleton loading={loading} variant={"text"} level={"title-lg"}>
|
||||
<Stack direction={"row"} flexWrap={"wrap"} spacing={0.5}>
|
||||
<Stack direction={"row"} flexWrap={"wrap"} spacing={0.5} sx={{maxHeight:"75px", overflowY:"auto", scrollbarWidth: "thin"}}>
|
||||
{useManga.authorIds.map(authorId => <AuthorTag key={authorId} authorId={authorId} color={"success"} />)}
|
||||
{useManga.tags.map(tag => <Chip key={tag} variant={"outlined"} size={"md"} color={"primary"}>{tag}</Chip>)}
|
||||
{useManga.linkIds.map(linkId => <LinkTag key={linkId} linkId={linkId} color={"danger"} />)}
|
||||
{useManga.tags.map(tag => <Chip key={tag} variant={"soft"} size={"md"} color={"primary"}>{tag}</Chip>)}
|
||||
{useManga.linkIds.map(linkId => <LinkTag key={linkId} linkId={linkId} color={"warning"} />)}
|
||||
</Stack>
|
||||
</Skeleton>
|
||||
<Skeleton loading={loading} sx={{maxHeight:"300px"}}>
|
||||
<MarkdownPreview source={useManga.description} style={{backgroundColor: "transparent", color: "black"}} />
|
||||
<MarkdownPreview source={useManga.description} style={{backgroundColor: "transparent", color: "black", maxHeight:"310px", overflowY:"auto", marginTop:"10px", scrollbarWidth: "thin"}} />
|
||||
</Skeleton>
|
||||
</Box>
|
||||
: null
|
||||
|
@ -6,13 +6,16 @@ import {JobType} from "../api/types/Jobs/IJob.ts";
|
||||
import IDownloadAvailableChaptersJob from "../api/types/Jobs/IDownloadAvailableChaptersJob.ts";
|
||||
import {MangaFromId} from "./Manga.tsx";
|
||||
import { Remove } from "@mui/icons-material";
|
||||
import * as React from "react";
|
||||
|
||||
export default function MangaList(){
|
||||
export default function MangaList({connected, children}: {connected: boolean, children?: React.ReactNode} ){
|
||||
const apiUri = useContext(ApiUriContext);
|
||||
|
||||
const [jobList, setJobList] = useState<IDownloadAvailableChaptersJob[]>([]);
|
||||
|
||||
const getJobList = useCallback(() => {
|
||||
if(!connected)
|
||||
return;
|
||||
GetJobsWithType(apiUri, JobType.DownloadAvailableChaptersJob).then((jl) => setJobList(jl as IDownloadAvailableChaptersJob[]));
|
||||
},[apiUri]);
|
||||
|
||||
@ -24,8 +27,21 @@ export default function MangaList(){
|
||||
getJobList();
|
||||
}, [apiUri]);
|
||||
|
||||
const timerRef = React.useRef<ReturnType<typeof setInterval>>(undefined);
|
||||
useEffect(() => {
|
||||
if(!connected){
|
||||
clearTimeout(timerRef.current);
|
||||
return;
|
||||
}else{
|
||||
timerRef.current = setInterval(() => {
|
||||
getJobList();
|
||||
}, 2000);
|
||||
}
|
||||
}, [connected,]);
|
||||
|
||||
return(
|
||||
<Stack direction="row" spacing={1}>
|
||||
{children}
|
||||
{jobList.map((job) => (
|
||||
<MangaFromId key={job.mangaId} mangaId={job.mangaId}>
|
||||
<Button color={"danger"} endDecorator={<Remove />} onClick={() => deleteJob(job.jobId)}>Delete</Button>
|
||||
|
@ -48,6 +48,7 @@ export default function Search({open, setOpen}:{open:boolean, setOpen:React.Disp
|
||||
const [resultsLoading, setResultsLoading] = useState<boolean>(false);
|
||||
|
||||
const StartSearch = useCallback((mangaConnector : IMangaConnector | undefined, value: string)=>{
|
||||
setStep(3);
|
||||
if(mangaConnector === undefined)
|
||||
return;
|
||||
setResults([]);
|
||||
@ -92,19 +93,21 @@ export default function Search({open, setOpen}:{open:boolean, setOpen:React.Disp
|
||||
// @ts-ignore
|
||||
return (
|
||||
<Drawer size={"lg"} anchor={"right"} open={open} onClose={() => {
|
||||
setStep(2);
|
||||
if(step > 2)
|
||||
setStep(2);
|
||||
setResults([]);
|
||||
setOpen(false);
|
||||
}}>
|
||||
<ModalClose />
|
||||
<Stepper orientation={"vertical"} sx={{ height: '100%', width: "calc(100% - 80px)", margin:"40px"}}>
|
||||
<Step indicator={
|
||||
<StepIndicator variant="solid" color="primary">
|
||||
<StepIndicator variant={step==1?"solid":"outlined"} color={mangaConnectors.length < 1 ? "danger" : "primary"}>
|
||||
1
|
||||
</StepIndicator>}>
|
||||
<Skeleton loading={mangaConnectorsLoading}>
|
||||
<Select
|
||||
disabled={mangaConnectorsLoading || resultsLoading}
|
||||
color={mangaConnectors.length < 1 ? "danger" : "neutral"}
|
||||
disabled={mangaConnectorsLoading || resultsLoading || mangaConnectors.length < 1}
|
||||
placeholder={"Select Connector"}
|
||||
slotProps={{
|
||||
listbox: {
|
||||
@ -119,13 +122,13 @@ export default function Search({open, setOpen}:{open:boolean, setOpen:React.Disp
|
||||
setStep(2);
|
||||
setSelectedMangaConnector(mangaConnectors.find((o) => o.name === newValue));
|
||||
}}
|
||||
endDecorator={<Chip size={"sm"} color={"primary"}>{mangaConnectors.length}</Chip>}>
|
||||
endDecorator={<Chip size={"sm"} color={mangaConnectors.length < 1 ? "danger" : "primary"}>{mangaConnectors.length}</Chip>}>
|
||||
{mangaConnectors?.map((connector: IMangaConnector) => ConnectorOption(connector))}
|
||||
</Select>
|
||||
</Skeleton>
|
||||
</Step>
|
||||
<Step indicator={
|
||||
<StepIndicator variant="solid" color="primary">
|
||||
<StepIndicator variant={step==2?"solid":"outlined"} color="primary">
|
||||
2
|
||||
</StepIndicator>}>
|
||||
<Input disabled={step < 2 || resultsLoading} placeholder={"Name or Url " + (selectedMangaConnector ? selectedMangaConnector.baseUris[0] : "")} onKeyDown={(e) => {
|
||||
@ -137,7 +140,7 @@ export default function Search({open, setOpen}:{open:boolean, setOpen:React.Disp
|
||||
}}/>
|
||||
</Step>
|
||||
<Step indicator={
|
||||
<StepIndicator variant="solid" color="primary">
|
||||
<StepIndicator variant={step==3?"solid":"outlined"} color="primary">
|
||||
3
|
||||
</StepIndicator>}>
|
||||
<Typography endDecorator={<Chip size={"sm"} color={"primary"}>{results.length}</Chip>}>Results</Typography>
|
||||
|
53
tranga-website/src/Components/Settings/AprilFoolsMode.tsx
Normal file
53
tranga-website/src/Components/Settings/AprilFoolsMode.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import IBackendSettings from "../../api/types/IBackendSettings.ts";
|
||||
import {useCallback, useContext, useState} from "react";
|
||||
import {ApiUriContext} from "../../api/fetchApi.tsx";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionDetails,
|
||||
AccordionSummary,
|
||||
ColorPaletteProp,
|
||||
Switch,
|
||||
Typography
|
||||
} from "@mui/joy";
|
||||
import * as React from "react";
|
||||
import {UpdateAprilFoolsToggle} from "../../api/BackendSettings.tsx";
|
||||
|
||||
export default function ImageProcessing({backendSettings}: {backendSettings?: IBackendSettings}) {
|
||||
const apiUri = useContext(ApiUriContext);
|
||||
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [color, setColor] = useState<ColorPaletteProp>("neutral");
|
||||
|
||||
const timerRef = React.useRef<ReturnType<typeof setTimeout>>(undefined);
|
||||
const valueChanged = (e : React.ChangeEvent<HTMLInputElement>) => {
|
||||
setColor("warning");
|
||||
clearTimeout(timerRef.current);
|
||||
console.log(e);
|
||||
timerRef.current = setTimeout(() => {
|
||||
UpdateAprilFoolsMode(e.target.checked);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
const UpdateAprilFoolsMode = useCallback((value: boolean) => {
|
||||
UpdateAprilFoolsToggle(apiUri, value)
|
||||
.then(() => setColor("success"))
|
||||
.catch(() => setColor("danger"))
|
||||
.finally(() => setLoading(false));
|
||||
}, [apiUri]);
|
||||
|
||||
return (
|
||||
<Accordion>
|
||||
<AccordionSummary>April Fools Mode</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Typography endDecorator={
|
||||
<Switch disabled={backendSettings === undefined || loading}
|
||||
onChange={valueChanged}
|
||||
color={color}
|
||||
defaultChecked={backendSettings?.aprilFoolsMode} />
|
||||
}>
|
||||
Toggle
|
||||
</Typography>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
import IBackendSettings from "../../api/types/IBackendSettings";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionDetails,
|
||||
AccordionSummary, Chip,
|
||||
CircularProgress,
|
||||
ColorPaletteProp,
|
||||
Divider,
|
||||
Input,
|
||||
Stack, Tooltip, Typography
|
||||
} from "@mui/joy";
|
||||
import {KeyboardEventHandler, useCallback, useContext, useState} from "react";
|
||||
import {ApiUriContext} from "../../api/fetchApi.tsx";
|
||||
import {UpdateChapterNamingScheme} from "../../api/BackendSettings.tsx";
|
||||
|
||||
export default function ChapterNamingScheme({backendSettings}: {backendSettings?: IBackendSettings}) {
|
||||
const apiUri = useContext(ApiUriContext);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [value, setValue] = useState<string>("");
|
||||
const [color, setColor] = useState<ColorPaletteProp>("neutral");
|
||||
|
||||
const keyDown : KeyboardEventHandler<HTMLInputElement> = useCallback((e) => {
|
||||
if(e.key === "Enter") {
|
||||
setLoading(true);
|
||||
UpdateChapterNamingScheme(apiUri, value)
|
||||
.then(() => setColor("success"))
|
||||
.catch(() => setColor("danger"))
|
||||
.finally(() => setLoading(false));
|
||||
}
|
||||
}, [apiUri])
|
||||
|
||||
return (
|
||||
<Accordion>
|
||||
<AccordionSummary>Chapter Naming Scheme</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Input disabled={backendSettings === undefined || loading}
|
||||
placeholder={"Chapter Naming Scheme"}
|
||||
defaultValue={backendSettings?.chapterNamingScheme}
|
||||
onKeyDown={keyDown}
|
||||
onChange={e => setValue(e.target.value)}
|
||||
color={color}
|
||||
endDecorator={(loading ? <CircularProgress color={"primary"} size={"sm"} /> : null)}
|
||||
/>
|
||||
<Typography level={"title-sm"}>Placeholders:</Typography>
|
||||
<Stack direction="row" spacing={1} divider={<Divider />}>
|
||||
<Tooltip arrow placement="bottom" size="md" variant="outlined"
|
||||
title={"Manga Title"} >
|
||||
<Chip color={"primary"}>%M</Chip>
|
||||
</Tooltip>
|
||||
<Tooltip arrow placement="bottom" size="md" variant="outlined"
|
||||
title={"Volume Number"} >
|
||||
<Chip color={"primary"}>%V</Chip>
|
||||
</Tooltip>
|
||||
<Tooltip arrow placement="bottom" size="md" variant="outlined"
|
||||
title={"Chapter Number"} >
|
||||
<Chip color={"primary"}>%C</Chip>
|
||||
</Tooltip>
|
||||
<Tooltip arrow placement="bottom" size="md" variant="outlined"
|
||||
title={"Chapter Title"} >
|
||||
<Chip color={"primary"}>%T</Chip>
|
||||
</Tooltip>
|
||||
<Tooltip arrow placement="bottom" size="md" variant="outlined"
|
||||
title={"Year"} >
|
||||
<Chip color={"primary"}>%Y</Chip>
|
||||
</Tooltip>
|
||||
<Tooltip arrow placement="bottom" size="md" variant="outlined"
|
||||
title={"First Author"} >
|
||||
<Chip color={"primary"}>%A</Chip>
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
}
|
114
tranga-website/src/Components/Settings/ImageProcessing.tsx
Normal file
114
tranga-website/src/Components/Settings/ImageProcessing.tsx
Normal file
@ -0,0 +1,114 @@
|
||||
import IBackendSettings from "../../api/types/IBackendSettings.ts";
|
||||
import {useCallback, useContext, useState} from "react";
|
||||
import {ApiUriContext} from "../../api/fetchApi.tsx";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionDetails,
|
||||
AccordionSummary,
|
||||
ColorPaletteProp,
|
||||
Input,
|
||||
Switch,
|
||||
Typography
|
||||
} from "@mui/joy";
|
||||
import * as React from "react";
|
||||
import {UpdateBWImageToggle, UpdateImageCompressionValue} from "../../api/BackendSettings.tsx";
|
||||
|
||||
export default function ImageProcessing({backendSettings}: {backendSettings?: IBackendSettings}) {
|
||||
const apiUri = useContext(ApiUriContext);
|
||||
|
||||
const [loadingBw, setLoadingBw] = useState<boolean>(false);
|
||||
const [bwInputColor, setBwInputcolor] = useState<ColorPaletteProp>("neutral");
|
||||
|
||||
const timerRefBw = React.useRef<ReturnType<typeof setTimeout>>(undefined);
|
||||
const bwChanged = (e : React.ChangeEvent<HTMLInputElement>) => {
|
||||
setBwInputcolor("warning");
|
||||
clearTimeout(timerRefBw.current);
|
||||
console.log(e);
|
||||
timerRefBw.current = setTimeout(() => {
|
||||
UpdateBw(e.target.checked);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
const UpdateBw = useCallback((value: boolean) => {
|
||||
UpdateBWImageToggle(apiUri, value)
|
||||
.then(() => setBwInputcolor("success"))
|
||||
.catch(() => setBwInputcolor("danger"))
|
||||
.finally(() => setLoadingBw(false));
|
||||
}, [apiUri]);
|
||||
|
||||
const [loadingCompression, setLoadingCompression] = useState<boolean>(false);
|
||||
const [compressionInputColor, setCompressionInputColor] = useState<ColorPaletteProp>("neutral");
|
||||
const [compressionEnabled, setCompressionEnabled] = useState<boolean>((backendSettings?.compression??100) < 100);
|
||||
const [compressionValue, setCompressionValue] = useState<number|undefined>(backendSettings?.compression);
|
||||
|
||||
const timerRefCompression = React.useRef<ReturnType<typeof setTimeout>>(undefined);
|
||||
const compressionLevelChanged = (e : React.ChangeEvent<HTMLInputElement>) => {
|
||||
setCompressionInputColor("warning");
|
||||
setCompressionValue(Number.parseInt(e.target.value));
|
||||
clearTimeout(timerRefCompression.current);
|
||||
|
||||
console.log(e);
|
||||
timerRefCompression.current = setTimeout(() => {
|
||||
UpdateCompressionLevel(Number.parseInt(e.target.value));
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
const compressionEnableChanged = (e : React.ChangeEvent<HTMLInputElement>) => {
|
||||
setCompressionInputColor("warning");
|
||||
setCompressionEnabled(e.target.checked);
|
||||
clearTimeout(timerRefCompression.current);
|
||||
timerRefCompression.current = setTimeout(() => {
|
||||
UpdateCompressionLevel(e.target.checked ? compressionValue! : 100);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
const UpdateCompressionLevel = useCallback((value: number)=> {
|
||||
setLoadingCompression(true);
|
||||
UpdateImageCompressionValue(apiUri, value)
|
||||
.then(() => {
|
||||
setCompressionInputColor("success");
|
||||
setCompressionValue(value);
|
||||
})
|
||||
.catch(() => setCompressionInputColor("danger"))
|
||||
.finally(() => setLoadingCompression(false));
|
||||
}, [apiUri]);
|
||||
|
||||
return (
|
||||
<Accordion>
|
||||
<AccordionSummary>Image Processing</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Typography endDecorator={
|
||||
<Switch disabled={backendSettings === undefined || loadingBw}
|
||||
onChange={bwChanged}
|
||||
color={bwInputColor}
|
||||
defaultChecked={backendSettings?.bwImages} />
|
||||
}>
|
||||
Black and White Images
|
||||
</Typography>
|
||||
<Typography endDecorator={
|
||||
<Switch disabled={backendSettings === undefined || loadingCompression}
|
||||
onChange={compressionEnableChanged}
|
||||
color={compressionInputColor}
|
||||
defaultChecked={compressionEnabled} endDecorator={
|
||||
<Input
|
||||
defaultValue={backendSettings?.compression}
|
||||
disabled={!compressionEnabled || loadingCompression}
|
||||
onChange={compressionLevelChanged}
|
||||
color={compressionInputColor}
|
||||
onKeyDown={(e) => {
|
||||
if(e.key === "Enter") {
|
||||
clearTimeout(timerRefCompression.current);
|
||||
// @ts-ignore
|
||||
UpdateCompressionLevel(Number.parseInt(e.target.value));
|
||||
}
|
||||
}}
|
||||
sx={{width:"70px"}}
|
||||
/>
|
||||
} />
|
||||
}>
|
||||
Image Compression
|
||||
</Typography>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
}
|
81
tranga-website/src/Components/Settings/RequestLimits.tsx
Normal file
81
tranga-website/src/Components/Settings/RequestLimits.tsx
Normal file
@ -0,0 +1,81 @@
|
||||
import IBackendSettings from "../../api/types/IBackendSettings.ts";
|
||||
import {useCallback, useContext, useState} from "react";
|
||||
import {ApiUriContext} from "../../api/fetchApi.tsx";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionDetails,
|
||||
AccordionSummary,
|
||||
Button,
|
||||
ColorPaletteProp,
|
||||
Input,
|
||||
Stack,
|
||||
Typography
|
||||
} from "@mui/joy";
|
||||
import {RequestLimitType} from "../../api/types/EnumRequestLimitType.ts";
|
||||
import {ResetRequestLimit, ResetRequestLimits, UpdateRequestLimit} from "../../api/BackendSettings.tsx";
|
||||
import {Restore} from "@mui/icons-material";
|
||||
|
||||
export default function RequestLimits({backendSettings}: {backendSettings?: IBackendSettings}) {
|
||||
const apiUri = useContext(ApiUriContext);
|
||||
|
||||
const [color, setColor] = useState<ColorPaletteProp>("neutral");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const Update = useCallback((target: HTMLInputElement, limit: RequestLimitType) => {
|
||||
setLoading(true);
|
||||
UpdateRequestLimit(apiUri, limit, Number.parseInt(target.value))
|
||||
.then(() => setColor("success"))
|
||||
.catch(() => setColor("danger"))
|
||||
.finally(() => setLoading(false));
|
||||
},[apiUri])
|
||||
|
||||
const Reset = useCallback((limit: RequestLimitType) => {
|
||||
setLoading(true);
|
||||
ResetRequestLimit(apiUri, limit)
|
||||
.then(() => setColor("success"))
|
||||
.catch(() => setColor("danger"))
|
||||
.finally(() => setLoading(false));
|
||||
}, [apiUri]);
|
||||
|
||||
const ResetAll = useCallback(() => {
|
||||
setLoading(true);
|
||||
ResetRequestLimits(apiUri)
|
||||
.then(() => setColor("success"))
|
||||
.catch(() => setColor("danger"))
|
||||
.finally(() => setLoading(false));
|
||||
}, [apiUri]);
|
||||
|
||||
return (
|
||||
<Accordion>
|
||||
<AccordionSummary>Request Limits</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Stack spacing={1} direction="column">
|
||||
<Button loading={backendSettings === undefined} onClick={ResetAll} size={"sm"} variant={"outlined"} endDecorator={<Restore />} color={"warning"}>Reset all</Button>
|
||||
<Item type={RequestLimitType.Default} color={color} backendSettings={backendSettings} loading={loading} Reset={Reset} Update={Update} />
|
||||
<Item type={RequestLimitType.MangaInfo} color={color} backendSettings={backendSettings} loading={loading} Reset={Reset} Update={Update} />
|
||||
<Item type={RequestLimitType.MangaImage} color={color} backendSettings={backendSettings} loading={loading} Reset={Reset} Update={Update} />
|
||||
<Item type={RequestLimitType.MangaDexFeed} color={color} backendSettings={backendSettings} loading={loading} Reset={Reset} Update={Update} />
|
||||
<Item type={RequestLimitType.MangaDexImage} color={color} backendSettings={backendSettings} loading={loading} Reset={Reset} Update={Update} />
|
||||
</Stack>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
}
|
||||
|
||||
function Item({type, color, loading, backendSettings, Reset, Update}:
|
||||
{type: RequestLimitType, color: ColorPaletteProp, loading: boolean, backendSettings: IBackendSettings | undefined, Reset: (x: RequestLimitType) => void, Update: (a: HTMLInputElement, x: RequestLimitType) => void}) {
|
||||
return (
|
||||
<Input slotProps={{input: {min: 0, max: 360}}}
|
||||
color={color}
|
||||
startDecorator={<Typography sx={{width:"140px"}}>{type}</Typography>}
|
||||
endDecorator={<Button onClick={() => Reset(type)}>Reset</Button>}
|
||||
disabled={loading} type={"number"}
|
||||
defaultValue={backendSettings?.requestLimits[type]}
|
||||
placeholder={"Default"}
|
||||
required
|
||||
onKeyDown={(e) => {
|
||||
if(e.key == "Enter")
|
||||
Update(e.target as HTMLInputElement, type);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,8 +1,15 @@
|
||||
import IBackendSettings from "../../api/types/IBackendSettings";
|
||||
import {Accordion, AccordionDetails, AccordionSummary, CircularProgress, ColorPaletteProp, Input} from "@mui/joy";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionDetails,
|
||||
AccordionSummary,
|
||||
Button,
|
||||
ColorPaletteProp,
|
||||
Input
|
||||
} from "@mui/joy";
|
||||
import {KeyboardEventHandler, useCallback, useContext, useState} from "react";
|
||||
import {ApiUriContext} from "../../api/fetchApi.tsx";
|
||||
import {UpdateUserAgent} from "../../api/BackendSettings.tsx";
|
||||
import {ResetUserAgent, UpdateUserAgent} from "../../api/BackendSettings.tsx";
|
||||
|
||||
export default function UserAgent({backendSettings}: {backendSettings?: IBackendSettings}) {
|
||||
const apiUri = useContext(ApiUriContext);
|
||||
@ -20,6 +27,14 @@ export default function UserAgent({backendSettings}: {backendSettings?: IBackend
|
||||
}
|
||||
}, [apiUri])
|
||||
|
||||
const Reset = useCallback(() => {
|
||||
setLoading(true);
|
||||
ResetUserAgent(apiUri)
|
||||
.then(() => setColor("success"))
|
||||
.catch(() => setColor("danger"))
|
||||
.finally(() => setLoading(false));
|
||||
}, [apiUri]);
|
||||
|
||||
return (
|
||||
<Accordion>
|
||||
<AccordionSummary>UserAgent</AccordionSummary>
|
||||
@ -30,7 +45,7 @@ export default function UserAgent({backendSettings}: {backendSettings?: IBackend
|
||||
onKeyDown={keyDown}
|
||||
onChange={e => setValue(e.target.value)}
|
||||
color={color}
|
||||
endDecorator={(loading ? <CircularProgress color={"primary"} size={"sm"} /> : null)}
|
||||
endDecorator={<Button onClick={Reset} loading={loading}>Reset</Button>}
|
||||
/>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
|
@ -15,6 +15,10 @@ import {ApiUriContext} from "./api/fetchApi.tsx";
|
||||
import IBackendSettings from "./api/types/IBackendSettings.ts";
|
||||
import { GetSettings } from './api/BackendSettings.tsx';
|
||||
import UserAgent from "./Components/Settings/UserAgent.tsx";
|
||||
import ImageProcessing from "./Components/Settings/ImageProcessing.tsx";
|
||||
import ChapterNamingScheme from "./Components/Settings/ChapterNamingScheme.tsx";
|
||||
import AprilFoolsMode from './Components/Settings/AprilFoolsMode.tsx';
|
||||
import RequestLimits from "./Components/Settings/RequestLimits.tsx";
|
||||
|
||||
const checkConnection = async (apiUri: string): Promise<boolean> =>{
|
||||
return fetch(`${apiUri}/swagger/v2/swagger.json`,
|
||||
@ -101,6 +105,10 @@ export default function Settings({open, setOpen, setApiUri, setConnected}:{open:
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
<UserAgent backendSettings={backendSettings} />
|
||||
<ImageProcessing backendSettings={backendSettings} />
|
||||
<ChapterNamingScheme backendSettings={backendSettings} />
|
||||
<AprilFoolsMode backendSettings={backendSettings} />
|
||||
<RequestLimits backendSettings={backendSettings} />
|
||||
</AccordionGroup>
|
||||
</DialogContent>
|
||||
</Drawer>
|
||||
|
Loading…
x
Reference in New Issue
Block a user