🏗️ Refactored Import socket events
This commit is contained in:
@@ -53,25 +53,6 @@ export const Import = (props: IProps): ReactElement => {
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
// 1a. Act on each comic issue successfully imported/failed, as indicated
|
|
||||||
// by the LS_COVER_EXTRACTED/LS_COVER_EXTRACTION_FAILED events
|
|
||||||
socketIOInstance.on("LS_COVER_EXTRACTED", (data) => {
|
|
||||||
const { completedJobCount, importResult } = data;
|
|
||||||
importJobQueue.setJobCount("successful", completedJobCount);
|
|
||||||
importJobQueue.setMostRecentImport(importResult.rawFileDetails.name);
|
|
||||||
});
|
|
||||||
socketIOInstance.on("LS_COVER_EXTRACTION_FAILED", (data) => {
|
|
||||||
const { failedJobCount } = data;
|
|
||||||
importJobQueue.setJobCount("failed", failedJobCount);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 1b. Clear the localStorage sessionId upon receiving the
|
|
||||||
// LS_IMPORT_QUEUE_DRAINED event
|
|
||||||
socketIOInstance.on("LS_IMPORT_QUEUE_DRAINED", (data) => {
|
|
||||||
localStorage.removeItem("sessionId");
|
|
||||||
importJobQueue.setStatus("drained");
|
|
||||||
queryClient.invalidateQueries({ queryKey: ["allImportJobResults"] });
|
|
||||||
});
|
|
||||||
const toggleQueue = (queueAction: string, queueStatus: string) => {
|
const toggleQueue = (queueAction: string, queueStatus: string) => {
|
||||||
socketIOInstance.emit(
|
socketIOInstance.emit(
|
||||||
"call",
|
"call",
|
||||||
@@ -94,29 +75,35 @@ export const Import = (props: IProps): ReactElement => {
|
|||||||
switch (status) {
|
switch (status) {
|
||||||
case "running":
|
case "running":
|
||||||
return (
|
return (
|
||||||
<div className="control">
|
<div>
|
||||||
<button
|
<button
|
||||||
className="button is-warning is-light"
|
className="flex space-x-1 sm:mt-0 sm:flex-row sm:items-center rounded-lg border border-green-400 dark:border-green-200 bg-green-200 px-3 py-1 text-gray-500 hover:bg-transparent hover:text-green-600 focus:outline-none focus:ring active:text-indigo-500"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
toggleQueue("pause", "paused");
|
toggleQueue("pause", "paused");
|
||||||
importJobQueue.setStatus("paused");
|
importJobQueue.setStatus("paused");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<i className="fa-solid fa-pause mr-2"></i> Pause
|
<span className="text-md">Pause</span>
|
||||||
|
<span className="w-5 h-5">
|
||||||
|
<i className="h-5 w-5 icon-[solar--pause-bold]"></i>
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
case "paused":
|
case "paused":
|
||||||
return (
|
return (
|
||||||
<div className="control">
|
<div>
|
||||||
<button
|
<button
|
||||||
className="button is-success is-light"
|
className="flex space-x-1 sm:mt-0 sm:flex-row sm:items-center rounded-lg border border-green-400 dark:border-green-200 bg-green-200 px-3 py-1 text-gray-500 hover:bg-transparent hover:text-green-600 focus:outline-none focus:ring active:text-indigo-500"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
toggleQueue("resume", "running");
|
toggleQueue("resume", "running");
|
||||||
importJobQueue.setStatus("running");
|
importJobQueue.setStatus("running");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<i className="fa-solid fa-play mr-2"></i> Resume
|
<span className="text-md">Resume</span>
|
||||||
|
<span className="w-5 h-5">
|
||||||
|
<i className="h-5 w-5 icon-[solar--play-bold]"></i>
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -132,7 +119,7 @@ export const Import = (props: IProps): ReactElement => {
|
|||||||
<div>
|
<div>
|
||||||
<section>
|
<section>
|
||||||
<header className="bg-slate-200 dark:bg-slate-500">
|
<header className="bg-slate-200 dark:bg-slate-500">
|
||||||
<div className="mx-auto max-w-screen-xl px-2 py-2 sm:px-6 sm:py-12 lg:px-8">
|
<div className="mx-auto max-w-screen-xl px-2 py-2 sm:px-6 sm:py-8 lg:px-8 lg:py-4">
|
||||||
<div className="sm:flex sm:items-center sm:justify-between">
|
<div className="sm:flex sm:items-center sm:justify-between">
|
||||||
<div className="text-center sm:text-left">
|
<div className="text-center sm:text-left">
|
||||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-white sm:text-3xl">
|
<h1 className="text-2xl font-bold text-gray-900 dark:text-white sm:text-3xl">
|
||||||
@@ -143,25 +130,16 @@ export const Import = (props: IProps): ReactElement => {
|
|||||||
Import comics into the ThreeTwo library.
|
Import comics into the ThreeTwo library.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-4 flex flex-col gap-4 sm:mt-0 sm:flex-row sm:items-center">
|
|
||||||
<button
|
|
||||||
className="block rounded-lg bg-indigo-600 px-5 py-3 text-sm font-medium text-white transition hover:bg-indigo-700 focus:outline-none focus:ring"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
Create Post
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div className="mx-auto max-w-screen-xl px-4 py-4 sm:px-6 sm:py-12 lg:px-8">
|
<div className="mx-auto max-w-screen-xl px-4 py-4 sm:px-6 sm:py-8 lg:px-8">
|
||||||
<article
|
<article
|
||||||
role="alert"
|
role="alert"
|
||||||
className="rounded-lg max-w-screen-md border-s-4 border-blue-500 bg-blue-50 p-4 dark:border-s-4 dark:border-blue-600 dark:bg-blue-300 dark:text-slate-600"
|
className="rounded-lg max-w-screen-md border-s-4 border-blue-500 bg-blue-50 p-4 dark:border-s-4 dark:border-blue-600 dark:bg-blue-300 dark:text-slate-600"
|
||||||
>
|
>
|
||||||
<div className="message-body">
|
<div>
|
||||||
<p>
|
<p>
|
||||||
Importing will add comics identified from the mapped folder into
|
Importing will add comics identified from the mapped folder into
|
||||||
ThreeTwo's database.
|
ThreeTwo's database.
|
||||||
@@ -195,78 +173,73 @@ export const Import = (props: IProps): ReactElement => {
|
|||||||
<span>Start Import</span>
|
<span>Start Import</span>
|
||||||
</button>
|
</button>
|
||||||
</p> */}
|
</p> */}
|
||||||
<div className="mt-4">
|
<div className="my-4">
|
||||||
<button
|
{importJobQueue.status === "drained" ||
|
||||||
className="flex space-x-1 sm:mt-0 sm:flex-row sm:items-center rounded-lg border border-green-600 bg-green-300 px-5 py-3 text-gray-500 hover:bg-transparent hover:text-green-600 focus:outline-none focus:ring active:text-indigo-500"
|
(importJobQueue.status === undefined && (
|
||||||
onClick={() => {
|
<button
|
||||||
initiateImport();
|
className="flex space-x-1 sm:mt-0 sm:flex-row sm:items-center rounded-lg border border-green-400 dark:border-green-200 bg-green-200 px-5 py-3 text-gray-500 hover:bg-transparent hover:text-green-600 focus:outline-none focus:ring active:text-indigo-500"
|
||||||
importJobQueue.setStatus("running");
|
onClick={() => {
|
||||||
}}
|
initiateImport();
|
||||||
>
|
importJobQueue.setStatus("running");
|
||||||
<span className="text-md">Start Import</span>
|
}}
|
||||||
<span className="w-6 h-6">
|
>
|
||||||
<i className="h-6 w-6 icon-[solar--file-left-bold-duotone]"></i>
|
<span className="text-md">Start Import</span>
|
||||||
</span>
|
<span className="w-6 h-6">
|
||||||
</button>
|
<i className="h-6 w-6 icon-[solar--file-left-bold-duotone]"></i>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{importJobQueue.status !== "drained" &&
|
{/* Activity */}
|
||||||
!isUndefined(importJobQueue.status) && (
|
{(importJobQueue.status === "running" ||
|
||||||
<div className="mt-4">
|
importJobQueue.status === "paused") && (
|
||||||
<table className="table">
|
<>
|
||||||
<thead>
|
<span className="flex items-center my-5 max-w-screen-lg">
|
||||||
<tr>
|
<span className="text-xl text-slate-500 dark:text-slate-200 pr-5">
|
||||||
<th>Completed Jobs</th>
|
Import Activity
|
||||||
<th>Failed Jobs</th>
|
</span>
|
||||||
<th>Queue Controls</th>
|
<span className="h-px flex-1 bg-slate-200 dark:bg-slate-400"></span>
|
||||||
<th>Queue Status</th>
|
</span>
|
||||||
</tr>
|
<div className="mt-5 flex flex-col gap-4 sm:mt-0 sm:flex-row sm:items-center">
|
||||||
</thead>
|
<dl className="grid grid-cols-2 gap-4 sm:grid-cols-2">
|
||||||
|
{/* Successful import counts */}
|
||||||
|
<div className="flex flex-col rounded-lg bg-green-100 dark:bg-green-200 px-4 py-6 text-center">
|
||||||
|
<dd className="text-3xl text-green-600 md:text-5xl">
|
||||||
|
{importJobQueue.successfulJobCount}
|
||||||
|
</dd>
|
||||||
|
<dt className="text-lg font-medium text-gray-500">
|
||||||
|
imported
|
||||||
|
</dt>
|
||||||
|
</div>
|
||||||
|
{/* Failed job counts */}
|
||||||
|
<div className="flex flex-col rounded-lg bg-red-100 dark:bg-red-200 px-4 py-6 text-center">
|
||||||
|
<dd className="text-3xl text-red-600 md:text-5xl">
|
||||||
|
{importJobQueue.failedJobCount}
|
||||||
|
</dd>
|
||||||
|
<dt className="text-lg font-medium text-gray-500">
|
||||||
|
failed
|
||||||
|
</dt>
|
||||||
|
</div>
|
||||||
|
|
||||||
<tbody>
|
<div className="flex flex-col dark:text-slate-200 text-slate-400">
|
||||||
<tr>
|
<dd>{renderQueueControls(importJobQueue.status)}</dd>
|
||||||
<th>
|
</div>
|
||||||
{importJobQueue.successfulJobCount > 0 && (
|
</dl>
|
||||||
<div className="box has-background-success-light has-text-centered">
|
</div>
|
||||||
<span className="is-size-2 has-text-weight-bold">
|
<div className="flex">
|
||||||
{importJobQueue.successfulJobCount}
|
<span className="mt-2 dark:text-slate-200 text-slate-400">
|
||||||
</span>
|
Imported: <span>{importJobQueue.mostRecentImport}</span>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</th>
|
|
||||||
<td>
|
|
||||||
{importJobQueue.failedJobCount > 0 && (
|
|
||||||
<div className="box has-background-danger has-text-centered">
|
|
||||||
<span className="is-size-2 has-text-weight-bold">
|
|
||||||
{importJobQueue.failedJobCount}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>{renderQueueControls(importJobQueue.status)}</td>
|
|
||||||
<td>
|
|
||||||
{importJobQueue.status !== undefined ? (
|
|
||||||
<span className="tag is-warning">
|
|
||||||
{importJobQueue.status}
|
|
||||||
</span>
|
|
||||||
) : null}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
Imported{" "}
|
|
||||||
<span className="has-text-weight-bold">
|
|
||||||
{importJobQueue.mostRecentImport}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Past imports */}
|
{/* Past imports */}
|
||||||
{!isLoading && !isEmpty(data?.data) && (
|
{!isLoading && !isEmpty(data?.data) && (
|
||||||
<div className="max-w-screen-lg">
|
<div className="max-w-screen-lg">
|
||||||
<span className="flex items-center mt-6">
|
<span className="flex items-center mt-6">
|
||||||
<span className="text-xl dark:text-slate-200 pr-5">
|
<span className="text-xl text-slate-500 dark:text-slate-200 pr-5">
|
||||||
Past Imports
|
Past Imports
|
||||||
</span>
|
</span>
|
||||||
<span className="h-px flex-1 bg-slate-200 dark:bg-slate-400"></span>
|
<span className="h-px flex-1 bg-slate-200 dark:bg-slate-400"></span>
|
||||||
|
|||||||
@@ -76,15 +76,15 @@ export const Navbar2 = (): ReactElement => {
|
|||||||
<ul className="flex items-center gap-6 text-md">
|
<ul className="flex items-center gap-6 text-md">
|
||||||
{/* Settings Icon and text */}
|
{/* Settings Icon and text */}
|
||||||
<li>
|
<li>
|
||||||
<a
|
<Link
|
||||||
href="#"
|
to="/settings"
|
||||||
className="flex items-center space-x-1 text-gray-500 transition hover:text-gray-500/75 dark:text-white dark:hover:text-white/75"
|
className="flex items-center space-x-1 text-gray-500 transition hover:text-gray-500/75 dark:text-white dark:hover:text-white/75"
|
||||||
>
|
>
|
||||||
<span className="w-5 h-5">
|
<span className="w-5 h-5">
|
||||||
<i className="icon-[solar--settings-outline] h-5 w-5"></i>
|
<i className="icon-[solar--settings-outline] h-5 w-5"></i>
|
||||||
</span>
|
</span>
|
||||||
<span>Settings</span>
|
<span>Settings</span>
|
||||||
</a>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { SOCKET_BASE_URI } from "../constants/endpoints";
|
|||||||
import { produce } from "immer";
|
import { produce } from "immer";
|
||||||
import AirDCPPSocket from "../services/DcppSearchService";
|
import AirDCPPSocket from "../services/DcppSearchService";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { QueryClient } from "@tanstack/react-query";
|
||||||
|
|
||||||
/* Broadly, this file sets up:
|
/* Broadly, this file sets up:
|
||||||
* 1. The zustand-based global client state
|
* 1. The zustand-based global client state
|
||||||
@@ -69,6 +70,7 @@ export const useStore = create((set, get) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const { getState, setState } = useStore;
|
const { getState, setState } = useStore;
|
||||||
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
/** Socket.IO initialization **/
|
/** Socket.IO initialization **/
|
||||||
// 1. Fetch sessionId from localStorage
|
// 1. Fetch sessionId from localStorage
|
||||||
@@ -115,6 +117,42 @@ socketIOInstance.on("RESTORE_JOB_COUNTS_AFTER_SESSION_RESTORATION", (data) => {
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 1a. Act on each comic issue successfully imported/failed, as indicated
|
||||||
|
// by the LS_COVER_EXTRACTED/LS_COVER_EXTRACTION_FAILED events
|
||||||
|
socketIOInstance.on("LS_COVER_EXTRACTED", (data) => {
|
||||||
|
const { completedJobCount, importResult } = data;
|
||||||
|
setState((state) => ({
|
||||||
|
importJobQueue: {
|
||||||
|
...state.importJobQueue,
|
||||||
|
successfulJobCount: completedJobCount,
|
||||||
|
mostRecentImport: importResult.rawFileDetails.name,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
socketIOInstance.on("LS_COVER_EXTRACTION_FAILED", (data) => {
|
||||||
|
const { failedJobCount } = data;
|
||||||
|
setState((state) => ({
|
||||||
|
importJobQueue: {
|
||||||
|
...state.importJobQueue,
|
||||||
|
failedJobCount,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
// 1b. Clear the localStorage sessionId upon receiving the
|
||||||
|
// LS_IMPORT_QUEUE_DRAINED event
|
||||||
|
socketIOInstance.on("LS_IMPORT_QUEUE_DRAINED", (data) => {
|
||||||
|
localStorage.removeItem("sessionId");
|
||||||
|
setState((state) => ({
|
||||||
|
importJobQueue: {
|
||||||
|
...state.importJobQueue,
|
||||||
|
status: "drained",
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
console.log("a", queryClient);
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["allImportJobResults"] });
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to init AirDC++ Socket with supplied settings
|
* Method to init AirDC++ Socket with supplied settings
|
||||||
* @param configuration - credentials, and hostname details to init AirDC++ connection
|
* @param configuration - credentials, and hostname details to init AirDC++ connection
|
||||||
|
|||||||
Reference in New Issue
Block a user