Tranga-Website/Website/modules/Settings.tsx

318 lines
22 KiB
TypeScript
Raw Normal View History

2024-10-23 03:01:23 +02:00
import React, {ChangeEventHandler, KeyboardEventHandler, MouseEventHandler, useEffect, useState} from 'react';
import IFrontendSettings from "./interfaces/IFrontendSettings";
import '../styles/settings.css';
2024-10-20 20:12:27 +02:00
import IBackendSettings from "./interfaces/IBackendSettings";
import {getData, postData} from "../App";
2024-10-23 02:28:36 +02:00
import LibraryConnector, {Kavita, Komga} from "./LibraryConnector";
import NotificationConnector, {Gotify, Lunasea, Ntfy} from "./NotificationConnector";
2024-10-20 20:12:27 +02:00
import ILibraryConnector from "./interfaces/ILibraryConnector";
import INotificationConnector from "./interfaces/INotificationConnector";
2024-10-23 03:01:23 +02:00
import Toggle from 'react-toggle';
import '../styles/react-toggle.css';
2024-10-20 20:12:27 +02:00
export default function Settings({backendConnected, apiUri, settings, changeSettings} : {backendConnected: boolean, apiUri: string, settings: IFrontendSettings, changeSettings: (settings: IFrontendSettings) => void}) {
2024-10-22 18:12:24 +02:00
const [frontendSettings, setFrontendSettings] = useState<IFrontendSettings>(settings);
2024-10-20 20:12:27 +02:00
const [backendSettings, setBackendSettings] = useState<IBackendSettings>();
const [showSettings, setShowSettings] = useState<boolean>(false);
2024-10-23 02:28:36 +02:00
const [libraryConnectors, setLibraryConnectors] = useState<ILibraryConnector[]>();
const [notificationConnectors, setNotificationConnectors] = useState<INotificationConnector[]>();
const [komgaSettings, setKomgaSettings] = useState<{url: string, username: string, password: string}>({url: "", username: "", password: ""});
const [kavitaSettings, setKavitaSettings] = useState<{url: string, username: string, password: string}>({url: "", username: "", password: ""});
const [gotifySettings, setGotifySettings] = useState<{url: string, appToken: string}>({url: "", appToken: ""});
const [lunaseaSettings, setLunaseaSettings] = useState<{webhook: string}>({webhook: ""});
const [ntfySettings, setNtfySettings] = useState<{url: string, username: string, password: string, topic: string | undefined}>({url: "", username: "", password: "", topic: undefined});
2024-10-20 20:12:27 +02:00
useEffect(() => {
2024-10-23 02:28:36 +02:00
console.debug(`${showSettings ? "Showing" : "Not showing"} settings.`);
2024-10-20 20:12:27 +02:00
if(!showSettings || !backendConnected)
return;
UpdateBackendSettings();
2024-10-23 02:28:36 +02:00
LibraryConnector.GetLibraryConnectors(apiUri).then(setLibraryConnectors).catch(console.error);
NotificationConnector.GetNotificationConnectors(apiUri).then(setNotificationConnectors).catch(console.error);
2024-10-20 20:12:27 +02:00
}, [showSettings]);
2024-10-22 18:12:24 +02:00
useEffect(() => {
changeSettings(frontendSettings);
}, [frontendSettings]);
2024-10-20 20:12:27 +02:00
async function GetSettings(apiUri: string) : Promise<IBackendSettings> {
2024-10-20 20:16:32 +02:00
//console.info("Getting Settings");
2024-10-20 20:12:27 +02:00
return getData(`${apiUri}/v2/Settings`)
.then((json) => {
2024-10-20 20:16:32 +02:00
//console.info("Got Settings");
2024-10-20 20:12:27 +02:00
const ret = json as IBackendSettings;
//console.debug(ret);
return (ret);
})
.catch(Promise.reject);
}
function UpdateBackendSettings() {
GetSettings(apiUri).then(setBackendSettings).catch(console.error);
}
2024-10-23 02:28:36 +02:00
const GetKomga = () : ILibraryConnector | undefined =>
libraryConnectors?.find(con => con.libraryType == 0);
2024-10-20 20:12:27 +02:00
2024-10-23 02:28:36 +02:00
const KomgaConnected = () : boolean => GetKomga() != undefined;
2024-10-20 20:12:27 +02:00
2024-10-23 02:28:36 +02:00
const GetKavita = () : ILibraryConnector | undefined =>
libraryConnectors?.find(con => con.libraryType == 1);
2024-10-20 20:12:27 +02:00
2024-10-23 02:28:36 +02:00
const KavitaConnected = () : boolean => GetKavita() != undefined;
2024-10-20 20:12:27 +02:00
2024-10-23 02:28:36 +02:00
const GetGotify = () : INotificationConnector | undefined =>
notificationConnectors?.find(con => con.notificationConnectorType == 0);
2024-10-20 20:12:27 +02:00
2024-10-23 02:28:36 +02:00
const GotifyConnected = () : boolean => GetGotify() != undefined;
2024-10-20 20:12:27 +02:00
2024-10-23 02:28:36 +02:00
const GetLunasea = () : INotificationConnector | undefined =>
notificationConnectors?.find(con => con.notificationConnectorType == 1);
const LunaseaConnected = () : boolean => GetLunasea() != undefined;
const GetNtfy = () : INotificationConnector | undefined =>
notificationConnectors?.find(con => con.notificationConnectorType == 2);
const NtfyConnected = () : boolean => GetNtfy() != undefined;
2024-10-20 20:12:27 +02:00
const SubmitApiUri : KeyboardEventHandler<HTMLInputElement> = (e) => {
2024-10-22 18:12:24 +02:00
if(e.currentTarget.value.length < 1)
return;
if(e.key == "Enter"){
setFrontendSettings({...frontendSettings, apiUri: e.currentTarget.value});
2024-10-23 02:28:36 +02:00
RefreshInputs();
2024-10-22 18:12:24 +02:00
}
}
const SubmitUserAgent: KeyboardEventHandler<HTMLInputElement> = (e) => {
if(e.currentTarget.value.length < 1 || backendSettings === undefined)
return;
if(e.key == "Enter"){
//console.info(`Updating Useragent ${e.currentTarget.value}`);
postData(`${apiUri}/v2/Settings/UserAgent`, {value: e.currentTarget.value})
.then((json) => {
//console.info(`Successfully updated Useragent ${e.currentTarget.value}`);
UpdateBackendSettings();
RefreshInputs();
})
.catch(() => alert("Failed to update Useragent."));
}
}
2024-10-23 03:01:23 +02:00
const ResetUserAgent: MouseEventHandler<HTMLSpanElement> = () => {
2024-10-23 03:01:23 +02:00
//console.info(`Resetting Useragent`);
postData(`${apiUri}/v2/Settings/UserAgent`, {value: undefined})
.then((json) => {
2024-10-23 03:01:23 +02:00
//console.info(`Successfully reset Useragent`);
UpdateBackendSettings();
RefreshInputs();
})
.catch(() => alert("Failed to update Useragent."));
}
2024-10-23 03:01:23 +02:00
const SetAprilFoolsMode : ChangeEventHandler<HTMLInputElement> = (e) => {
2024-10-27 02:32:05 +01:00
//console.info(`Updating AprilFoolsMode ${e.target.checked}`);
postData(`${apiUri}/v2/Settings/AprilFoolsMode`, {value: e.target.checked})
2024-10-23 03:01:23 +02:00
.then((json) => {
//console.info(`Successfully updated AprilFoolsMode ${e.currentTarget.value}`);
UpdateBackendSettings();
})
}
const SetCompressImages : MouseEventHandler<HTMLInputElement> = (e) => {
//console.info(`Updating ImageCompression ${e.currentTarget.value}`);
postData(`${apiUri}/v2/Settings/CompressImages`, {value: e.currentTarget.value})
2024-10-27 03:44:06 +01:00
.then((json) => {
//console.info(`Successfully updated ImageCompression ${e.currentTarget.value}`);
2024-10-27 03:44:06 +01:00
UpdateBackendSettings();
})
}
const SetBWImages : ChangeEventHandler<HTMLInputElement> = (e) => {
//console.info(`Updating B/W Images ${e.target.checked}`);
2024-10-27 03:44:06 +01:00
postData(`${apiUri}/v2/Settings/BWImages`, {value: e.target.checked})
.then((json) => {
//console.info(`Successfully updated B/W Images ${e.target.checked}`);
UpdateBackendSettings();
})
}
2024-10-23 02:28:36 +02:00
function RefreshInputs(){
alert("Saved.");
2024-10-22 18:12:24 +02:00
setShowSettings(false);
2024-10-20 20:12:27 +02:00
}
return (
<div id="Settings">
<img id="SettingsIcon" src="../media/settings-cogwheel.svg" alt="cogwheel" onClick={() => setShowSettings(true)}/>
{showSettings
? <div className="popup">
<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">
<div className="settings-section">
TRANGA
<div className="settings-section-content">
<div className="section-item">
<span className="settings-section-title">API Settings</span>
2024-10-27 04:15:25 +01:00
<label className="tooltip" data-tooltip="Set the URI of the Backend. Include https:// and if necessary port." htmlFor="settingApiUri">API URI:</label>
2024-10-27 03:44:06 +01:00
<input placeholder={frontendSettings.apiUri} type="text" id="settingApiUri"
onKeyDown={SubmitApiUri}/>
2024-10-27 04:15:25 +01:00
<label htmlFor="userAgent" className="tooltip" data-tooltip="Set a custom User-Agent. This will allow more frequent requests to sites.">User Agent:</label>
2024-10-27 03:44:06 +01:00
<input id="userAgent" type="text"
placeholder={backendSettings != undefined ? backendSettings.userAgent : "UserAgent"}
onKeyDown={SubmitUserAgent}/>
<span id="resetUserAgent" onClick={ResetUserAgent}>Reset</span>
2024-10-27 04:15:25 +01:00
<label htmlFor="aprilFoolsMode" className="tooltip" data-tooltip="Disable all downloads on April 1st">April Fools Mode</label>
2024-10-23 03:01:23 +02:00
<Toggle id="aprilFoolsMode"
2024-10-27 03:44:06 +01:00
checked={backendSettings?.aprilFoolsMode ?? false}
onChange={SetAprilFoolsMode}/>
2024-10-27 04:15:25 +01:00
<label htmlFor="compression" className="tooltip" data-tooltip="JPEG Compression Quality">Image Compression</label>
<input type="range" min="1" max="100" defaultValue={backendSettings?.compression ?? 50} className="slider" id="compression" onMouseUp={SetCompressImages}/>
2024-10-27 03:44:06 +01:00
<label htmlFor="bwImages">B/W Images</label>
<Toggle id="bwImages"
checked={backendSettings?.bwImages ?? false}
onChange={SetBWImages}/>
</div>
<div className="section-item">
<span className="settings-section-title">Rate Limits</span>
<label htmlFor="DefaultRL">Default:</label>
2024-10-20 20:12:27 +02:00
<input id="defaultRL" type="text" placeholder={backendSettings != undefined ? backendSettings.requestLimits.Default.toString() : "-1"} />
<label htmlFor="CoverRL">Manga Covers:</label>
2024-10-20 20:12:27 +02:00
<input id="coverRL" type="text" placeholder={backendSettings != undefined ? backendSettings.requestLimits.MangaCover.toString() : "-1"} />
<label htmlFor="ImageRL">Manga Images:</label>
2024-10-20 20:12:27 +02:00
<input id="imageRL" type="text" placeholder={backendSettings != undefined ? backendSettings.requestLimits.MangaImage.toString() : "-1"} />
<label htmlFor="InfoRL">Manga Info:</label>
2024-10-20 20:12:27 +02:00
<input id="infoRL" type="text" placeholder={backendSettings != undefined ? backendSettings.requestLimits.MangaInfo.toString() : "-1"} />
</div>
<div className="section-item">
<span className="settings-section-title">Appearance</span>
<label htmlFor="cssStyle">Library Style:</label>
<select id="cssStyle">
<option id="card_compact" value="card_compact">Cards (Compact)</option>
<option id="card_hover" value="card_hover">Cards (Hover)</option>
</select>
</div>
</div>
</div>
<div className="settings-section">
<span className="settings-section-title">Sources</span>
<div className="settings-section-content">
<div className="section-item">
<span className="settings-section-title">
<img src="../media/connector-icons/mangadex-logo.svg" alt="Mangadex Logo" />
<a href="https://mangadex.org">MangaDex</a>
</span>
<label htmlFor="mDexFeedRL">Feed Rate Limit:</label>
2024-10-20 20:12:27 +02:00
<input id="mDexFeedRL" type="text" placeholder={backendSettings != undefined ? backendSettings.requestLimits.MangaDexFeed.toString() : "-1"} />
<label htmlFor="mDexImageRL">Image Rate Limit:</label>
2024-10-20 20:12:27 +02:00
<input id="mDexImageRL" type="number" placeholder={backendSettings != undefined ? backendSettings.requestLimits.MangaDexImage.toString() : "-1"} />
</div>
</div>
</div>
2024-10-22 18:32:20 +02:00
<div className="settings-section" >
LIBRARY CONNECTORS
<div className="settings-section-content">
2024-10-23 02:28:36 +02:00
<div className="section-item" connector-status={KomgaConnected() ? "Configured" : "Not Configured"}>
<span className="settings-section-title">
2024-10-22 18:32:20 +02:00
<img src='../media/connector-icons/komga.svg' alt="Komga Logo"/>
Komga
</span>
2024-10-22 18:32:20 +02:00
<label htmlFor="komgaUrl">URL</label>
2024-10-23 02:28:36 +02:00
<input placeholder={GetKomga()?.baseUrl ?? "URL"} id="komgaUrl" type="text" onChange={(e) => setKomgaSettings(s => ({...s, url: e.target.value}))} />
2024-10-22 18:32:20 +02:00
<label htmlFor="komgaUsername">Username</label>
2024-10-23 02:28:36 +02:00
<input placeholder={KomgaConnected() ? "***" : "Username"} id="komgaUsername" type="text" onChange={(e) => setKomgaSettings(s => ({...s, username: e.target.value}))} />
2024-10-22 18:32:20 +02:00
<label htmlFor="komgaPassword">Password</label>
2024-10-23 02:28:36 +02:00
<input placeholder={KomgaConnected() ? "***" : "Password"} id="komgaPassword" type="password" onChange={(e) => setKomgaSettings(s => ({...s, password: e.target.value}))} />
2024-10-22 18:32:20 +02:00
<div className="section-actions">
2024-10-23 02:28:36 +02:00
<span onClick={() => new Komga(komgaSettings).Test(apiUri).then(()=>alert("Test successful"))}>Test</span>
<span onClick={() => new Komga(komgaSettings).Reset(apiUri).then(RefreshInputs)}>Reset</span>
<span onClick={() => new Komga(komgaSettings).Create(apiUri).then(RefreshInputs)}>Apply</span>
</div>
</div>
2024-10-23 02:28:36 +02:00
<div className="section-item" connector-status={KavitaConnected() ? "Configured" : "Not Configured" }>
<span className="settings-section-title">
2024-10-22 18:32:20 +02:00
<img src='../media/connector-icons/kavita.png' alt="Kavita Logo"/>
Kavita
</span>
2024-10-22 18:32:20 +02:00
<label htmlFor="kavitaUrl">URL</label>
2024-10-23 02:28:36 +02:00
<input placeholder={GetKavita()?.baseUrl ?? "URL"} id="kavitaUrl" type="text" onChange={(e) => setKavitaSettings(s => ({...s, url: e.target.value}))} />
2024-10-22 18:32:20 +02:00
<label htmlFor="kavitaUsername">Username</label>
2024-10-23 02:28:36 +02:00
<input placeholder={KavitaConnected() ? "***" : "Username"} id="kavitaUsername" type="text" onChange={(e) => setKavitaSettings(s => ({...s, username: e.target.value}))} />
2024-10-22 18:32:20 +02:00
<label htmlFor="kavitaPassword">Password</label>
2024-10-23 02:28:36 +02:00
<input placeholder={KavitaConnected() ? "***" : "Password"} id="kavitaPassword" type="password" onChange={(e) => setKavitaSettings(s => ({...s, password: e.target.value}))} />
2024-10-22 18:32:20 +02:00
<div className="section-actions">
2024-10-23 02:28:36 +02:00
<span onClick={() => new Kavita(kavitaSettings).Test(apiUri).then(()=>alert("Test successful"))}>Test</span>
<span onClick={() => new Kavita(kavitaSettings).Reset(apiUri).then(RefreshInputs)}>Reset</span>
<span onClick={() => new Kavita(kavitaSettings).Create(apiUri).then(RefreshInputs)}>Apply</span>
</div>
</div>
</div>
</div>
<div className="settings-section">
NOTIFICATION CONNECTORS
<div className="settings-section-content">
2024-10-23 02:28:36 +02:00
<div className="section-item" connector-status={GotifyConnected() ? "Configured" : "Not Configured"}>
<span className="settings-section-title">
2024-10-22 18:32:20 +02:00
<img src='../media/connector-icons/gotify-logo.png' alt="Gotify Logo"/>
Gotify
</span>
2024-10-22 18:32:20 +02:00
<label htmlFor="gotifyUrl">URL</label>
2024-10-23 02:28:36 +02:00
<input placeholder={GetGotify()?.endpoint ?? "URL"} id="gotifyUrl" type="text" onChange={(e) => setGotifySettings(s => ({...s, url: e.target.value}))} />
2024-10-22 18:32:20 +02:00
<label htmlFor="gotifyAppToken">AppToken</label>
2024-10-23 02:28:36 +02:00
<input placeholder={GotifyConnected() ? "***" : "AppToken"} id="gotifyAppToken" type="text" onChange={(e) => setGotifySettings(s => ({...s, appToken: e.target.value}))} />
2024-10-22 18:32:20 +02:00
<div className="section-actions">
2024-10-23 02:28:36 +02:00
<span onClick={() => new Gotify(gotifySettings).Test(apiUri).then(()=>alert("Test successful"))}>Test</span>
<span onClick={() => new Gotify(gotifySettings).Reset(apiUri).then(RefreshInputs)}>Reset</span>
<span onClick={() => new Gotify(gotifySettings).Create(apiUri).then(RefreshInputs)}>Apply</span>
</div>
2024-10-23 02:28:36 +02:00
</div>
<div className="section-item"
connector-status={LunaseaConnected() ? "Configured" : "Not Configured"}>
<span className="settings-section-title">
2024-10-22 18:32:20 +02:00
<img src='../media/connector-icons/lunasea.png' alt="Lunasea Logo"/>
LunaSea
</span>
2024-10-22 18:32:20 +02:00
<label htmlFor="lunaseaWebhook">Webhook id</label>
2024-10-23 02:28:36 +02:00
<input placeholder={GetLunasea() != undefined ? "***" : "device/:id or user/:id"} id="lunaseaWebhook" type="text" onChange={(e) => setLunaseaSettings(s => ({...s, webhook: e.target.value}))} />
2024-10-22 18:32:20 +02:00
<div className="section-actions">
2024-10-23 02:28:36 +02:00
<span onClick={() => new Lunasea(lunaseaSettings).Test(apiUri).then(()=>alert("Test successful"))}>Test</span>
<span onClick={() => new Lunasea(lunaseaSettings).Reset(apiUri).then(RefreshInputs)}>Reset</span>
<span onClick={() => new Lunasea(lunaseaSettings).Create(apiUri).then(RefreshInputs)}>Apply</span>
</div>
</div>
2024-10-23 02:28:36 +02:00
<div className="section-item"
connector-status={NtfyConnected() ? "Configured" : "Not Configured"}>
<span className="settings-section-title">
2024-10-20 20:12:27 +02:00
<img src='../media/connector-icons/ntfy.svg' alt="ntfy Logo"/>
Ntfy
</span>
2024-10-22 18:32:20 +02:00
<label htmlFor="ntfyEndpoint">URL</label>
2024-10-23 02:28:36 +02:00
<input placeholder={GetNtfy()?.endpoint ?? "URL"} id="ntfyEndpoint" type="text" onChange={(e) => setNtfySettings(s => ({...s, url: e.target.value}))} />
2024-10-20 20:12:27 +02:00
<label htmlFor="ntfyUsername">Username</label>
2024-10-23 02:28:36 +02:00
<input placeholder={NtfyConnected() ? "***" : "Username"} id="ntfyUsername" type="text" onChange={(e) => setNtfySettings(s => ({...s, username: e.target.value}))} />
2024-10-20 20:12:27 +02:00
<label htmlFor="ntfyPassword">Password</label>
2024-10-23 02:28:36 +02:00
<input placeholder={NtfyConnected() ? "***" : "Password"} id="ntfyPassword" type="password" onChange={(e) => setNtfySettings(s => ({...s, password: e.target.value}))} />
2024-10-20 20:12:27 +02:00
<label htmlFor="ntfyTopic">Topic</label>
2024-10-23 02:28:36 +02:00
<input placeholder={GetNtfy()?.topic ?? "Topic"} id="ntfyTopic" type="text" onChange={(e) => setNtfySettings(s => ({...s, topic: e.target.value}))} />
2024-10-22 18:32:20 +02:00
<div className="section-actions">
2024-10-23 02:28:36 +02:00
<span onClick={() => new Ntfy(ntfySettings).Test(apiUri).then(()=>alert("Test successful"))}>Test</span>
<span onClick={() => new Ntfy(ntfySettings).Reset(apiUri).then(RefreshInputs)}>Reset</span>
<span onClick={() => new Ntfy(ntfySettings).Create(apiUri).then(RefreshInputs)}>Apply</span>
</div>
</div>
</div>
</div>
</div>
</div>
: <></>
}
</div>
);
}