🐂 Queue controls

This commit is contained in:
2023-08-22 22:07:33 -05:00
parent b4ef0b7817
commit debd9a20bf
7 changed files with 183 additions and 111 deletions

View File

@@ -35,6 +35,7 @@ import {
VOLUMES_FETCHED,
CV_WEEKLY_PULLLIST_FETCHED,
LIBRARY_SERVICE_HEALTH,
LS_SET_QUEUE_STATUS,
} from "../constants/action-types";
import { success } from "react-notification-system-redux";
@@ -96,13 +97,14 @@ export const fetchComicBookMetadata = () => async (dispatch) => {
data: {},
});
};
export const toggleImportQueueStatus = (options) => async (dispatch) => {
dispatch({
type: LS_TOGGLE_IMPORT_QUEUE,
meta: { remote: true },
data: { manjhul: "jigyadam", action: options.action },
});
};
export const setQueueControl =
(queueAction: string, queueStatus: string) => async (dispatch) => {
dispatch({
type: LS_SET_QUEUE_STATUS,
meta: { remote: true },
data: { queueAction, queueStatus },
});
};
/**
* Fetches comic book metadata for various types
* @return metadata for the comic book object categories

View File

@@ -108,9 +108,6 @@ export const App = (): ReactElement => {
meta: { remote: true },
session: { sessionId },
});
socketIOConnectionInstance.on("yelaveda", (data) => {
console.log(data);
});
} else {
// Inititalize the session and persist the sessionId to localStorage
socketIOConnectionInstance.on("sessionInitialized", (sessionId) => {

View File

@@ -1,4 +1,10 @@
import React, { ReactElement, useCallback, useContext, useEffect, useState } from "react";
import React, {
ReactElement,
useCallback,
useContext,
useEffect,
useState,
} from "react";
import { getTransfers } from "../../actions/airdcpp.actions";
import { useDispatch, useSelector } from "react-redux";
import { AirDCPPSocketContext } from "../../context/AirDCPPSocket";
@@ -20,7 +26,9 @@ export const Downloads = (props: IDownloadsProps): ReactElement => {
const airDCPPTransfers = useSelector(
(state: RootState) => state.airdcpp.transfers,
);
const issueBundles = useSelector((state: RootState) => state.airdcpp.issue_bundles);
const issueBundles = useSelector(
(state: RootState) => state.airdcpp.issue_bundles,
);
const [bundles, setBundles] = useState([]);
// Make the call to get all transfers from AirDC++
useEffect(() => {
@@ -37,18 +45,26 @@ export const Downloads = (props: IDownloadsProps): ReactElement => {
useEffect(() => {
if (!isUndefined(issueBundles)) {
const foo = issueBundles.data.map((bundle) => {
const { rawFileDetails, inferredMetadata, acquisition: { directconnect: { downloads } }, sourcedMetadata: { locg, comicvine } } = bundle;
const {
rawFileDetails,
inferredMetadata,
acquisition: {
directconnect: { downloads },
},
sourcedMetadata: { locg, comicvine },
} = bundle;
const { issueName, url } = determineCoverFile({
rawFileDetails, comicvine, locg,
rawFileDetails,
comicvine,
locg,
});
return { ...bundle, issueName, url }
})
return { ...bundle, issueName, url };
});
setBundles(foo);
}
}, [issueBundles])
}, [issueBundles]);
return !isNil(bundles) ?
return !isNil(bundles) ? (
<div className="container">
<section className="section">
<h1 className="title">Downloads</h1>
@@ -56,45 +72,59 @@ export const Downloads = (props: IDownloadsProps): ReactElement => {
<div className="column is-half">
{bundles.map((bundle, idx) => {
console.log(bundle);
return <div key={idx}>
<MetadataPanel
data={bundle}
imageStyle={{ maxWidth: 80 }}
titleStyle={{ fontSize: "0.8rem" }}
tagsStyle={{ fontSize: "0.7rem" }}
containerStyle={{
maxWidth: 400,
padding: 0,
margin: "0 0 8px 0",
}} />
return (
<div key={idx}>
<MetadataPanel
data={bundle}
imageStyle={{ maxWidth: 80 }}
titleStyle={{ fontSize: "0.8rem" }}
tagsStyle={{ fontSize: "0.7rem" }}
containerStyle={{
maxWidth: 400,
padding: 0,
margin: "0 0 8px 0",
}}
/>
<table className="table is-size-7">
<thead>
<tr>
<th>Name</th>
<th>Size</th>
<th>Type</th>
<th>Bundle ID</th>
</tr>
</thead>
<tbody>
{bundle.acquisition.directconnect.downloads.map((bundle, idx) => {
return (<tr key={idx}>
<td>{bundle.name}</td>
<td>{bundle.size}</td>
<td>{bundle.type.str}</td>
<td><span className="tag is-warning">{bundle.bundleId}</span></td>
</tr>)
})}
</tbody>
</table>
{/* <pre>{JSON.stringify(bundle.acquisition.directconnect.downloads, null, 2)}</pre> */}
</div>
<table className="table is-size-7">
<thead>
<tr>
<th>Name</th>
<th>Size</th>
<th>Type</th>
<th>Bundle ID</th>
</tr>
</thead>
<tbody>
{bundle.acquisition.directconnect.downloads.map(
(bundle, idx) => {
return (
<tr key={idx}>
<td>{bundle.name}</td>
<td>{bundle.size}</td>
<td>{bundle.type.str}</td>
<td>
<span className="tag is-warning">
{bundle.bundleId}
</span>
</td>
</tr>
);
},
)}
</tbody>
</table>
{/* <pre>{JSON.stringify(bundle.acquisition.directconnect.downloads, null, 2)}</pre> */}
</div>
);
})}
</div>
</div>
</section>
</div> : <div>There are no downloads.</div>;
</div>
) : (
<div>There are no downloads.</div>
);
};
export default Downloads;

View File

@@ -1,22 +1,20 @@
import React, { ReactElement } from "react";
type IHeaderProps = {
headerContent: string;
subHeaderContent: string;
iconClassNames: string;
}
headerContent: string;
subHeaderContent: string;
iconClassNames: string;
};
export const Header = (props: IHeaderProps): ReactElement => {
return (
<>
<h4 className="title is-4">
<i className={props.iconClassNames}></i> {props.headerContent}
</h4>
<p className="subtitle is-7">{props.subHeaderContent}</p>
</>
);
};
return (<>
<h4 className="title is-4">
<i className={props.iconClassNames}></i> {props.headerContent}
</h4>
<p className="subtitle is-7">
{props.subHeaderContent}
</p>
</>)
}
export default Header;
export default Header;

View File

@@ -2,11 +2,15 @@ import React, { ReactElement, useCallback, useContext, useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import {
fetchComicBookMetadata,
toggleImportQueueStatus,
setQueueControl,
} from "../actions/fileops.actions";
import "react-loader-spinner/dist/loader/css/react-spinner-loader.css";
import Loader from "react-loader-spinner";
import { isUndefined } from "lodash";
import {
LS_IMPORT_CALL_IN_PROGRESS,
LS_SET_QUEUE_STATUS,
} from "../constants/action-types";
interface IProps {
matches?: unknown;
@@ -34,40 +38,62 @@ export const Import = (props: IProps): ReactElement => {
(state: RootState) => state.fileOps.librarySearchResultCount,
);
const failedImportJobCount = useSelector(
(state: RootState) => state.fileOps.failedImportJobCount,
(state: RootState) => state.fileOps.failedJobCount,
);
const lastQueueJob = useSelector(
(state: RootState) => state.fileOps.lastQueueJob,
);
const libraryQueueImportStatus = useSelector(
(state: RootState) => state.fileOps.IMSCallInProgress,
(state: RootState) => state.fileOps.LSQueueImportStatus,
);
const [isImportQueuePaused, setImportQueueStatus] = useState(undefined);
const initiateImport = useCallback(() => {
if (typeof props.path !== "undefined") {
dispatch(fetchComicBookMetadata(props.path));
}
}, [dispatch]);
const toggleImport = useCallback(() => {
setImportQueueStatus(!isImportQueuePaused);
if (isImportQueuePaused === true) {
dispatch(toggleImportQueueStatus({ action: "resume" }));
} else if (isImportQueuePaused === false) {
dispatch(toggleImportQueueStatus({ action: "pause" }));
const toggleQueue = useCallback(
(queueAction: string, queueStatus: string) => {
dispatch(setQueueControl(queueAction, queueStatus));
},
[],
);
const renderQueueControls = (status: string): ReactElement | null => {
switch (status) {
case "running":
return (
<div className="control">
<button
className="button is-warning is-light"
onClick={() => toggleQueue("pause", "paused")}
>
<i className="fa-solid fa-pause mr-2"></i> Pause
</button>
</div>
);
case "paused":
return (
<div className="control">
<button
className="button is-success is-light"
onClick={() => toggleQueue("resume", "running")}
>
<i className="fa-solid fa-play mr-2"></i> Resume
</button>
</div>
);
case "drained":
return null;
default:
return null;
}
}, [isImportQueuePaused]);
const pauseIconText = (
<>
<i className="fa-solid fa-pause mr-2"></i> Pause
</>
);
const playIconText = (
<>
<i className="fa-solid fa-play mr-2"></i> Resume
</>
);
};
return (
<div className="container">
<section className="section is-small">
@@ -88,14 +114,16 @@ export const Import = (props: IProps): ReactElement => {
This process could take a while, if you have a lot of comics, or
are importing over a network connection.
</p>
{JSON.stringify(libraryQueueImportStatus)}
</div>
</article>
<p className="buttons">
<button
className={
libraryQueueImportStatus
? "button is-loading is-medium"
: "button is-medium"
libraryQueueImportStatus === "drained" ||
libraryQueueImportStatus === undefined
? "button is-medium"
: "button is-loading is-medium"
}
onClick={initiateImport}
>
@@ -121,11 +149,13 @@ export const Import = (props: IProps): ReactElement => {
<tbody>
<tr>
<th>
<div className="box has-background-success-light has-text-centered">
<span className="is-size-2 has-text-weight-bold">
{libraryQueueResults}
</span>
</div>
{libraryQueueResults && (
<div className="box has-background-success-light has-text-centered">
<span className="is-size-2 has-text-weight-bold">
{libraryQueueResults}
</span>
</div>
)}
</th>
<td>
{!isUndefined(failedImportJobCount) && (
@@ -137,16 +167,7 @@ export const Import = (props: IProps): ReactElement => {
)}
</td>
<td>
<div className="control">
<button
className="button is-warning is-light"
onClick={toggleImport}
>
{isImportQueuePaused ? pauseIconText : playIconText}
</button>
</div>
</td>
<td>{renderQueueControls(libraryQueueImportStatus)}</td>
</tr>
</tbody>
</table>

View File

@@ -129,6 +129,8 @@ export const LS_IMPORT = "LS_IMPORT";
export const LS_COVER_EXTRACTED = "LS_COVER_EXTRACTED";
export const LS_COVER_EXTRACTION_FAILED = "LS_COVER_EXTRACTION_FAILED";
export const LS_COMIC_ADDED = "LS_COMIC_ADDED";
export const LS_IMPORT_QUEUE_DRAINED = "LS_IMPORT_QUEUE_DRAINED";
export const LS_SET_QUEUE_STATUS = "LS_SET_QUEUE_STATUS";
// Settings
export const SETTINGS_CALL_IN_PROGRESS = "SETTINGS_CALL_IN_PROGRESS";

View File

@@ -33,6 +33,8 @@ import {
COMICBOOK_EXTRACTION_SUCCESS,
LIBRARY_SERVICE_HEALTH,
HEALTH_STATUS_TICK,
LS_IMPORT_QUEUE_DRAINED,
LS_SET_QUEUE_STATUS,
} from "../constants/action-types";
import { removeLeadingPeriod } from "../shared/utils/formatting.utils";
import { LIBRARY_SERVICE_HOST } from "../constants/endpoints";
@@ -43,6 +45,7 @@ const initialState = {
SSCallInProgress: false,
imageAnalysisResults: {},
comicBookExtractionInProgress: false,
LSQueueImportStatus: undefined,
comicBookMetadata: [],
comicVolumeGroups: [],
isSocketConnected: false,
@@ -154,6 +157,7 @@ function fileOpsReducer(state = initialState, action) {
case LS_IMPORT: {
return {
...state,
LSQueueImportStatus: "running",
};
}
case LS_COVER_EXTRACTED: {
@@ -173,14 +177,32 @@ function fileOpsReducer(state = initialState, action) {
console.log("FAILED", action);
return {
...state,
failedImportJobCount: action.failedJobCount,
failedJobCount: action.failedJobCount,
};
}
case "LS_IMPORT_QUEUE_DRAINED": {
console.log("Queue drained");
case LS_IMPORT_QUEUE_DRAINED: {
console.log("drained");
return {
...state,
LSQueueImportStatus: "drained",
};
}
case "RESTORE_JOB_COUNTS_AFTER_SESSION_RESTORATION": {
console.log(action);
return {
...state,
librarySearchResultCount: action.completedJobCount,
failedJobCount: action.failedJobCount,
};
}
case LS_SET_QUEUE_STATUS: {
console.log(action);
return {
...state,
LSQueueImportStatus: action.data.queueStatus,
};
}