diff --git a/Website/App.tsx b/Website/App.tsx index 40da40f..1fcf465 100644 --- a/Website/App.tsx +++ b/Website/App.tsx @@ -1,10 +1,11 @@ -import React, {EventHandler, useEffect} from 'react'; +import React, {useEffect, useState} from 'react'; import Footer from "./modules/Footer"; import Search from "./modules/Search"; import Header from "./modules/Header"; import MonitorJobsList from "./modules/MonitorJobsList"; import './styles/index.css' -import QueuePopUp from "./modules/QueuePopUp"; +import {Job} from "./modules/Job"; +import IFrontendSettings from "./modules/interfaces/IFrontendSettings"; export default function App(){ const [connected, setConnected] = React.useState(false); @@ -15,35 +16,14 @@ export default function App(){ const [joblistUpdateInterval, setJoblistUpdateInterval] = React.useState(); useEffect(() => { - checkConnection(); + checkConnection().then(res => setConnected(res)).catch(() => setConnected(false)); setInterval(() => { - checkConnection(); + checkConnection().then(res => setConnected(res)).catch(() => setConnected(false)); }, 500); }, []); - const checkConnection = () =>{ - getData('http://127.0.0.1:6531/v2/Ping').then((result) => { - setConnected(result != null); - }).catch(() => setConnected(false)); - } - - useEffect(() => { - if(connected){ - setLastJobListUpdate(new Date()); - setJoblistUpdateInterval(setInterval(() => { - setLastJobListUpdate(new Date()); - }, 5000)); - }else{ - clearInterval(joblistUpdateInterval); - setJoblistUpdateInterval(undefined); - } - }, [connected]); - - - - const JobsChanged : EventHandler = () => { - setLastMangaListUpdate(new Date()); - setLastJobListUpdate(new Date()); + function CreateJob(internalId: string, jobType: string){ + Job.CreateJobDateInterval(internalId, jobType, frontendSettings.jobInterval); } return(
@@ -52,18 +32,14 @@ export default function App(){ ? <> {showSearch ? <> - setShowSearch(false)} /> + setShowSearch(false)} />
: <>} - {showQueue - ? setShowQueue(false)} /> - : <> - } - setShowSearch(true)} onJobsChanged={JobsChanged} key={lastMangaListUpdate.getTime()}/> + setShowSearch(true)} onJobsChanged={() => console.info("jobsChanged")} connectedToBackend={connected} /> :

No connection to backend

} -
setShowQueue(true)}/> +
) } @@ -133,4 +109,10 @@ export function isValidUri(uri: string) : boolean{ } catch (err) { return false; } +} + +export const checkConnection = async (): Promise =>{ + return getData('http://127.0.0.1:6531/v2/Ping').then((result) => { + return result != null; + }).catch(() => Promise.reject()); } \ No newline at end of file diff --git a/Website/modules/Footer.tsx b/Website/modules/Footer.tsx index b65bf17..235c850 100644 --- a/Website/modules/Footer.tsx +++ b/Website/modules/Footer.tsx @@ -3,12 +3,14 @@ import '../styles/footer.css'; import {Job} from './Job'; import Icon from '@mdi/react'; import { mdiRun, mdiCounter, mdiEyeCheck, mdiTrayFull } from '@mdi/js'; +import QueuePopUp from "./QueuePopUp"; -export default function Footer({showQueue} : {showQueue(): void}){ +export default function Footer({connectedToBackend} : {connectedToBackend: boolean}) { const [MonitoringJobsCount, setMonitoringJobsCount] = React.useState(0); const [AllJobsCount, setAllJobsCount] = React.useState(0); const [RunningJobsCount, setRunningJobsCount] = React.useState(0); const [StandbyJobsCount, setStandbyJobsCount] = React.useState(0); + const [countUpdateInterval, setcountUpdateInterval] = React.useState(); function UpdateBackendState(){ Job.GetMonitoringJobs().then((jobs) => setMonitoringJobsCount(jobs.length)); @@ -18,15 +20,25 @@ export default function Footer({showQueue} : {showQueue(): void}){ } useEffect(() => { - UpdateBackendState(); - }, []); + if(connectedToBackend){ + UpdateBackendState(); + setcountUpdateInterval(setInterval(() => { + UpdateBackendState(); + }, 2000)); + }else{ + clearInterval(countUpdateInterval); + setcountUpdateInterval(undefined); + } + }, [connectedToBackend]); return (
-
{MonitoringJobsCount}
-
{StandbyJobsCount}
-
{RunningJobsCount}
-
{AllJobsCount}
+
{MonitoringJobsCount}
+ +
{StandbyJobsCount}
+
{RunningJobsCount}
+
+
{AllJobsCount}

Made with Blåhaj 🦈

) } \ No newline at end of file diff --git a/Website/modules/Job.tsx b/Website/modules/Job.tsx index fcacf56..3281050 100644 --- a/Website/modules/Job.tsx +++ b/Website/modules/Job.tsx @@ -4,6 +4,10 @@ import IProgressToken from "./interfaces/IProgressToken"; export class Job { + static IntervalStringFromDate(date: Date) : string { + return `${date.getDay()}.${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`; + } + static async GetAllJobs(): Promise { console.info("Getting all Jobs"); return getData("http://127.0.0.1:6531/v2/Jobs") @@ -101,9 +105,18 @@ export class Job }); } + static async CreateJobDateInterval(internalId: string, jobType: string, interval: Date) : Promise { + return this.CreateJob(internalId, jobType, this.IntervalStringFromDate(interval)); + } + static async CreateJob(internalId: string, jobType: string, interval: string): Promise { + const validate = /(?:[0-9]{1,2}\.)?[0-9]{1,2}:[0-9]{1,2}(?::[0-9]{1,2})?/ console.info(`Creating Job for Manga ${internalId} at ${interval} interval`); - let data = { + if(!validate.test(interval)){ + console.error("Interval was in incorrect format."); + return Promise.reject(); + } + const data = { internalId: internalId, interval: interval }; diff --git a/Website/modules/MonitorJobsList.tsx b/Website/modules/MonitorJobsList.tsx index c5872ae..3a2dc90 100644 --- a/Website/modules/MonitorJobsList.tsx +++ b/Website/modules/MonitorJobsList.tsx @@ -8,9 +8,10 @@ import '../styles/MangaCoverCard.css' import Icon from '@mdi/react'; import { mdiTrashCanOutline, mdiPlayBoxOutline } from '@mdi/js'; -export default function MonitorJobsList({onStartSearch, onJobsChanged} : {onStartSearch() : void, onJobsChanged: EventHandler}) { +export default function MonitorJobsList({onStartSearch, onJobsChanged, connectedToBackend} : {onStartSearch() : void, onJobsChanged: EventHandler, connectedToBackend: boolean}) { const [MonitoringJobs, setMonitoringJobs] = useState([]); const [AllManga, setAllManga] = useState([]); + const [joblistUpdateInterval, setJoblistUpdateInterval] = React.useState(); useEffect(() => { console.debug("Updating display list."); @@ -28,8 +29,16 @@ export default function MonitorJobsList({onStartSearch, onJobsChanged} : {onStar }, [MonitoringJobs]); useEffect(() => { - UpdateMonitoringJobsList(); - }, []); + if(connectedToBackend){ + UpdateMonitoringJobsList(); + setJoblistUpdateInterval(setInterval(() => { + UpdateMonitoringJobsList(); + }, 1000)); + }else{ + clearInterval(joblistUpdateInterval); + setJoblistUpdateInterval(undefined); + } + }, [connectedToBackend]); function UpdateMonitoringJobsList(){ console.debug("Updating MonitoringJobsList"); diff --git a/Website/modules/QueuePopUp.tsx b/Website/modules/QueuePopUp.tsx index a46fa57..f1ed402 100644 --- a/Website/modules/QueuePopUp.tsx +++ b/Website/modules/QueuePopUp.tsx @@ -1,16 +1,18 @@ -import React, {ReactElement, useEffect} from 'react'; +import React, {useEffect, useState} from 'react'; import IJob, {JobTypeFromNumber} from "./interfaces/IJob"; import '../styles/queuePopUp.css'; +import '../styles/popup.css'; import {Job} from "./Job"; import IManga from "./interfaces/IManga"; import {Manga} from "./Manga"; -export default function QueuePopUp({closeQueue} : {closeQueue(): void}){ +export default function QueuePopUp({children} : {children: JSX.Element[]}) { const [StandbyJobs, setStandbyJobs] = React.useState([]); const [StandbyJobsManga, setStandbyJobsManga] = React.useState([]); const [RunningJobs, setRunningJobs] = React.useState([]); const [RunningJobsManga, setRunningJobsManga] = React.useState([]); + const [showQueuePopup, setShowQueuePopup] = useState(false); useEffect(() => { Job.GetStandbyJobs() @@ -56,45 +58,54 @@ export default function QueuePopUp({closeQueue} : {closeQueue(): void}){ .then(setRunningJobsManga); }, [RunningJobs]); - return ( -
-
-

Queue Status

- Close Search + return (<> +
setShowQueuePopup(true)}> + {children}
-
-
-

Running

-
- {RunningJobs.map((job: IJob) => { - const manga = RunningJobsManga.find(manga => manga.internalId == job.mangaInternalId || manga.internalId == job.chapter?.parentManga.internalId); - if (manga === undefined || manga === null) - return
Error. Could not find matching manga for {job.id}
- return
- -

{JobTypeFromNumber(job.jobType)}

-
; - })} + {showQueuePopup + ?
+
+

Queue Status {showQueuePopup ? "true" : "false"}

+ Close Search setShowQueuePopup(false)}/> +
+
+
+

Running

+
+ {RunningJobs.map((job: IJob) => { + const manga = RunningJobsManga.find(manga => manga.internalId == job.mangaInternalId || manga.internalId == job.chapter?.parentManga.internalId); + if (manga === undefined || manga === null) + return
Error. Could not find matching manga + for {job.id}
+ return
+ +

{JobTypeFromNumber(job.jobType)}

+
; + })} +
+
+
+

Standby

+
+ {StandbyJobs.map((job: IJob) => { + const manga = StandbyJobsManga.find(manga => manga.internalId == job.mangaInternalId || manga.internalId == job.chapter?.parentManga.internalId); + if (manga === undefined || manga === null) + return
Error. Could not find matching manga + for {job.id}
+ return
+ +

{manga.sortName}

+

{JobTypeFromNumber(job.jobType)}

+

{job.jobType == 0 ? `Vol.${job.chapter?.volumeNumber} Ch.${job.chapter?.chapterNumber}` : ""}

+
; + })} +
+
-
-

Standby

-
- {StandbyJobs.map((job: IJob) => { - const manga = StandbyJobsManga.find(manga => manga.internalId == job.mangaInternalId || manga.internalId == job.chapter?.parentManga.internalId); - if (manga === undefined || manga === null) - return
Error. Could not find matching manga - for {job.id}
- return
- -

{manga.sortName}

-

{JobTypeFromNumber(job.jobType)}

-

{job.jobType == 0 ? `Vol.${job.chapter?.volumeNumber} Ch.${job.chapter?.chapterNumber}` : ""}

-
; - })} -
-
-
-
+ : <> + } + ); } \ No newline at end of file diff --git a/Website/modules/Search.tsx b/Website/modules/Search.tsx index 2932f47..7d0d265 100644 --- a/Website/modules/Search.tsx +++ b/Website/modules/Search.tsx @@ -6,7 +6,7 @@ import IManga, {SearchResult} from "./interfaces/IManga"; import '../styles/search.css'; import '../styles/MangaSearchResult.css' -export default function Search({onJobsChanged, closeSearch} : {onJobsChanged: EventHandler, closeSearch(): void}) { +export default function Search({createJob, closeSearch} : {createJob: (internalId: string, type: string) => void, closeSearch(): void}) { const [mangaConnectors, setConnectors] = useState(); const [selectedConnector, setSelectedConnector] = useState(); const [selectedLanguage, setSelectedLanguage] = useState(); @@ -98,8 +98,7 @@ export default function Search({onJobsChanged, closeSearch} : {onJobsChanged: Ev
@@ -107,7 +106,7 @@ export default function Search({onJobsChanged, closeSearch} : {onJobsChanged: Ev
{searchResults === undefined ?

- : searchResults.map(result => SearchResult(result, onJobsChanged))} + : searchResults.map(result => SearchResult(result, createJob))}
) } \ No newline at end of file diff --git a/Website/modules/interfaces/IManga.tsx b/Website/modules/interfaces/IManga.tsx index 469220b..e81c9aa 100644 --- a/Website/modules/interfaces/IManga.tsx +++ b/Website/modules/interfaces/IManga.tsx @@ -52,7 +52,7 @@ export function CoverCard(manga: IManga) : ReactElement {
); } -export function SearchResult(manga: IManga, jobsChanged: EventHandler) : ReactElement { +export function SearchResult(manga: IManga, createJob: (internalId: string, type: string) => void) : ReactElement { return(
@@ -65,7 +65,7 @@ export function SearchResult(manga: IManga, jobsChanged: EventHandler) : Re
); diff --git a/Website/styles/footer.css b/Website/styles/footer.css index 50393a8..54f5818 100644 --- a/Website/styles/footer.css +++ b/Website/styles/footer.css @@ -11,6 +11,7 @@ footer { position: fixed; bottom: 0; color: white; + z-index: 10; } #madeWith { @@ -20,19 +21,20 @@ footer { cursor: url("Website/media/blahaj.png"), grab; } -footer div { +footer .statusBadge { margin: 0 10px; display: flex; align-items: center; justify-items: center; background-color: rgba(255,255,255, 0.3); border-radius: 10px; - padding: 0 5px; + padding: 2px 5px; } -footer div > * { - margin: 0 2px; - padding: 3px 0; +footer > div { + display: flex; + align-items: center; + justify-items: center; } footer .hoverHand { diff --git a/Website/styles/popup.css b/Website/styles/popup.css new file mode 100644 index 0000000..c41a4f8 --- /dev/null +++ b/Website/styles/popup.css @@ -0,0 +1,43 @@ +.popup { + position: fixed; + left: 10%; + top: 7.5%; + width: 80%; + height: 80%; + margin: auto; + z-index: 100; + background-color: var(--second-background-color); + border-radius: 10px; + overflow: hidden; +} + +.popup .popupHeader { + position: absolute; + top: 0; + left: 0; + height: 40px; + width: 100%; + background-color: var(--primary-color); + color: var(--accent-color); +} + +.popup .popupHeader h1 { + margin: 4px 10px; + font-size: 20pt; +} + +.popup .close { + position: absolute; + top: 0; + right: 0; + height: 100%; + cursor: pointer; +} + +.popup .popupBody { + position: absolute; + top: 40px; + left: 0; + width: 100%; + height: calc(100% - 40px); +} \ No newline at end of file diff --git a/Website/styles/queuePopUp.css b/Website/styles/queuePopUp.css index 1f2afeb..59713d8 100644 --- a/Website/styles/queuePopUp.css +++ b/Website/styles/queuePopUp.css @@ -1,44 +1,4 @@ -#QueuePopUp { - position: absolute; - left: 10%; - top: 7.5%; - width: 80%; - height: 80%; - margin: auto; - z-index: 100; - background-color: var(--second-background-color); - border-radius: 10px; - overflow: hidden; -} - -#QueuePopUp #QueuePopUpHeader { - position: absolute; - top: 0; - left: 0; - height: 40px; - width: 100%; - background-color: var(--primary-color); - color: var(--accent-color); -} - -#QueuePopUp #QueuePopUpHeader h1 { - margin: 4px 10px; - font-size: 20pt; -} - -#QueuePopUp #closeSearch { - position: absolute; - top: 0; - right: 0; - height: 100%; -} - #QueuePopUp #QueuePopUpBody { - position: absolute; - top: 40px; - left: 0; - width: 100%; - height: calc(100% - 40px); display: flex; }