🐂 Queue controls
This commit is contained in:
@@ -35,6 +35,7 @@ import {
|
|||||||
VOLUMES_FETCHED,
|
VOLUMES_FETCHED,
|
||||||
CV_WEEKLY_PULLLIST_FETCHED,
|
CV_WEEKLY_PULLLIST_FETCHED,
|
||||||
LIBRARY_SERVICE_HEALTH,
|
LIBRARY_SERVICE_HEALTH,
|
||||||
|
LS_SET_QUEUE_STATUS,
|
||||||
} from "../constants/action-types";
|
} from "../constants/action-types";
|
||||||
import { success } from "react-notification-system-redux";
|
import { success } from "react-notification-system-redux";
|
||||||
|
|
||||||
@@ -96,13 +97,14 @@ export const fetchComicBookMetadata = () => async (dispatch) => {
|
|||||||
data: {},
|
data: {},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
export const toggleImportQueueStatus = (options) => async (dispatch) => {
|
export const setQueueControl =
|
||||||
dispatch({
|
(queueAction: string, queueStatus: string) => async (dispatch) => {
|
||||||
type: LS_TOGGLE_IMPORT_QUEUE,
|
dispatch({
|
||||||
meta: { remote: true },
|
type: LS_SET_QUEUE_STATUS,
|
||||||
data: { manjhul: "jigyadam", action: options.action },
|
meta: { remote: true },
|
||||||
});
|
data: { queueAction, queueStatus },
|
||||||
};
|
});
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* Fetches comic book metadata for various types
|
* Fetches comic book metadata for various types
|
||||||
* @return metadata for the comic book object categories
|
* @return metadata for the comic book object categories
|
||||||
|
|||||||
@@ -108,9 +108,6 @@ export const App = (): ReactElement => {
|
|||||||
meta: { remote: true },
|
meta: { remote: true },
|
||||||
session: { sessionId },
|
session: { sessionId },
|
||||||
});
|
});
|
||||||
socketIOConnectionInstance.on("yelaveda", (data) => {
|
|
||||||
console.log(data);
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
// Inititalize the session and persist the sessionId to localStorage
|
// Inititalize the session and persist the sessionId to localStorage
|
||||||
socketIOConnectionInstance.on("sessionInitialized", (sessionId) => {
|
socketIOConnectionInstance.on("sessionInitialized", (sessionId) => {
|
||||||
|
|||||||
@@ -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 { getTransfers } from "../../actions/airdcpp.actions";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { AirDCPPSocketContext } from "../../context/AirDCPPSocket";
|
import { AirDCPPSocketContext } from "../../context/AirDCPPSocket";
|
||||||
@@ -20,7 +26,9 @@ export const Downloads = (props: IDownloadsProps): ReactElement => {
|
|||||||
const airDCPPTransfers = useSelector(
|
const airDCPPTransfers = useSelector(
|
||||||
(state: RootState) => state.airdcpp.transfers,
|
(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([]);
|
const [bundles, setBundles] = useState([]);
|
||||||
// Make the call to get all transfers from AirDC++
|
// Make the call to get all transfers from AirDC++
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -37,18 +45,26 @@ export const Downloads = (props: IDownloadsProps): ReactElement => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isUndefined(issueBundles)) {
|
if (!isUndefined(issueBundles)) {
|
||||||
const foo = issueBundles.data.map((bundle) => {
|
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({
|
const { issueName, url } = determineCoverFile({
|
||||||
rawFileDetails, comicvine, locg,
|
rawFileDetails,
|
||||||
|
comicvine,
|
||||||
|
locg,
|
||||||
});
|
});
|
||||||
return { ...bundle, issueName, url }
|
return { ...bundle, issueName, url };
|
||||||
|
});
|
||||||
})
|
|
||||||
setBundles(foo);
|
setBundles(foo);
|
||||||
}
|
}
|
||||||
}, [issueBundles])
|
}, [issueBundles]);
|
||||||
|
|
||||||
return !isNil(bundles) ?
|
return !isNil(bundles) ? (
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<section className="section">
|
<section className="section">
|
||||||
<h1 className="title">Downloads</h1>
|
<h1 className="title">Downloads</h1>
|
||||||
@@ -56,45 +72,59 @@ export const Downloads = (props: IDownloadsProps): ReactElement => {
|
|||||||
<div className="column is-half">
|
<div className="column is-half">
|
||||||
{bundles.map((bundle, idx) => {
|
{bundles.map((bundle, idx) => {
|
||||||
console.log(bundle);
|
console.log(bundle);
|
||||||
return <div key={idx}>
|
return (
|
||||||
<MetadataPanel
|
<div key={idx}>
|
||||||
data={bundle}
|
<MetadataPanel
|
||||||
imageStyle={{ maxWidth: 80 }}
|
data={bundle}
|
||||||
titleStyle={{ fontSize: "0.8rem" }}
|
imageStyle={{ maxWidth: 80 }}
|
||||||
tagsStyle={{ fontSize: "0.7rem" }}
|
titleStyle={{ fontSize: "0.8rem" }}
|
||||||
containerStyle={{
|
tagsStyle={{ fontSize: "0.7rem" }}
|
||||||
maxWidth: 400,
|
containerStyle={{
|
||||||
padding: 0,
|
maxWidth: 400,
|
||||||
margin: "0 0 8px 0",
|
padding: 0,
|
||||||
}} />
|
margin: "0 0 8px 0",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<table className="table is-size-7">
|
<table className="table is-size-7">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Size</th>
|
<th>Size</th>
|
||||||
<th>Type</th>
|
<th>Type</th>
|
||||||
<th>Bundle ID</th>
|
<th>Bundle ID</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{bundle.acquisition.directconnect.downloads.map((bundle, idx) => {
|
{bundle.acquisition.directconnect.downloads.map(
|
||||||
return (<tr key={idx}>
|
(bundle, idx) => {
|
||||||
<td>{bundle.name}</td>
|
return (
|
||||||
<td>{bundle.size}</td>
|
<tr key={idx}>
|
||||||
<td>{bundle.type.str}</td>
|
<td>{bundle.name}</td>
|
||||||
<td><span className="tag is-warning">{bundle.bundleId}</span></td>
|
<td>{bundle.size}</td>
|
||||||
</tr>)
|
<td>{bundle.type.str}</td>
|
||||||
})}
|
<td>
|
||||||
</tbody>
|
<span className="tag is-warning">
|
||||||
</table>
|
{bundle.bundleId}
|
||||||
{/* <pre>{JSON.stringify(bundle.acquisition.directconnect.downloads, null, 2)}</pre> */}
|
</span>
|
||||||
</div>
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{/* <pre>{JSON.stringify(bundle.acquisition.directconnect.downloads, null, 2)}</pre> */}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div> : <div>There are no downloads.</div>;
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>There are no downloads.</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Downloads;
|
export default Downloads;
|
||||||
|
|||||||
@@ -1,22 +1,20 @@
|
|||||||
import React, { ReactElement } from "react";
|
import React, { ReactElement } from "react";
|
||||||
|
|
||||||
type IHeaderProps = {
|
type IHeaderProps = {
|
||||||
headerContent: string;
|
headerContent: string;
|
||||||
subHeaderContent: string;
|
subHeaderContent: string;
|
||||||
iconClassNames: string;
|
iconClassNames: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const Header = (props: IHeaderProps): ReactElement => {
|
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 (<>
|
export default Header;
|
||||||
<h4 className="title is-4">
|
|
||||||
<i className={props.iconClassNames}></i> {props.headerContent}
|
|
||||||
</h4>
|
|
||||||
<p className="subtitle is-7">
|
|
||||||
{props.subHeaderContent}
|
|
||||||
</p>
|
|
||||||
</>)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Header;
|
|
||||||
|
|||||||
@@ -2,11 +2,15 @@ import React, { ReactElement, useCallback, useContext, useState } from "react";
|
|||||||
import { useSelector, useDispatch } from "react-redux";
|
import { useSelector, useDispatch } from "react-redux";
|
||||||
import {
|
import {
|
||||||
fetchComicBookMetadata,
|
fetchComicBookMetadata,
|
||||||
toggleImportQueueStatus,
|
setQueueControl,
|
||||||
} from "../actions/fileops.actions";
|
} from "../actions/fileops.actions";
|
||||||
import "react-loader-spinner/dist/loader/css/react-spinner-loader.css";
|
import "react-loader-spinner/dist/loader/css/react-spinner-loader.css";
|
||||||
import Loader from "react-loader-spinner";
|
import Loader from "react-loader-spinner";
|
||||||
import { isUndefined } from "lodash";
|
import { isUndefined } from "lodash";
|
||||||
|
import {
|
||||||
|
LS_IMPORT_CALL_IN_PROGRESS,
|
||||||
|
LS_SET_QUEUE_STATUS,
|
||||||
|
} from "../constants/action-types";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
matches?: unknown;
|
matches?: unknown;
|
||||||
@@ -34,40 +38,62 @@ export const Import = (props: IProps): ReactElement => {
|
|||||||
(state: RootState) => state.fileOps.librarySearchResultCount,
|
(state: RootState) => state.fileOps.librarySearchResultCount,
|
||||||
);
|
);
|
||||||
const failedImportJobCount = useSelector(
|
const failedImportJobCount = useSelector(
|
||||||
(state: RootState) => state.fileOps.failedImportJobCount,
|
(state: RootState) => state.fileOps.failedJobCount,
|
||||||
);
|
);
|
||||||
|
|
||||||
const lastQueueJob = useSelector(
|
const lastQueueJob = useSelector(
|
||||||
(state: RootState) => state.fileOps.lastQueueJob,
|
(state: RootState) => state.fileOps.lastQueueJob,
|
||||||
);
|
);
|
||||||
const libraryQueueImportStatus = useSelector(
|
const libraryQueueImportStatus = useSelector(
|
||||||
(state: RootState) => state.fileOps.IMSCallInProgress,
|
(state: RootState) => state.fileOps.LSQueueImportStatus,
|
||||||
);
|
);
|
||||||
const [isImportQueuePaused, setImportQueueStatus] = useState(undefined);
|
|
||||||
const initiateImport = useCallback(() => {
|
const initiateImport = useCallback(() => {
|
||||||
if (typeof props.path !== "undefined") {
|
if (typeof props.path !== "undefined") {
|
||||||
dispatch(fetchComicBookMetadata(props.path));
|
dispatch(fetchComicBookMetadata(props.path));
|
||||||
}
|
}
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
const toggleImport = useCallback(() => {
|
const toggleQueue = useCallback(
|
||||||
setImportQueueStatus(!isImportQueuePaused);
|
(queueAction: string, queueStatus: string) => {
|
||||||
if (isImportQueuePaused === true) {
|
dispatch(setQueueControl(queueAction, queueStatus));
|
||||||
dispatch(toggleImportQueueStatus({ action: "resume" }));
|
},
|
||||||
} else if (isImportQueuePaused === false) {
|
[],
|
||||||
dispatch(toggleImportQueueStatus({ action: "pause" }));
|
);
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<section className="section is-small">
|
<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
|
This process could take a while, if you have a lot of comics, or
|
||||||
are importing over a network connection.
|
are importing over a network connection.
|
||||||
</p>
|
</p>
|
||||||
|
{JSON.stringify(libraryQueueImportStatus)}
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
<p className="buttons">
|
<p className="buttons">
|
||||||
<button
|
<button
|
||||||
className={
|
className={
|
||||||
libraryQueueImportStatus
|
libraryQueueImportStatus === "drained" ||
|
||||||
? "button is-loading is-medium"
|
libraryQueueImportStatus === undefined
|
||||||
: "button is-medium"
|
? "button is-medium"
|
||||||
|
: "button is-loading is-medium"
|
||||||
}
|
}
|
||||||
onClick={initiateImport}
|
onClick={initiateImport}
|
||||||
>
|
>
|
||||||
@@ -121,11 +149,13 @@ export const Import = (props: IProps): ReactElement => {
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
<div className="box has-background-success-light has-text-centered">
|
{libraryQueueResults && (
|
||||||
<span className="is-size-2 has-text-weight-bold">
|
<div className="box has-background-success-light has-text-centered">
|
||||||
{libraryQueueResults}
|
<span className="is-size-2 has-text-weight-bold">
|
||||||
</span>
|
{libraryQueueResults}
|
||||||
</div>
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</th>
|
</th>
|
||||||
<td>
|
<td>
|
||||||
{!isUndefined(failedImportJobCount) && (
|
{!isUndefined(failedImportJobCount) && (
|
||||||
@@ -137,16 +167,7 @@ export const Import = (props: IProps): ReactElement => {
|
|||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>{renderQueueControls(libraryQueueImportStatus)}</td>
|
||||||
<div className="control">
|
|
||||||
<button
|
|
||||||
className="button is-warning is-light"
|
|
||||||
onClick={toggleImport}
|
|
||||||
>
|
|
||||||
{isImportQueuePaused ? pauseIconText : playIconText}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -129,6 +129,8 @@ export const LS_IMPORT = "LS_IMPORT";
|
|||||||
export const LS_COVER_EXTRACTED = "LS_COVER_EXTRACTED";
|
export const LS_COVER_EXTRACTED = "LS_COVER_EXTRACTED";
|
||||||
export const LS_COVER_EXTRACTION_FAILED = "LS_COVER_EXTRACTION_FAILED";
|
export const LS_COVER_EXTRACTION_FAILED = "LS_COVER_EXTRACTION_FAILED";
|
||||||
export const LS_COMIC_ADDED = "LS_COMIC_ADDED";
|
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
|
// Settings
|
||||||
export const SETTINGS_CALL_IN_PROGRESS = "SETTINGS_CALL_IN_PROGRESS";
|
export const SETTINGS_CALL_IN_PROGRESS = "SETTINGS_CALL_IN_PROGRESS";
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ import {
|
|||||||
COMICBOOK_EXTRACTION_SUCCESS,
|
COMICBOOK_EXTRACTION_SUCCESS,
|
||||||
LIBRARY_SERVICE_HEALTH,
|
LIBRARY_SERVICE_HEALTH,
|
||||||
HEALTH_STATUS_TICK,
|
HEALTH_STATUS_TICK,
|
||||||
|
LS_IMPORT_QUEUE_DRAINED,
|
||||||
|
LS_SET_QUEUE_STATUS,
|
||||||
} from "../constants/action-types";
|
} from "../constants/action-types";
|
||||||
import { removeLeadingPeriod } from "../shared/utils/formatting.utils";
|
import { removeLeadingPeriod } from "../shared/utils/formatting.utils";
|
||||||
import { LIBRARY_SERVICE_HOST } from "../constants/endpoints";
|
import { LIBRARY_SERVICE_HOST } from "../constants/endpoints";
|
||||||
@@ -43,6 +45,7 @@ const initialState = {
|
|||||||
SSCallInProgress: false,
|
SSCallInProgress: false,
|
||||||
imageAnalysisResults: {},
|
imageAnalysisResults: {},
|
||||||
comicBookExtractionInProgress: false,
|
comicBookExtractionInProgress: false,
|
||||||
|
LSQueueImportStatus: undefined,
|
||||||
comicBookMetadata: [],
|
comicBookMetadata: [],
|
||||||
comicVolumeGroups: [],
|
comicVolumeGroups: [],
|
||||||
isSocketConnected: false,
|
isSocketConnected: false,
|
||||||
@@ -154,6 +157,7 @@ function fileOpsReducer(state = initialState, action) {
|
|||||||
case LS_IMPORT: {
|
case LS_IMPORT: {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
LSQueueImportStatus: "running",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case LS_COVER_EXTRACTED: {
|
case LS_COVER_EXTRACTED: {
|
||||||
@@ -173,14 +177,32 @@ function fileOpsReducer(state = initialState, action) {
|
|||||||
console.log("FAILED", action);
|
console.log("FAILED", action);
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
failedImportJobCount: action.failedJobCount,
|
failedJobCount: action.failedJobCount,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
case "LS_IMPORT_QUEUE_DRAINED": {
|
case LS_IMPORT_QUEUE_DRAINED: {
|
||||||
console.log("Queue drained");
|
console.log("drained");
|
||||||
return {
|
return {
|
||||||
...state,
|
...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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user