Compare commits
22 Commits
main
...
import-que
| Author | SHA1 | Date | |
|---|---|---|---|
| 9d95888628 | |||
| 284ccefc79 | |||
| 874ba6b337 | |||
| 7efbbe0cfe | |||
| 80b7113ed5 | |||
| 75777d32dd | |||
| ffa1b0cc09 | |||
| 084453b093 | |||
| 9d5cca913e | |||
| bab92fccc8 | |||
| b0282ccbb3 | |||
| a5c02d3fc6 | |||
| debd9a20bf | |||
| b4ef0b7817 | |||
| 0c2cbb6937 | |||
| 8c6890a044 | |||
| a2ae4ce79d | |||
| 31cd53b7a2 | |||
| 11ef9e6e2a | |||
|
|
26fdb6d3a4 | ||
| a2f8d8b1e8 | |||
| ae9ec75983 |
@@ -1,10 +1,13 @@
|
|||||||
import type { StorybookConfig } from "@storybook/react-vite";
|
import type { StorybookConfig } from "@storybook/react-vite";
|
||||||
|
|
||||||
const config: StorybookConfig = {
|
const config: StorybookConfig = {
|
||||||
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
|
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
|
||||||
addons: [
|
addons: [
|
||||||
"@storybook/addon-links",
|
"@storybook/addon-links",
|
||||||
"@storybook/addon-essentials",
|
"@storybook/addon-essentials",
|
||||||
|
"@storybook/addon-onboarding",
|
||||||
"@storybook/addon-interactions",
|
"@storybook/addon-interactions",
|
||||||
|
"@storybook/addon-mdx-gfm"
|
||||||
],
|
],
|
||||||
framework: {
|
framework: {
|
||||||
name: "@storybook/react-vite",
|
name: "@storybook/react-vite",
|
||||||
|
|||||||
55
package.json
55
package.json
@@ -15,7 +15,6 @@
|
|||||||
"author": "Rishi Ghan",
|
"author": "Rishi Ghan",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.13.17",
|
|
||||||
"@bluelovers/fast-glob": "https://github.com/rishighan/fast-glob-v2-api.git",
|
"@bluelovers/fast-glob": "https://github.com/rishighan/fast-glob-v2-api.git",
|
||||||
"@dnd-kit/core": "^4.0.0",
|
"@dnd-kit/core": "^4.0.0",
|
||||||
"@dnd-kit/sortable": "^5.0.0",
|
"@dnd-kit/sortable": "^5.0.0",
|
||||||
@@ -23,14 +22,11 @@
|
|||||||
"@fortawesome/fontawesome-free": "^6.3.0",
|
"@fortawesome/fontawesome-free": "^6.3.0",
|
||||||
"@redux-devtools/extension": "^3.2.2",
|
"@redux-devtools/extension": "^3.2.2",
|
||||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||||
"@tanstack/react-table": "^8.5.11",
|
"@tanstack/react-table": "^8.9.3",
|
||||||
"@types/axios": "^0.14.0",
|
|
||||||
"@types/mime-types": "^2.1.0",
|
"@types/mime-types": "^2.1.0",
|
||||||
"@types/react-router-dom": "^5.3.3",
|
"@types/react-router-dom": "^5.3.3",
|
||||||
"@types/socket.io": "^3.0.2",
|
|
||||||
"@types/socket.io-client": "^3.0.0",
|
|
||||||
"@vitejs/plugin-react": "^3.1.0",
|
"@vitejs/plugin-react": "^3.1.0",
|
||||||
"airdcpp-apisocket": "2.4.5-beta.1",
|
"airdcpp-apisocket": "^2.5.0-beta.2",
|
||||||
"axios": "^1.3.4",
|
"axios": "^1.3.4",
|
||||||
"axios-cache-interceptor": "^1.0.1",
|
"axios-cache-interceptor": "^1.0.1",
|
||||||
"axios-rate-limit": "^1.3.0",
|
"axios-rate-limit": "^1.3.0",
|
||||||
@@ -56,7 +52,7 @@
|
|||||||
"react-collapsible": "^2.9.0",
|
"react-collapsible": "^2.9.0",
|
||||||
"react-comic-viewer": "^0.4.0",
|
"react-comic-viewer": "^0.4.0",
|
||||||
"react-day-picker": "^8.6.0",
|
"react-day-picker": "^8.6.0",
|
||||||
"react-dom": "^18.1.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-fast-compare": "^3.2.0",
|
"react-fast-compare": "^3.2.0",
|
||||||
"react-final-form": "^6.5.9",
|
"react-final-form": "^6.5.9",
|
||||||
"react-final-form-arrays": "^3.1.4",
|
"react-final-form-arrays": "^3.1.4",
|
||||||
@@ -78,27 +74,21 @@
|
|||||||
"redux-thunk": "^2.4.2",
|
"redux-thunk": "^2.4.2",
|
||||||
"slick-carousel": "^1.8.1",
|
"slick-carousel": "^1.8.1",
|
||||||
"socket.io-client": "^4.3.2",
|
"socket.io-client": "^4.3.2",
|
||||||
"styled-components": "^5.3.9",
|
"styled-components": "^6.0.7",
|
||||||
"threetwo-ui-typings": "^1.0.14",
|
"threetwo-ui-typings": "^1.0.14",
|
||||||
"vite-plugin-html": "^3.2.0",
|
"vite-plugin-html": "^3.2.0",
|
||||||
"websocket": "^1.0.34",
|
"websocket": "^1.0.34"
|
||||||
"xregexp": "^5.0.2"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "^7.13.10",
|
"@storybook/addon-essentials": "^7.3.2",
|
||||||
"@babel/core": "^7.13.10",
|
"@storybook/addon-interactions": "^7.3.2",
|
||||||
"@babel/plugin-syntax-top-level-await": "^7.14.5",
|
"@storybook/addon-links": "^7.3.2",
|
||||||
"@babel/plugin-transform-runtime": "^7.13.15",
|
"@storybook/addon-mdx-gfm": "^7.3.2",
|
||||||
"@babel/preset-env": "^7.20.2",
|
"@storybook/addon-onboarding": "^1.0.8",
|
||||||
"@babel/preset-react": "^7.18.6",
|
"@storybook/blocks": "^7.3.2",
|
||||||
"@babel/preset-typescript": "^7.13.0",
|
"@storybook/react": "^7.3.2",
|
||||||
"@storybook/addon-essentials": "^7.0.0-rc.3",
|
"@storybook/react-vite": "^7.3.2",
|
||||||
"@storybook/addon-interactions": "^7.0.0-rc.3",
|
"@storybook/testing-library": "^0.2.0",
|
||||||
"@storybook/addon-links": "^7.0.0-rc.3",
|
|
||||||
"@storybook/blocks": "^7.0.0-rc.3",
|
|
||||||
"@storybook/react": "^7.0.0-rc.3",
|
|
||||||
"@storybook/react-vite": "^7.0.0-rc.3",
|
|
||||||
"@storybook/testing-library": "^0.0.14-next.1",
|
|
||||||
"@tsconfig/node14": "^1.0.0",
|
"@tsconfig/node14": "^1.0.0",
|
||||||
"@types/ellipsize": "^0.1.1",
|
"@types/ellipsize": "^0.1.1",
|
||||||
"@types/express": "^4.17.8",
|
"@types/express": "^4.17.8",
|
||||||
@@ -110,7 +100,7 @@
|
|||||||
"@types/react-redux": "^7.1.25",
|
"@types/react-redux": "^7.1.25",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.17.0",
|
"@typescript-eslint/eslint-plugin": "^4.17.0",
|
||||||
"@typescript-eslint/parser": "^4.17.0",
|
"@typescript-eslint/parser": "^4.17.0",
|
||||||
"babel-eslint": "^10.0.0",
|
"babel-plugin-styled-components": "^2.1.4",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"bulma": "^0.9.4",
|
"bulma": "^0.9.4",
|
||||||
"eslint": "^7.22.0",
|
"eslint": "^7.22.0",
|
||||||
@@ -122,19 +112,20 @@
|
|||||||
"eslint-plugin-jsx-a11y": "^6.0.3",
|
"eslint-plugin-jsx-a11y": "^6.0.3",
|
||||||
"eslint-plugin-prettier": "^3.3.1",
|
"eslint-plugin-prettier": "^3.3.1",
|
||||||
"eslint-plugin-react": "^7.22.0",
|
"eslint-plugin-react": "^7.22.0",
|
||||||
"eslint-plugin-storybook": "^0.6.11",
|
"eslint-plugin-storybook": "^0.6.13",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"install": "^0.13.0",
|
"install": "^0.13.0",
|
||||||
"jest": "^26.6.3",
|
"jest": "^29.6.3",
|
||||||
"nodemon": "^1.17.3",
|
"nodemon": "^3.0.1",
|
||||||
"npm": "^8.11.0",
|
"npm": "^8.11.0",
|
||||||
"prettier": "^2.2.1",
|
"prettier": "^2.2.1",
|
||||||
"react-refresh": "^0.14.0",
|
"react-refresh": "^0.14.0",
|
||||||
"rimraf": "^4.1.3",
|
"rimraf": "^4.1.3",
|
||||||
"sass": "^1.58.1",
|
"sass": "^1.66.1",
|
||||||
"storybook": "^7.0.0-rc.3",
|
"storybook": "^7.3.2",
|
||||||
"tslint": "^6.1.3",
|
"tslint": "^6.1.3",
|
||||||
"typescript": "^5.0.2",
|
"typescript": "^5.1.6",
|
||||||
"vite": "^4.2.3"
|
"vite": "^4.4.9"
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ export const getWeeklyPullList = (options) => async (dispatch) => {
|
|||||||
dispatch({
|
dispatch({
|
||||||
type: CV_WEEKLY_PULLLIST_CALL_IN_PROGRESS,
|
type: CV_WEEKLY_PULLLIST_CALL_IN_PROGRESS,
|
||||||
});
|
});
|
||||||
|
|
||||||
await cachedAxios(`${COMICVINE_SERVICE_URI}/getWeeklyPullList`, {
|
await cachedAxios(`${COMICVINE_SERVICE_URI}/getWeeklyPullList`, {
|
||||||
method: "get",
|
method: "get",
|
||||||
params: options,
|
params: options,
|
||||||
|
|||||||
@@ -4,13 +4,12 @@ import {
|
|||||||
COMICVINE_SERVICE_URI,
|
COMICVINE_SERVICE_URI,
|
||||||
IMAGETRANSFORMATION_SERVICE_BASE_URI,
|
IMAGETRANSFORMATION_SERVICE_BASE_URI,
|
||||||
LIBRARY_SERVICE_BASE_URI,
|
LIBRARY_SERVICE_BASE_URI,
|
||||||
LIBRARY_SERVICE_HOST,
|
|
||||||
SEARCH_SERVICE_BASE_URI,
|
SEARCH_SERVICE_BASE_URI,
|
||||||
|
JOB_QUEUE_SERVICE_BASE_URI,
|
||||||
} from "../constants/endpoints";
|
} from "../constants/endpoints";
|
||||||
import {
|
import {
|
||||||
IMS_COMIC_BOOK_GROUPS_FETCHED,
|
IMS_COMIC_BOOK_GROUPS_FETCHED,
|
||||||
IMS_COMIC_BOOK_GROUPS_CALL_IN_PROGRESS,
|
IMS_COMIC_BOOK_GROUPS_CALL_IN_PROGRESS,
|
||||||
IMS_COMIC_BOOK_GROUPS_CALL_FAILED,
|
|
||||||
IMS_RECENT_COMICS_FETCHED,
|
IMS_RECENT_COMICS_FETCHED,
|
||||||
IMS_WANTED_COMICS_FETCHED,
|
IMS_WANTED_COMICS_FETCHED,
|
||||||
CV_API_CALL_IN_PROGRESS,
|
CV_API_CALL_IN_PROGRESS,
|
||||||
@@ -22,23 +21,38 @@ import {
|
|||||||
LS_IMPORT,
|
LS_IMPORT,
|
||||||
IMG_ANALYSIS_CALL_IN_PROGRESS,
|
IMG_ANALYSIS_CALL_IN_PROGRESS,
|
||||||
IMG_ANALYSIS_DATA_FETCH_SUCCESS,
|
IMG_ANALYSIS_DATA_FETCH_SUCCESS,
|
||||||
IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_SUCCESS,
|
|
||||||
IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_IN_PROGRESS,
|
IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_IN_PROGRESS,
|
||||||
SS_SEARCH_RESULTS_FETCHED,
|
SS_SEARCH_RESULTS_FETCHED,
|
||||||
SS_SEARCH_IN_PROGRESS,
|
SS_SEARCH_IN_PROGRESS,
|
||||||
FILEOPS_STATE_RESET,
|
FILEOPS_STATE_RESET,
|
||||||
LS_IMPORT_CALL_IN_PROGRESS,
|
LS_IMPORT_CALL_IN_PROGRESS,
|
||||||
LS_TOGGLE_IMPORT_QUEUE,
|
|
||||||
SS_SEARCH_FAILED,
|
SS_SEARCH_FAILED,
|
||||||
SS_SEARCH_RESULTS_FETCHED_SPECIAL,
|
SS_SEARCH_RESULTS_FETCHED_SPECIAL,
|
||||||
WANTED_COMICS_FETCHED,
|
WANTED_COMICS_FETCHED,
|
||||||
VOLUMES_FETCHED,
|
VOLUMES_FETCHED,
|
||||||
CV_WEEKLY_PULLLIST_FETCHED,
|
LIBRARY_SERVICE_HEALTH,
|
||||||
|
LS_SET_QUEUE_STATUS,
|
||||||
|
LS_IMPORT_JOB_STATISTICS_FETCHED,
|
||||||
} from "../constants/action-types";
|
} from "../constants/action-types";
|
||||||
import { success } from "react-notification-system-redux";
|
import { success } from "react-notification-system-redux";
|
||||||
|
|
||||||
import { isNil, map } from "lodash";
|
import { isNil } from "lodash";
|
||||||
|
|
||||||
|
export const getServiceStatus = (serviceName?: string) => async (dispatch) => {
|
||||||
|
axios
|
||||||
|
.request({
|
||||||
|
url: `${LIBRARY_SERVICE_BASE_URI}/getHealthInformation`,
|
||||||
|
method: "GET",
|
||||||
|
transformResponse: (r: string) => JSON.parse(r),
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
const { data } = response;
|
||||||
|
dispatch({
|
||||||
|
type: LIBRARY_SERVICE_HEALTH,
|
||||||
|
status: data,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
export async function walkFolder(path: string): Promise<Array<IFolderData>> {
|
export async function walkFolder(path: string): Promise<Array<IFolderData>> {
|
||||||
return axios
|
return axios
|
||||||
.request<Array<IFolderData>>({
|
.request<Array<IFolderData>>({
|
||||||
@@ -74,19 +88,38 @@ export const fetchComicBookMetadata = () => async (dispatch) => {
|
|||||||
// autoDismiss: 0,
|
// autoDismiss: 0,
|
||||||
// }),
|
// }),
|
||||||
// );
|
// );
|
||||||
|
const sessionId = localStorage.getItem("sessionId");
|
||||||
dispatch({
|
dispatch({
|
||||||
type: LS_IMPORT,
|
type: LS_IMPORT,
|
||||||
meta: { remote: true },
|
});
|
||||||
data: {},
|
|
||||||
|
await axios.request({
|
||||||
|
url: `${LIBRARY_SERVICE_BASE_URI}/newImport`,
|
||||||
|
method: "POST",
|
||||||
|
data: { sessionId },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
export const toggleImportQueueStatus = (options) => async (dispatch) => {
|
|
||||||
|
export const getImportJobResultStatistics = () => async (dispatch) => {
|
||||||
|
const result = await axios.request({
|
||||||
|
url: `${JOB_QUEUE_SERVICE_BASE_URI}/getJobResultStatistics`,
|
||||||
|
method: "GET",
|
||||||
|
});
|
||||||
dispatch({
|
dispatch({
|
||||||
type: LS_TOGGLE_IMPORT_QUEUE,
|
type: LS_IMPORT_JOB_STATISTICS_FETCHED,
|
||||||
meta: { remote: true },
|
data: result.data,
|
||||||
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
|
* Fetches comic book metadata for various types
|
||||||
* @return metadata for the comic book object categories
|
* @return metadata for the comic book object categories
|
||||||
|
|||||||
@@ -15,13 +15,14 @@ export const AirDCPPSettingsForm = (): ReactElement => {
|
|||||||
try {
|
try {
|
||||||
if (!isUndefined(hostname)) {
|
if (!isUndefined(hostname)) {
|
||||||
const matches = hostname.match(hostnameRegex);
|
const matches = hostname.match(hostnameRegex);
|
||||||
return (isNil(matches) && matches.length !== 0) ? hostname : "Invalid hostname; it should not contain special characters";
|
return isNil(matches) && matches.length !== 0
|
||||||
|
? hostname
|
||||||
|
: "Invalid hostname; it should not contain special characters";
|
||||||
}
|
}
|
||||||
}
|
} catch {
|
||||||
catch {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const onSubmit = useCallback(async (values) => {
|
const onSubmit = useCallback(async (values) => {
|
||||||
try {
|
try {
|
||||||
@@ -39,7 +40,7 @@ export const AirDCPPSettingsForm = (): ReactElement => {
|
|||||||
airDCPPSettings.setSettings({});
|
airDCPPSettings.setSettings({});
|
||||||
dispatch(deleteSettings());
|
dispatch(deleteSettings());
|
||||||
}, []);
|
}, []);
|
||||||
const validate = async () => { };
|
const validate = async () => {};
|
||||||
const initFormData = !isUndefined(
|
const initFormData = !isUndefined(
|
||||||
airDCPPSettings.airDCPPState.settings.directConnect,
|
airDCPPSettings.airDCPPState.settings.directConnect,
|
||||||
)
|
)
|
||||||
@@ -67,13 +68,20 @@ export const AirDCPPSettingsForm = (): ReactElement => {
|
|||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<div className="control is-expanded">
|
<div className="control is-expanded">
|
||||||
<Field
|
<Field name="hostname" validate={hostValidator}>
|
||||||
name="hostname"
|
|
||||||
validate={hostValidator}>
|
|
||||||
{({ input, meta }) => (
|
{({ input, meta }) => (
|
||||||
<div>
|
<div>
|
||||||
<input {...input} type="text" placeholder="AirDC++ hostname" className="input" />
|
<input
|
||||||
{meta.error && meta.touched && <span className="is-size-7 has-text-danger">{meta.error}</span>}
|
{...input}
|
||||||
|
type="text"
|
||||||
|
placeholder="AirDC++ hostname"
|
||||||
|
className="input"
|
||||||
|
/>
|
||||||
|
{meta.error && meta.touched && (
|
||||||
|
<span className="is-size-7 has-text-danger">
|
||||||
|
{meta.error}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React, { ReactElement, useContext, useEffect } from "react";
|
import React, { ReactElement, useContext, useEffect } from "react";
|
||||||
import Dashboard from "./Dashboard/Dashboard";
|
import Dashboard from "./Dashboard/Dashboard";
|
||||||
|
|
||||||
import Import from "./Import";
|
import Import from "./Import";
|
||||||
import { ComicDetailContainer } from "./ComicDetail/ComicDetailContainer";
|
import { ComicDetailContainer } from "./ComicDetail/ComicDetailContainer";
|
||||||
import TabulatedContentContainer from "./Library/TabulatedContentContainer";
|
import TabulatedContentContainer from "./Library/TabulatedContentContainer";
|
||||||
@@ -17,7 +16,9 @@ import {
|
|||||||
AirDCPPSocketContextProvider,
|
AirDCPPSocketContextProvider,
|
||||||
AirDCPPSocketContext,
|
AirDCPPSocketContext,
|
||||||
} from "../context/AirDCPPSocket";
|
} from "../context/AirDCPPSocket";
|
||||||
import { isEmpty, isUndefined } from "lodash";
|
import { SocketIOProvider } from "../context/SocketIOContext";
|
||||||
|
import socketIOConnectionInstance from "../shared/socket.io/instance";
|
||||||
|
import { isEmpty, isNil, isUndefined } from "lodash";
|
||||||
import {
|
import {
|
||||||
AIRDCPP_DOWNLOAD_PROGRESS_TICK,
|
AIRDCPP_DOWNLOAD_PROGRESS_TICK,
|
||||||
LS_SINGLE_IMPORT,
|
LS_SINGLE_IMPORT,
|
||||||
@@ -95,7 +96,26 @@ const AirDCPPSocketComponent = (): ReactElement => {
|
|||||||
return <></>;
|
return <></>;
|
||||||
};
|
};
|
||||||
export const App = (): ReactElement => {
|
export const App = (): ReactElement => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
useEffect(() => {
|
||||||
|
// Check if there is a sessionId in localStorage
|
||||||
|
const sessionId = localStorage.getItem("sessionId");
|
||||||
|
if (!isNil(sessionId)) {
|
||||||
|
// Resume the session
|
||||||
|
dispatch({
|
||||||
|
type: "RESUME_SESSION",
|
||||||
|
meta: { remote: true },
|
||||||
|
session: { sessionId },
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Inititalize the session and persist the sessionId to localStorage
|
||||||
|
socketIOConnectionInstance.on("sessionInitialized", (sessionId) => {
|
||||||
|
localStorage.setItem("sessionId", sessionId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
return (
|
return (
|
||||||
|
<SocketIOProvider socket={socketIOConnectionInstance}>
|
||||||
<AirDCPPSocketContextProvider>
|
<AirDCPPSocketContextProvider>
|
||||||
<div>
|
<div>
|
||||||
<AirDCPPSocketComponent />
|
<AirDCPPSocketComponent />
|
||||||
@@ -134,6 +154,7 @@ export const App = (): ReactElement => {
|
|||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
</AirDCPPSocketContextProvider>
|
</AirDCPPSocketContextProvider>
|
||||||
|
</SocketIOProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,9 @@ export const ComicInfoXML = (data): ReactElement => {
|
|||||||
<dd>
|
<dd>
|
||||||
<span className="is-size-7">{json.notes[0]}</span>
|
<span className="is-size-7">{json.notes[0]}</span>
|
||||||
</dd>
|
</dd>
|
||||||
<dd className="mt-1 mb-1">{json.summary[0]}</dd>
|
<dd className="mt-1 mb-1">
|
||||||
|
{!isUndefined(json.summary) && json.summary[0]}
|
||||||
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
} from "../../actions/fileops.actions";
|
} from "../../actions/fileops.actions";
|
||||||
import { getLibraryStatistics } from "../../actions/comicinfo.actions";
|
import { getLibraryStatistics } from "../../actions/comicinfo.actions";
|
||||||
import { isEmpty, isNil } from "lodash";
|
import { isEmpty, isNil } from "lodash";
|
||||||
|
import Header from "../Header";
|
||||||
|
|
||||||
export const Dashboard = (): ReactElement => {
|
export const Dashboard = (): ReactElement => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
@@ -43,7 +44,7 @@ export const Dashboard = (): ReactElement => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const recentComics = useSelector(
|
const recentComics = useSelector(
|
||||||
(state: RootState) => state.fileOps.recentComics
|
(state: RootState) => state.fileOps.recentComics,
|
||||||
);
|
);
|
||||||
const wantedComics = useSelector(
|
const wantedComics = useSelector(
|
||||||
(state: RootState) => state.fileOps.wantedComics,
|
(state: RootState) => state.fileOps.wantedComics,
|
||||||
@@ -64,6 +65,36 @@ export const Dashboard = (): ReactElement => {
|
|||||||
<>
|
<>
|
||||||
{/* Pull List */}
|
{/* Pull List */}
|
||||||
<PullList issues={recentComics} />
|
<PullList issues={recentComics} />
|
||||||
|
<>
|
||||||
|
<div className="content mt-6">
|
||||||
|
<Header
|
||||||
|
headerContent="Import Activity"
|
||||||
|
subHeaderContent="Results aggregated from the last import"
|
||||||
|
iconClassNames="fa-solid fa-file-invoice mr-2"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table className="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<abbr title="Position">Pos</abbr>
|
||||||
|
</th>
|
||||||
|
<th>Team</th>
|
||||||
|
<th>
|
||||||
|
<abbr title="Played">Pld</abbr>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>1</th>
|
||||||
|
<td>38</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</>
|
||||||
|
|
||||||
{/* Stats */}
|
{/* Stats */}
|
||||||
{!isEmpty(libraryStatistics) && (
|
{!isEmpty(libraryStatistics) && (
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { isNil, map } from "lodash";
|
import { isNil, map } from "lodash";
|
||||||
import React, { createRef, ReactElement, useCallback, useEffect } from "react";
|
import React, { createRef, ReactElement, useCallback, useEffect } from "react";
|
||||||
import Card from "../Carda";
|
import Card from "../Carda";
|
||||||
|
import Header from "../Header";
|
||||||
import Masonry from "react-masonry-css";
|
import Masonry from "react-masonry-css";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { getWeeklyPullList } from "../../actions/comicinfo.actions";
|
import { getWeeklyPullList } from "../../actions/comicinfo.actions";
|
||||||
@@ -20,7 +21,7 @@ export const PullList = ({ issues }: PullListProps): ReactElement => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(
|
dispatch(
|
||||||
getWeeklyPullList({
|
getWeeklyPullList({
|
||||||
startDate: "2023-5-25",
|
startDate: "2023-8-9",
|
||||||
pageSize: "15",
|
pageSize: "15",
|
||||||
currentPage: "1",
|
currentPage: "1",
|
||||||
}),
|
}),
|
||||||
@@ -88,12 +89,9 @@ export const PullList = ({ issues }: PullListProps): ReactElement => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="content">
|
<div className="content">
|
||||||
<h4 className="title is-4">
|
<Header headerContent="Discover"
|
||||||
<i className="fa-solid fa-splotch"></i> Discover
|
subHeaderContent="Pull List aggregated for the week from League Of Comic Geeks"
|
||||||
</h4>
|
iconClassNames="fa-solid fa-splotch mr-2"/>
|
||||||
<p className="subtitle is-7">
|
|
||||||
Pull List aggregated for the week from League Of Comic Geeks
|
|
||||||
</p>
|
|
||||||
<div className="field is-grouped">
|
<div className="field is-grouped">
|
||||||
{/* select week */}
|
{/* select week */}
|
||||||
<div className="control">
|
<div className="control">
|
||||||
@@ -127,7 +125,7 @@ export const PullList = ({ issues }: PullListProps): ReactElement => {
|
|||||||
<Slider {...settings} ref={(c) => (sliderRef = c)}>
|
<Slider {...settings} ref={(c) => (sliderRef = c)}>
|
||||||
{!isNil(pullList) &&
|
{!isNil(pullList) &&
|
||||||
pullList &&
|
pullList &&
|
||||||
map(pullList, ({issue}, idx) => {
|
map(pullList, ({ issue }, idx) => {
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
key={idx}
|
key={idx}
|
||||||
|
|||||||
@@ -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,7 +72,8 @@ 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 (
|
||||||
|
<div key={idx}>
|
||||||
<MetadataPanel
|
<MetadataPanel
|
||||||
data={bundle}
|
data={bundle}
|
||||||
imageStyle={{ maxWidth: 80 }}
|
imageStyle={{ maxWidth: 80 }}
|
||||||
@@ -66,7 +83,8 @@ export const Downloads = (props: IDownloadsProps): ReactElement => {
|
|||||||
maxWidth: 400,
|
maxWidth: 400,
|
||||||
padding: 0,
|
padding: 0,
|
||||||
margin: "0 0 8px 0",
|
margin: "0 0 8px 0",
|
||||||
}} />
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<table className="table is-size-7">
|
<table className="table is-size-7">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -78,23 +96,35 @@ export const Downloads = (props: IDownloadsProps): ReactElement => {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{bundle.acquisition.directconnect.downloads.map((bundle, idx) => {
|
{bundle.acquisition.directconnect.downloads.map(
|
||||||
return (<tr key={idx}>
|
(bundle, idx) => {
|
||||||
|
return (
|
||||||
|
<tr key={idx}>
|
||||||
<td>{bundle.name}</td>
|
<td>{bundle.name}</td>
|
||||||
<td>{bundle.size}</td>
|
<td>{bundle.size}</td>
|
||||||
<td>{bundle.type.str}</td>
|
<td>{bundle.type.str}</td>
|
||||||
<td><span className="tag is-warning">{bundle.bundleId}</span></td>
|
<td>
|
||||||
</tr>)
|
<span className="tag is-warning">
|
||||||
})}
|
{bundle.bundleId}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{/* <pre>{JSON.stringify(bundle.acquisition.directconnect.downloads, null, 2)}</pre> */}
|
{/* <pre>{JSON.stringify(bundle.acquisition.directconnect.downloads, null, 2)}</pre> */}
|
||||||
</div>
|
</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;
|
||||||
|
|||||||
20
src/client/components/Header.tsx
Normal file
20
src/client/components/Header.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import React, { ReactElement } from "react";
|
||||||
|
|
||||||
|
type IHeaderProps = {
|
||||||
|
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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Header;
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
import React, { ReactElement, useCallback, useContext, useState } from "react";
|
import React, { ReactElement, useCallback, useEffect } from "react";
|
||||||
import { useSelector, useDispatch } from "react-redux";
|
import { useSelector, useDispatch } from "react-redux";
|
||||||
import {
|
import {
|
||||||
fetchComicBookMetadata,
|
fetchComicBookMetadata,
|
||||||
toggleImportQueueStatus,
|
getImportJobResultStatistics,
|
||||||
|
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 { format } from "date-fns";
|
||||||
import Loader from "react-loader-spinner";
|
import Loader from "react-loader-spinner";
|
||||||
|
import { isEmpty, isNil, isUndefined } from "lodash";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
matches?: unknown;
|
matches?: unknown;
|
||||||
@@ -29,102 +32,203 @@ interface IProps {
|
|||||||
|
|
||||||
export const Import = (props: IProps): ReactElement => {
|
export const Import = (props: IProps): ReactElement => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const libraryQueueResults = useSelector(
|
const successfulImportJobCount = useSelector(
|
||||||
(state: RootState) => state.fileOps.librarySearchResultCount,
|
(state: RootState) => state.fileOps.successfulJobCount,
|
||||||
|
);
|
||||||
|
const failedImportJobCount = useSelector(
|
||||||
|
(state: RootState) => state.fileOps.failedJobCount,
|
||||||
|
);
|
||||||
|
|
||||||
|
const lastQueueJob = useSelector(
|
||||||
|
(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(false);
|
|
||||||
|
const allImportJobResults = useSelector(
|
||||||
|
(state: RootState) => state.fileOps.importJobStatistics,
|
||||||
|
);
|
||||||
|
|
||||||
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 === false) {
|
dispatch(setQueueControl(queueAction, queueStatus));
|
||||||
dispatch(toggleImportQueueStatus({ action: "resume" }));
|
},
|
||||||
} else if (isImportQueuePaused === true) {
|
[],
|
||||||
dispatch(toggleImportQueueStatus({ action: "pause" }));
|
);
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(getImportJobResultStatistics());
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
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 Import
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
const playIconText = (
|
|
||||||
<>
|
|
||||||
<i className="fa-solid fa-play mr-2"></i> Resume Import
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<section className="section is-small">
|
<section className="section is-small">
|
||||||
<h1 className="title">Import</h1>
|
<h1 className="title">Import Comics</h1>
|
||||||
<article className="message is-dark">
|
<article className="message is-dark">
|
||||||
<div className="message-body">
|
<div className="message-body">
|
||||||
<p className="mb-2">
|
<p className="mb-2">
|
||||||
<span className="tag is-medium is-info is-light">
|
<span className="tag is-medium is-info is-light">
|
||||||
Import Only
|
Import Comics
|
||||||
</span>
|
</span>
|
||||||
will add comics identified from the mapped folder into the local
|
will add comics identified from the mapped folder into ThreeTwo's
|
||||||
db.
|
database.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span className="tag is-medium is-info is-light">
|
Metadata from ComicInfo.xml, if present, will also be extracted.
|
||||||
Import and Tag
|
</p>
|
||||||
</span>
|
<p>
|
||||||
will scan the ComicVine, shortboxed APIs and import comics from
|
This process could take a while, if you have a lot of comics, or
|
||||||
the mapped folder with the additional metadata.
|
are importing over a network connection.
|
||||||
</p>
|
</p>
|
||||||
</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}
|
||||||
>
|
>
|
||||||
<span className="icon">
|
<span className="icon">
|
||||||
<i className="fas fa-file-import"></i>
|
<i className="fas fa-file-import"></i>
|
||||||
</span>
|
</span>
|
||||||
<span>Import Only</span>
|
<span>Start Import</span>
|
||||||
</button>
|
|
||||||
|
|
||||||
<button className="button is-medium">
|
|
||||||
<span className="icon">
|
|
||||||
<i className="fas fa-tag"></i>
|
|
||||||
</span>
|
|
||||||
<span>Import and Tag</span>
|
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
<div className="columns is-multiline">
|
{libraryQueueImportStatus !== "drained" &&
|
||||||
<div className="column is-one-fifth">
|
!isUndefined(libraryQueueImportStatus) && (
|
||||||
<div className="box control-palette">
|
<>
|
||||||
|
<table className="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Completed Jobs</th>
|
||||||
|
<th>Failed Jobs</th>
|
||||||
|
<th>Queue Controls</th>
|
||||||
|
<th>Queue Status</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
{successfulImportJobCount > 0 && (
|
||||||
|
<div className="box has-background-success-light has-text-centered">
|
||||||
<span className="is-size-2 has-text-weight-bold">
|
<span className="is-size-2 has-text-weight-bold">
|
||||||
{JSON.stringify(libraryQueueResults, null, 2)}
|
{successfulImportJobCount}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="is-half">
|
)}
|
||||||
<div className="content">
|
</th>
|
||||||
<div className="control">
|
<td>
|
||||||
<button
|
{failedImportJobCount > 0 && (
|
||||||
className="button is-warning is-light"
|
<div className="box has-background-danger has-text-centered">
|
||||||
onClick={toggleImport}
|
<span className="is-size-2 has-text-weight-bold">
|
||||||
>
|
{failedImportJobCount}
|
||||||
{!isImportQueuePaused ? pauseIconText : playIconText}
|
</span>
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>{renderQueueControls(libraryQueueImportStatus)}</td>
|
||||||
|
<td>
|
||||||
|
{libraryQueueImportStatus !== undefined ? (
|
||||||
|
<span className="tag is-warning">
|
||||||
|
{libraryQueueImportStatus}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
Imported{" "}
|
||||||
|
<span className="has-text-weight-bold">{lastQueueJob}</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Past imports */}
|
||||||
|
|
||||||
|
<h3 className="subtitle is-4 mt-5">Past Imports</h3>
|
||||||
|
<table className="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Time Started</th>
|
||||||
|
<th>Session Id</th>
|
||||||
|
<th>Imported</th>
|
||||||
|
<th>Failed</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{allImportJobResults.map((jobResult, id) => {
|
||||||
|
return (
|
||||||
|
<tr key={id}>
|
||||||
|
<td>
|
||||||
|
{format(
|
||||||
|
new Date(jobResult.earliestTimestamp),
|
||||||
|
"EEEE, hh:mma, do LLLL Y",
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span className="tag is-warning">
|
||||||
|
{jobResult.sessionId}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span className="tag is-success">
|
||||||
|
{jobResult.completedJobs}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span className="tag is-danger">
|
||||||
|
{jobResult.failedJobs}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export const Library = (): ReactElement => {
|
|||||||
|
|
||||||
const ComicInfoXML = (value) => {
|
const ComicInfoXML = (value) => {
|
||||||
return value.data ? (
|
return value.data ? (
|
||||||
<div className="comicvine-metadata">
|
<div className="comicvine-metadata mt-3">
|
||||||
<dl>
|
<dl>
|
||||||
<span className="tags has-addons is-size-7">
|
<span className="tags has-addons is-size-7">
|
||||||
<span className="tag">Series</span>
|
<span className="tag">Series</span>
|
||||||
|
|||||||
@@ -20,6 +20,25 @@ const Navbar: React.FunctionComponent = (props) => {
|
|||||||
const socketDisconnectionReason = useSelector(
|
const socketDisconnectionReason = useSelector(
|
||||||
(state: RootState) => state.airdcpp.socketDisconnectionReason,
|
(state: RootState) => state.airdcpp.socketDisconnectionReason,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Import-related selector hooks
|
||||||
|
const successfulImportJobCount = useSelector(
|
||||||
|
(state: RootState) => state.fileOps.successfulJobCount,
|
||||||
|
);
|
||||||
|
const failedImportJobCount = useSelector(
|
||||||
|
(state: RootState) => state.fileOps.failedJobCount,
|
||||||
|
);
|
||||||
|
|
||||||
|
const lastQueueJob = useSelector(
|
||||||
|
(state: RootState) => state.fileOps.lastQueueJob,
|
||||||
|
);
|
||||||
|
const libraryQueueImportStatus = useSelector(
|
||||||
|
(state: RootState) => state.fileOps.LSQueueImportStatus,
|
||||||
|
);
|
||||||
|
|
||||||
|
const allImportJobResults = useSelector(
|
||||||
|
(state: RootState) => state.fileOps.importJobStatistics,
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<nav className="navbar is-fixed-top">
|
<nav className="navbar is-fixed-top">
|
||||||
<div className="navbar-brand">
|
<div className="navbar-brand">
|
||||||
@@ -88,9 +107,43 @@ const Navbar: React.FunctionComponent = (props) => {
|
|||||||
<div className="navbar-dropdown is-right is-boxed">
|
<div className="navbar-dropdown is-right is-boxed">
|
||||||
<a className="navbar-item">
|
<a className="navbar-item">
|
||||||
<DownloadProgressTick data={downloadProgressTick} />
|
<DownloadProgressTick data={downloadProgressTick} />
|
||||||
</a> </div>
|
</a>
|
||||||
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{!isUndefined(libraryQueueImportStatus) &&
|
||||||
|
location.hash !== "#/import" ? (
|
||||||
|
<div className="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a className="navbar-link is-arrowless">
|
||||||
|
<i className="fa-solid fa-file-import has-text-warning-dark"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div className="navbar-dropdown is-right is-boxed">
|
||||||
|
<a className="navbar-item">
|
||||||
|
<ul>
|
||||||
|
{successfulImportJobCount > 0 ? (
|
||||||
|
<li className="mb-2">
|
||||||
|
<span className="tag is-success mr-2">
|
||||||
|
{successfulImportJobCount}
|
||||||
|
</span>
|
||||||
|
imported.
|
||||||
|
</li>
|
||||||
|
) : null}
|
||||||
|
{failedImportJobCount > 0 ? (
|
||||||
|
<li>
|
||||||
|
<span className="tag is-danger mr-2">
|
||||||
|
{failedImportJobCount}
|
||||||
|
</span>
|
||||||
|
failed to import.
|
||||||
|
</li>
|
||||||
|
) : null}
|
||||||
|
</ul>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
{/* AirDC++ socket connection status */}
|
{/* AirDC++ socket connection status */}
|
||||||
<div className="navbar-item has-dropdown is-hoverable">
|
<div className="navbar-item has-dropdown is-hoverable">
|
||||||
{airDCPPSocketConnectionStatus ? (
|
{airDCPPSocketConnectionStatus ? (
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const PullList = (): ReactElement => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(
|
dispatch(
|
||||||
getWeeklyPullList({
|
getWeeklyPullList({
|
||||||
startDate: "2022-11-15",
|
startDate: "2023-7-28",
|
||||||
pageSize: "15",
|
pageSize: "15",
|
||||||
currentPage: "1",
|
currentPage: "1",
|
||||||
}),
|
}),
|
||||||
|
|||||||
25
src/client/components/ServiceStatuses/ServiceStatuses.tsx
Normal file
25
src/client/components/ServiceStatuses/ServiceStatuses.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import React, { ReactElement } from "react";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { getServiceStatus } from "../../actions/fileops.actions";
|
||||||
|
|
||||||
|
export const ServiceStatuses = (): ReactElement => {
|
||||||
|
const serviceStatus = useSelector(
|
||||||
|
(state: RootState) => state.fileOps.libraryServiceStatus,
|
||||||
|
);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(getServiceStatus());
|
||||||
|
}, []);
|
||||||
|
return (
|
||||||
|
<div className="is-clearfix">
|
||||||
|
<div className="mt-4">
|
||||||
|
<h3 className="title">Core Services</h3>
|
||||||
|
<h6 className="subtitle has-text-grey-light">
|
||||||
|
Statuses for core services
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
<pre>{JSON.stringify(serviceStatus, null, 2)}</pre>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -2,6 +2,7 @@ import React, { useState, ReactElement } from "react";
|
|||||||
import { AirDCPPSettingsForm } from "./AirDCPPSettings/AirDCPPSettingsForm";
|
import { AirDCPPSettingsForm } from "./AirDCPPSettings/AirDCPPSettingsForm";
|
||||||
import { AirDCPPHubsForm } from "./AirDCPPSettings/AirDCPPHubsForm";
|
import { AirDCPPHubsForm } from "./AirDCPPSettings/AirDCPPHubsForm";
|
||||||
import { SystemSettingsForm } from "./SystemSettings/SystemSettingsForm";
|
import { SystemSettingsForm } from "./SystemSettings/SystemSettingsForm";
|
||||||
|
import { ServiceStatuses } from "./ServiceStatuses/ServiceStatuses";
|
||||||
import settingsObject from "../constants/settings/settingsMenu.json";
|
import settingsObject from "../constants/settings/settingsMenu.json";
|
||||||
import { isUndefined, map } from "lodash";
|
import { isUndefined, map } from "lodash";
|
||||||
|
|
||||||
@@ -22,6 +23,10 @@ export const Settings = (props: ISettingsProps): ReactElement => {
|
|||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "core-service",
|
||||||
|
content: <ServiceStatuses />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "flushdb",
|
id: "flushdb",
|
||||||
content: (
|
content: (
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ export const IMS_RAW_IMPORT_FAILED = "IMS_RAW_IMPORT_FAILED";
|
|||||||
export const LS_IMPORT_CALL_IN_PROGRESS = "LS_IMPORT_CALL_IN_PROGRESS";
|
export const LS_IMPORT_CALL_IN_PROGRESS = "LS_IMPORT_CALL_IN_PROGRESS";
|
||||||
// Library import bull mq queue control
|
// Library import bull mq queue control
|
||||||
export const LS_TOGGLE_IMPORT_QUEUE = "LS_TOGGLE_IMPORT_QUEUE";
|
export const LS_TOGGLE_IMPORT_QUEUE = "LS_TOGGLE_IMPORT_QUEUE";
|
||||||
export const LS_QUEUE_DRAINED = "LS_QUEUE_DRAINED";
|
|
||||||
|
|
||||||
// ComicVine Metadata
|
// ComicVine Metadata
|
||||||
export const IMS_CV_METADATA_IMPORT_CALL_IN_PROGRESS =
|
export const IMS_CV_METADATA_IMPORT_CALL_IN_PROGRESS =
|
||||||
@@ -51,11 +50,12 @@ export const IMS_COMIC_BOOK_GROUPS_CALL_IN_PROGRESS =
|
|||||||
"IMS_COMIC_BOOK_GROUPS_CALL_IN_PROGRESS";
|
"IMS_COMIC_BOOK_GROUPS_CALL_IN_PROGRESS";
|
||||||
export const IMS_COMIC_BOOK_GROUPS_CALL_FAILED =
|
export const IMS_COMIC_BOOK_GROUPS_CALL_FAILED =
|
||||||
"IMS_COMIC_BOOK_GROUPS_CALL_FAILED";
|
"IMS_COMIC_BOOK_GROUPS_CALL_FAILED";
|
||||||
export const VOLUMES_FETCHED="VOLUMES_FETCHED";
|
export const VOLUMES_FETCHED = "VOLUMES_FETCHED";
|
||||||
|
|
||||||
// search results from the Search service
|
// search results from the Search service
|
||||||
export const SS_SEARCH_RESULTS_FETCHED = "SS_SEARCH_RESULTS_FETCHED";
|
export const SS_SEARCH_RESULTS_FETCHED = "SS_SEARCH_RESULTS_FETCHED";
|
||||||
export const SS_SEARCH_RESULTS_FETCHED_SPECIAL = "SS_SEARCH_RESULTS_FETCHED_SPECIAL";
|
export const SS_SEARCH_RESULTS_FETCHED_SPECIAL =
|
||||||
|
"SS_SEARCH_RESULTS_FETCHED_SPECIAL";
|
||||||
export const SS_SEARCH_IN_PROGRESS = "SS_SEARCH_IN_PROGRESS";
|
export const SS_SEARCH_IN_PROGRESS = "SS_SEARCH_IN_PROGRESS";
|
||||||
export const SS_SEARCH_FAILED = "SS_SEARCH_FAILED";
|
export const SS_SEARCH_FAILED = "SS_SEARCH_FAILED";
|
||||||
|
|
||||||
@@ -124,10 +124,17 @@ export const AIRDCPP_TRANSFERS_FETCHED = "AIRDCPP_TRANSFERS_FETCHED";
|
|||||||
// Comics marked as "wanted"
|
// Comics marked as "wanted"
|
||||||
export const WANTED_COMICS_FETCHED = "WANTED_COMICS_FETCHED";
|
export const WANTED_COMICS_FETCHED = "WANTED_COMICS_FETCHED";
|
||||||
|
|
||||||
// LIBRARY SOCKET ENDPOINT
|
// LIBRARY Service import queue-related action types
|
||||||
export const LS_IMPORT = "LS_IMPORT";
|
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_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";
|
||||||
|
export const RESTORE_JOB_COUNTS_AFTER_SESSION_RESTORATION =
|
||||||
|
"RESTORE_JOB_COUNTS_AFTER_SESSION_RESTORATION";
|
||||||
|
export const LS_IMPORT_JOB_STATISTICS_FETCHED =
|
||||||
|
"LS_IMPORT_JOB_STATISTICS_FETCHED";
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
export const SETTINGS_CALL_IN_PROGRESS = "SETTINGS_CALL_IN_PROGRESS";
|
export const SETTINGS_CALL_IN_PROGRESS = "SETTINGS_CALL_IN_PROGRESS";
|
||||||
@@ -140,3 +147,6 @@ export const SETTINGS_DB_FLUSH_SUCCESS = "SETTINGS_DB_FLUSH_SUCCESS";
|
|||||||
export const METRON_DATA_FETCH_SUCCESS = "METRON_DATA_FETCH_SUCCESS";
|
export const METRON_DATA_FETCH_SUCCESS = "METRON_DATA_FETCH_SUCCESS";
|
||||||
export const METRON_DATA_FETCH_IN_PROGRESS = "METRON_DATA_FETCH_IN_PROGRESS";
|
export const METRON_DATA_FETCH_IN_PROGRESS = "METRON_DATA_FETCH_IN_PROGRESS";
|
||||||
export const METRON_DATA_FETCH_ERROR = "METRON_DATA_FETCH_ERROR";
|
export const METRON_DATA_FETCH_ERROR = "METRON_DATA_FETCH_ERROR";
|
||||||
|
|
||||||
|
// service health statuses
|
||||||
|
export const LIBRARY_SERVICE_HEALTH = "LIBRARY_SERVICE_HEALTH";
|
||||||
|
|||||||
@@ -77,3 +77,10 @@ export const SOCKET_BASE_URI = hostURIBuilder({
|
|||||||
port: "3001",
|
port: "3001",
|
||||||
apiPath: `/`,
|
apiPath: `/`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const JOB_QUEUE_SERVICE_BASE_URI = hostURIBuilder({
|
||||||
|
protocol: "http",
|
||||||
|
host: import.meta.env.UNDERLYING_HOSTNAME || "localhost",
|
||||||
|
port: "3000",
|
||||||
|
apiPath: `/api/jobqueue`,
|
||||||
|
});
|
||||||
|
|||||||
@@ -57,6 +57,21 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 4,
|
"id": 4,
|
||||||
|
"category": "statuses",
|
||||||
|
"displayName": "Service Status",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "core-service",
|
||||||
|
"displayName": "Core Services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "metadata-service",
|
||||||
|
"displayName": "Metadata Services"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
"category": "system",
|
"category": "system",
|
||||||
"displayName": "System",
|
"displayName": "System",
|
||||||
"children": [
|
"children": [
|
||||||
@@ -67,7 +82,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 5,
|
"id": 6,
|
||||||
"category": "acknowledgments",
|
"category": "acknowledgments",
|
||||||
"displayName": "Acknowledgments",
|
"displayName": "Acknowledgments",
|
||||||
"children": [
|
"children": [
|
||||||
|
|||||||
11
src/client/context/SocketIOContext.tsx
Normal file
11
src/client/context/SocketIOContext.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import React, { createContext } from "react";
|
||||||
|
|
||||||
|
export const SocketIOContext = createContext({});
|
||||||
|
|
||||||
|
export const SocketIOProvider = ({ children, socket }) => {
|
||||||
|
return (
|
||||||
|
<SocketIOContext.Provider value={socket}>
|
||||||
|
{children}
|
||||||
|
</SocketIOContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -15,10 +15,9 @@ import {
|
|||||||
IMS_COMIC_BOOK_GROUPS_FETCHED,
|
IMS_COMIC_BOOK_GROUPS_FETCHED,
|
||||||
IMS_COMIC_BOOK_GROUPS_CALL_FAILED,
|
IMS_COMIC_BOOK_GROUPS_CALL_FAILED,
|
||||||
IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_IN_PROGRESS,
|
IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_IN_PROGRESS,
|
||||||
IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_SUCCESS,
|
|
||||||
LS_IMPORT,
|
LS_IMPORT,
|
||||||
LS_COVER_EXTRACTED,
|
LS_COVER_EXTRACTED,
|
||||||
LS_QUEUE_DRAINED,
|
LS_COVER_EXTRACTION_FAILED,
|
||||||
LS_COMIC_ADDED,
|
LS_COMIC_ADDED,
|
||||||
IMG_ANALYSIS_CALL_IN_PROGRESS,
|
IMG_ANALYSIS_CALL_IN_PROGRESS,
|
||||||
IMG_ANALYSIS_DATA_FETCH_SUCCESS,
|
IMG_ANALYSIS_DATA_FETCH_SUCCESS,
|
||||||
@@ -30,6 +29,11 @@ import {
|
|||||||
SS_SEARCH_RESULTS_FETCHED_SPECIAL,
|
SS_SEARCH_RESULTS_FETCHED_SPECIAL,
|
||||||
VOLUMES_FETCHED,
|
VOLUMES_FETCHED,
|
||||||
COMICBOOK_EXTRACTION_SUCCESS,
|
COMICBOOK_EXTRACTION_SUCCESS,
|
||||||
|
LIBRARY_SERVICE_HEALTH,
|
||||||
|
LS_IMPORT_QUEUE_DRAINED,
|
||||||
|
LS_SET_QUEUE_STATUS,
|
||||||
|
RESTORE_JOB_COUNTS_AFTER_SESSION_RESTORATION,
|
||||||
|
LS_IMPORT_JOB_STATISTICS_FETCHED,
|
||||||
} 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";
|
||||||
@@ -40,6 +44,7 @@ const initialState = {
|
|||||||
SSCallInProgress: false,
|
SSCallInProgress: false,
|
||||||
imageAnalysisResults: {},
|
imageAnalysisResults: {},
|
||||||
comicBookExtractionInProgress: false,
|
comicBookExtractionInProgress: false,
|
||||||
|
LSQueueImportStatus: undefined,
|
||||||
comicBookMetadata: [],
|
comicBookMetadata: [],
|
||||||
comicVolumeGroups: [],
|
comicVolumeGroups: [],
|
||||||
isSocketConnected: false,
|
isSocketConnected: false,
|
||||||
@@ -55,11 +60,14 @@ const initialState = {
|
|||||||
libraryComics: [],
|
libraryComics: [],
|
||||||
volumes: [],
|
volumes: [],
|
||||||
librarySearchResultsFormatted: [],
|
librarySearchResultsFormatted: [],
|
||||||
librarySearchResultCount: 0,
|
lastQueueJob: "",
|
||||||
|
successfulJobCount: 0,
|
||||||
|
failedJobCount: 0,
|
||||||
|
importJobStatistics: [],
|
||||||
libraryQueueResults: [],
|
libraryQueueResults: [],
|
||||||
librarySearchError: {},
|
librarySearchError: {},
|
||||||
|
libraryServiceStatus: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
function fileOpsReducer(state = initialState, action) {
|
function fileOpsReducer(state = initialState, action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case IMS_COMICBOOK_METADATA_FETCHED:
|
case IMS_COMICBOOK_METADATA_FETCHED:
|
||||||
@@ -149,17 +157,58 @@ 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: {
|
||||||
console.log("BASH", action);
|
if (state.recentComics.length === 5) {
|
||||||
if(state.recentComics.length === 5) {
|
|
||||||
state.recentComics.pop();
|
state.recentComics.pop();
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
librarySearchResultCount: state.librarySearchResultCount + 1,
|
successfulJobCount: action.completedJobCount,
|
||||||
recentComics: [...state.recentComics, action.result.data.importResult]
|
lastQueueJob: action.importResult.rawFileDetails.name,
|
||||||
|
recentComics: [...state.recentComics, action.importResult],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case LS_COVER_EXTRACTION_FAILED: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
failedJobCount: action.failedJobCount,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case LS_IMPORT_QUEUE_DRAINED: {
|
||||||
|
localStorage.removeItem("sessionId");
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
LSQueueImportStatus: "drained",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case RESTORE_JOB_COUNTS_AFTER_SESSION_RESTORATION: {
|
||||||
|
console.log("Restoring state for an active import in progress...");
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
successfulJobCount: action.completedJobCount,
|
||||||
|
failedJobCount: action.failedJobCount,
|
||||||
|
LSQueueImportStatus: action.queueStatus,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case LS_SET_QUEUE_STATUS: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
LSQueueImportStatus: action.data.queueStatus,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case LS_IMPORT_JOB_STATISTICS_FETCHED: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
importJobStatistics: action.data,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,14 +240,8 @@ function fileOpsReducer(state = initialState, action) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case LS_QUEUE_DRAINED: {
|
|
||||||
console.log("drained", action);
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
case LS_COMIC_ADDED: {
|
case LS_COMIC_ADDED: {
|
||||||
console.log("ADDED na anna", action);
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
};
|
};
|
||||||
@@ -271,7 +314,12 @@ function fileOpsReducer(state = initialState, action) {
|
|||||||
SSCallInProgress: false,
|
SSCallInProgress: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
case LIBRARY_SERVICE_HEALTH: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
libraryServiceStatus: action.status,
|
||||||
|
};
|
||||||
|
}
|
||||||
case FILEOPS_STATE_RESET: {
|
case FILEOPS_STATE_RESET: {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
|||||||
11
src/client/shared/middleware/SocketIOMiddleware.tsx
Normal file
11
src/client/shared/middleware/SocketIOMiddleware.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
const socketIOMiddleware = (socket) => {
|
||||||
|
return (store) => (next) => (action) => {
|
||||||
|
if (action.type === "EMIT_SOCKET_EVENT") {
|
||||||
|
const { event, data } = action.payload;
|
||||||
|
socket.emit(event, data);
|
||||||
|
}
|
||||||
|
return next(action);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default socketIOMiddleware;
|
||||||
10
src/client/shared/socket.io/instance.tsx
Normal file
10
src/client/shared/socket.io/instance.tsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import io from "socket.io-client";
|
||||||
|
import { SOCKET_BASE_URI } from "../../constants/endpoints";
|
||||||
|
const sessionId = localStorage.getItem("sessionId");
|
||||||
|
const socketIOConnectionInstance = io(SOCKET_BASE_URI, {
|
||||||
|
transports: ["websocket"],
|
||||||
|
withCredentials: true,
|
||||||
|
query: { sessionId },
|
||||||
|
});
|
||||||
|
|
||||||
|
export default socketIOConnectionInstance;
|
||||||
@@ -4,11 +4,11 @@ import { composeWithDevTools } from "@redux-devtools/extension";
|
|||||||
import thunk from "redux-thunk";
|
import thunk from "redux-thunk";
|
||||||
import { createReduxHistoryContext } from "redux-first-history";
|
import { createReduxHistoryContext } from "redux-first-history";
|
||||||
import { reducers } from "../reducers/index";
|
import { reducers } from "../reducers/index";
|
||||||
|
|
||||||
import { io } from "socket.io-client";
|
|
||||||
import socketIoMiddleware from "redux-socket.io-middleware";
|
import socketIoMiddleware from "redux-socket.io-middleware";
|
||||||
import { SOCKET_BASE_URI } from "../constants/endpoints";
|
import socketIOMiddleware from "../shared/middleware/SocketIOMiddleware";
|
||||||
const socketConnection = io(SOCKET_BASE_URI, { transports: ["websocket"] });
|
import socketIOConnectionInstance from "../shared/socket.io/instance";
|
||||||
|
|
||||||
|
const customSocketIOMiddleware = socketIOMiddleware(socketIOConnectionInstance);
|
||||||
|
|
||||||
const { createReduxHistory, routerMiddleware, routerReducer } =
|
const { createReduxHistory, routerMiddleware, routerReducer } =
|
||||||
createReduxHistoryContext({
|
createReduxHistoryContext({
|
||||||
@@ -22,7 +22,8 @@ export const store = createStore(
|
|||||||
}),
|
}),
|
||||||
composeWithDevTools(
|
composeWithDevTools(
|
||||||
applyMiddleware(
|
applyMiddleware(
|
||||||
socketIoMiddleware(socketConnection),
|
socketIoMiddleware(socketIOConnectionInstance),
|
||||||
|
customSocketIOMiddleware,
|
||||||
thunk,
|
thunk,
|
||||||
routerMiddleware,
|
routerMiddleware,
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user