import IFrontendSettings from "./interfaces/IFrontendSettings"; import '../styles/settings.css'; import '../styles/react-toggle.css'; import React, {useEffect, useRef, useState} from "react"; import INotificationConnector, {NotificationConnectorItem} from "./interfaces/INotificationConnector"; import NotificationConnectorFunctions from "./NotificationConnectorFunctions"; import ILocalLibrary, {LocalLibraryItem} from "./interfaces/ILocalLibrary"; import LocalLibraryFunctions from "./LocalLibraryFunctions"; import IBackendSettings from "./interfaces/IBackendSettings"; import BackendSettings from "./BackendSettingsFunctions"; import Toggle from "react-toggle"; import Loader from "./Loader"; import {RequestType} from "./interfaces/IRequestLimits"; import IMangaConnector from "./interfaces/IMangaConnector"; import {MangaConnectorFunctions} from "./MangaConnectorFunctions"; export default function Settings({ backendConnected, apiUri, frontendSettings, setFrontendSettings } : { backendConnected: boolean, apiUri: string, frontendSettings: IFrontendSettings, setFrontendSettings: (settings: IFrontendSettings) => void }) { const [showSettings, setShowSettings] = useState<boolean>(false); const [loadingBackend, setLoadingBackend] = useState(false); const [backendSettings, setBackendSettings] = useState<IBackendSettings|null>(null); const [notificationConnectors, setNotificationConnectors] = useState<INotificationConnector[]>([]); const [mangaConnectors,setMangaConnectors] = useState<IMangaConnector[]>([]); const [localLibraries, setLocalLibraries] = useState<ILocalLibrary[]>([]); const [chapterNamingScheme, setChapterNamingScheme] = useState<string>(""); useEffect(() => { if(!backendConnected) return; NotificationConnectorFunctions.GetNotificationConnectors(apiUri).then(setNotificationConnectors); LocalLibraryFunctions.GetLibraries(apiUri).then(setLocalLibraries); BackendSettings.GetSettings(apiUri).then(setBackendSettings); MangaConnectorFunctions.GetAllConnectors(apiUri).then(setMangaConnectors); }, [backendConnected, showSettings]); const dateToStr = (x: Date) => { const ret = (x.getHours() < 10 ? "0" + x.getHours() : x.getHours()) + ":" + (x.getMinutes() < 10 ? "0" + x.getMinutes() : x.getMinutes()); return ret; } const ChangeRequestLimit = (requestType: RequestType, limit: number) => { if(backendSettings === null) return; setLoadingBackend(true); BackendSettings.UpdateRequestLimit(apiUri, requestType, limit) .then(() => setBackendSettings({...backendSettings, [requestType]: requestType})) .finally(() => setLoadingBackend(false)); } const ref : React.LegacyRef<HTMLInputElement> | undefined = useRef<HTMLInputElement>(null); return ( <div id="Settings"> <div onClick={() => setShowSettings(true)}> <img id="Settings-Cogwheel" src="../media/settings-cogwheel.svg" alt="settings-cogwheel" /> </div> {showSettings ? <div className="popup" id="SettingsPopUp"> <div className="popupHeader"> <h1>Settings</h1> <img alt="Close Settings" className="close" src="../media/close-x.svg" onClick={() => setShowSettings(false)}/> </div> <div id="SettingsPopUpBody" className="popupBody"> <Loader loading={loadingBackend} style={{width: "64px", height: "64px", margin: "25vh calc(sin(70)*(50% - 40px))", zIndex: 100, padding: 0, borderRadius: "50%", border: 0, minWidth: "initial", maxWidth: "initial"}}/> <div className="settings-apiuri"> <h3>ApiUri</h3> <input type="url" defaultValue={frontendSettings.apiUri} onChange={(e) => setFrontendSettings({...frontendSettings, apiUri:e.currentTarget.value})} id="ApiUri" /> </div> <div className="settings-jobinterval"> <h3>Default Job-Interval</h3> <input type="time" min="00:30" max="23:59" defaultValue={dateToStr(new Date(frontendSettings.jobInterval))} onChange={(e) => setFrontendSettings({...frontendSettings, jobInterval: new Date(e.currentTarget.valueAsNumber-60*60*1000) ?? frontendSettings.jobInterval})}/> </div> <div className={"settings-chapterNamingScheme"}> <h3>Chapter Naming-Scheme</h3> <input type={"text"} placeholder={backendSettings?.chapterNamingScheme} onChange={(e) => setChapterNamingScheme(e.target.value)} /> <button type={"button"} onClick={() => { setLoadingBackend(true); BackendSettings.UpdateChapterNamingScheme(apiUri, chapterNamingScheme).finally(() => setLoadingBackend(false)); }}>Submit</button> </div> <div className="settings-bwimages"> <h3>B/W Images</h3> <Toggle defaultChecked={backendSettings ? backendSettings.bwImages : false} disabled={backendSettings ? false : !loadingBackend} onChange={(e) => { if(backendSettings === null) return; setLoadingBackend(true); BackendSettings.UpdateBWImageToggle(apiUri, e.target.checked) .then(() => setBackendSettings({...backendSettings, bwImages: e.target.checked})) .finally(() => setLoadingBackend(false)); }} /> </div> <div className="settings-aprilfools"> <h3>April Fools Mode</h3> <Toggle defaultChecked={backendSettings ? backendSettings.aprilFoolsMode : false} disabled={backendSettings ? false : !loadingBackend} onChange={(e) => { if(backendSettings === null) return; setLoadingBackend(true); BackendSettings.UpdateAprilFoolsToggle(apiUri, e.target.checked) .then(() => setBackendSettings({...backendSettings, aprilFoolsMode: e.target.checked})) .finally(() => setLoadingBackend(false)); }} /> </div> <div className="settings-imagecompression"> <h3>Image Compression</h3> <Toggle defaultChecked={backendSettings ? backendSettings.compression < 100 : false} disabled={backendSettings ? false : !loadingBackend} onChange={(e) => { if(backendSettings === null) return; setLoadingBackend(true); BackendSettings.UpdateImageCompressionValue(apiUri, e.target.checked ? 40 : 100) .then(() => setBackendSettings({...backendSettings, compression: e.target.checked ? 40 : 100})) .then(() => { if(ref.current != null){ ref.current.value = e.target.checked ? "40" : "100"; ref.current.disabled = !e.target.checked; } }) .finally(() => setLoadingBackend(false)); }} /> <input ref={ref} type="number" min={0} max={100} defaultValue={backendSettings ? backendSettings.compression : 0} disabled={backendSettings ? false : !loadingBackend} onChange={(e) => { if(backendSettings === null) return; setLoadingBackend(true); BackendSettings.UpdateImageCompressionValue(apiUri, e.currentTarget.valueAsNumber) .then(() => setBackendSettings({...backendSettings, compression: e.currentTarget.valueAsNumber})) .finally(() => setLoadingBackend(false)); }} /> </div> <div className="settings-useragent"> <h3>User Agent</h3> <input type="text" defaultValue={backendSettings ? backendSettings.userAgent : ""} onChange={(e) => { if(backendSettings === null) return; setLoadingBackend(true); BackendSettings.UpdateUserAgent(apiUri, e.currentTarget.value) .then(() => setBackendSettings({...backendSettings, userAgent: e.currentTarget.value})) .finally(() => setLoadingBackend(false)); }} /> </div> <div className="settings-requestLimits"> <h3>Request Limits:</h3> <label htmlFor="Default">Default</label> <input id="Default" type="number" defaultValue={backendSettings ? backendSettings.requestLimits.Default : 0} disabled={backendSettings ? false : !loadingBackend} onChange={(e) => ChangeRequestLimit(RequestType.Default, e.currentTarget.valueAsNumber)} /> <label htmlFor="MangaInfo">MangaInfo</label> <input id="MangaInfo" type="number" defaultValue={backendSettings ? backendSettings.requestLimits.MangaInfo : 0} disabled={backendSettings ? false : !loadingBackend} onChange={(e) => ChangeRequestLimit(RequestType.MangaInfo, e.currentTarget.valueAsNumber)} /> <label htmlFor="MangaDexFeed">MangaDexFeed</label> <input id="MangaDexFeed" type="number" defaultValue={backendSettings ? backendSettings.requestLimits.MangaDexFeed : 0} disabled={backendSettings ? false : !loadingBackend} onChange={(e) => ChangeRequestLimit(RequestType.MangaDexFeed, e.currentTarget.valueAsNumber)} /> <label htmlFor="MangaDexImage">MangaDexImage</label> <input id="MangaDexImage" type="number" defaultValue={backendSettings ? backendSettings.requestLimits.MangaDexImage : 0} disabled={backendSettings ? false : !loadingBackend} onChange={(e) => ChangeRequestLimit(RequestType.MangaDexImage, e.currentTarget.valueAsNumber)} /> <label htmlFor="MangaImage">MangaImage</label> <input id="MangaImage" type="number" defaultValue={backendSettings ? backendSettings.requestLimits.MangaImage : 0} disabled={backendSettings ? false : !loadingBackend} onChange={(e) => ChangeRequestLimit(RequestType.MangaImage, e.currentTarget.valueAsNumber)} /> <label htmlFor="MangaCover">MangaCover</label> <input id="MangaCover" type="number" defaultValue={backendSettings ? backendSettings.requestLimits.MangaCover : 0} disabled={backendSettings ? false : !loadingBackend} onChange={(e) => ChangeRequestLimit(RequestType.MangaCover, e.currentTarget.valueAsNumber)} /> </div> <div className={"settings-mangaConnectors"}> {mangaConnectors.map(mc => { return ( <div key={mc.name}> <span>{mc.name}</span> <Toggle defaultChecked={mc.enabled} onChange={(e) => { MangaConnectorFunctions.SetConnectorEnabled(apiUri, mc.name, e.currentTarget.checked); }} /> </div>); })} </div> <div> <h3>Notification Connectors:</h3> {notificationConnectors.map(c => <NotificationConnectorItem apiUri={apiUri} notificationConnector={c} key={c.name} />)} <NotificationConnectorItem apiUri={apiUri} notificationConnector={null} key="New Notification Connector" /> </div> <div> <h3>Local Libraries:</h3> {localLibraries.map(l => <LocalLibraryItem apiUri={apiUri} library={l} key={l.localLibraryId} />)} <LocalLibraryItem apiUri={apiUri} library={null} key="New Local Library" /> </div> </div> </div> : null } </div> ); }