Removed react-redux from project
This commit is contained in:
@@ -118,7 +118,6 @@
|
||||
"@types/prop-types": "^15.7.15",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@types/react-redux": "^7.1.34",
|
||||
"@types/react-table": "^7.7.20",
|
||||
"autoprefixer": "^10.4.27",
|
||||
"docdash": "^2.0.2",
|
||||
|
||||
@@ -1,265 +0,0 @@
|
||||
/**
|
||||
* @fileoverview Redux action creators for AirDC++ integration.
|
||||
* Provides actions for Direct Connect protocol operations including
|
||||
* search, download management, and socket connection handling.
|
||||
* @module actions/airdcpp
|
||||
*/
|
||||
|
||||
import {
|
||||
SearchQuery,
|
||||
SearchInstance,
|
||||
PriorityEnum,
|
||||
SearchResponse,
|
||||
} from "threetwo-ui-typings";
|
||||
import {
|
||||
LIBRARY_SERVICE_BASE_URI,
|
||||
SEARCH_SERVICE_BASE_URI,
|
||||
} from "../constants/endpoints";
|
||||
import {
|
||||
AIRDCPP_SEARCH_RESULTS_ADDED,
|
||||
AIRDCPP_SEARCH_RESULTS_UPDATED,
|
||||
AIRDCPP_HUB_SEARCHES_SENT,
|
||||
AIRDCPP_RESULT_DOWNLOAD_INITIATED,
|
||||
AIRDCPP_DOWNLOAD_PROGRESS_TICK,
|
||||
AIRDCPP_BUNDLES_FETCHED,
|
||||
AIRDCPP_SEARCH_IN_PROGRESS,
|
||||
AIRDCPP_FILE_DOWNLOAD_COMPLETED,
|
||||
LS_SINGLE_IMPORT,
|
||||
IMS_COMIC_BOOK_DB_OBJECT_FETCHED,
|
||||
AIRDCPP_TRANSFERS_FETCHED,
|
||||
LIBRARY_ISSUE_BUNDLES,
|
||||
AIRDCPP_SOCKET_CONNECTED,
|
||||
AIRDCPP_SOCKET_DISCONNECTED,
|
||||
} from "../constants/action-types";
|
||||
import { isNil } from "lodash";
|
||||
import axios, { AxiosResponse } from "axios";
|
||||
|
||||
import type { AirDCPPSearchData } from "../types";
|
||||
import type { Dispatch } from "react";
|
||||
|
||||
/** Redux action type for AirDC++ actions */
|
||||
interface AirDCPPAction {
|
||||
type: string;
|
||||
data?: unknown;
|
||||
downloadResult?: unknown;
|
||||
bundleDBImportResult?: AxiosResponse | null;
|
||||
bundles?: AirDCPPBundle[];
|
||||
issue_bundles?: AxiosResponse;
|
||||
comicBookDetail?: unknown;
|
||||
IMS_inProgress?: boolean;
|
||||
}
|
||||
|
||||
/** AirDC++ Bundle type */
|
||||
interface AirDCPPBundle {
|
||||
id: string;
|
||||
name?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/** Download item in acquisition data */
|
||||
interface AirDCPPDownloadItem {
|
||||
bundleId: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/** Thunk dispatch type */
|
||||
type ThunkDispatch = Dispatch<AirDCPPAction>;
|
||||
|
||||
/**
|
||||
* Creates a promise that resolves after a specified delay.
|
||||
* Useful for rate limiting or adding delays between operations.
|
||||
*
|
||||
* @param {number} ms - Number of milliseconds to sleep
|
||||
* @returns {Promise<NodeJS.Timeout>} Promise that resolves after the delay
|
||||
* @example
|
||||
* await sleep(1000); // Wait 1 second
|
||||
*/
|
||||
export const sleep = (ms: number): Promise<NodeJS.Timeout> => {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
};
|
||||
|
||||
/**
|
||||
* Redux thunk action creator to toggle AirDC++ socket connection status.
|
||||
* Dispatches connection or disconnection events to update application state.
|
||||
*
|
||||
* @param {String} status - Connection status ("connected" or "disconnected")
|
||||
* @param {any} [payload] - Optional payload data for the status change
|
||||
* @returns {Function} Redux thunk function
|
||||
*/
|
||||
export const toggleAirDCPPSocketConnectionStatus =
|
||||
(status: String, payload?: unknown) => async (dispatch: ThunkDispatch) => {
|
||||
switch (status) {
|
||||
case "connected":
|
||||
dispatch({
|
||||
type: AIRDCPP_SOCKET_CONNECTED,
|
||||
data: payload,
|
||||
});
|
||||
break;
|
||||
|
||||
case "disconnected":
|
||||
dispatch({
|
||||
type: AIRDCPP_SOCKET_DISCONNECTED,
|
||||
data: payload,
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Redux thunk action creator to download an item from AirDC++ search results.
|
||||
* Initiates the download, stores bundle metadata in the database, and updates
|
||||
* the comic book record with download information.
|
||||
*
|
||||
* @param {Number} searchInstanceId - ID of the active search instance
|
||||
* @param {String} resultId - ID of the search result to download
|
||||
* @param {String} comicObjectId - ID of the comic book in the database
|
||||
* @param {String} name - Name of the file to download
|
||||
* @param {Number} size - Size of the file in bytes
|
||||
* @param {any} type - File type information
|
||||
* @param {any} ADCPPSocket - AirDC++ socket connection instance
|
||||
* @param {any} credentials - Authentication credentials for AirDC++
|
||||
* @returns {Function} Redux thunk function
|
||||
* @throws {Error} If download initiation or database update fails
|
||||
*/
|
||||
export const downloadAirDCPPItem =
|
||||
(
|
||||
searchInstanceId: Number,
|
||||
resultId: String,
|
||||
comicObjectId: String,
|
||||
name: String,
|
||||
size: Number,
|
||||
type: unknown,
|
||||
ADCPPSocket: { isConnected: () => boolean; connect: () => Promise<void>; post: (url: string) => Promise<{ bundle_info: { id: string } }> },
|
||||
credentials: unknown,
|
||||
) =>
|
||||
async (dispatch: ThunkDispatch) => {
|
||||
try {
|
||||
if (!ADCPPSocket.isConnected()) {
|
||||
await ADCPPSocket.connect();
|
||||
}
|
||||
let bundleDBImportResult: AxiosResponse | null = null;
|
||||
const downloadResult = await ADCPPSocket.post(
|
||||
`search/${searchInstanceId}/results/${resultId}/download`,
|
||||
);
|
||||
|
||||
if (!isNil(downloadResult)) {
|
||||
bundleDBImportResult = await axios({
|
||||
method: "POST",
|
||||
url: `${LIBRARY_SERVICE_BASE_URI}/applyAirDCPPDownloadMetadata`,
|
||||
headers: {
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
data: {
|
||||
bundleId: downloadResult.bundle_info.id,
|
||||
comicObjectId,
|
||||
name,
|
||||
size,
|
||||
type,
|
||||
},
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: AIRDCPP_RESULT_DOWNLOAD_INITIATED,
|
||||
downloadResult,
|
||||
bundleDBImportResult,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: IMS_COMIC_BOOK_DB_OBJECT_FETCHED,
|
||||
comicBookDetail: bundleDBImportResult?.data,
|
||||
IMS_inProgress: false,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Redux thunk action creator to fetch AirDC++ download bundles for a specific comic.
|
||||
* Retrieves the comic book record, extracts associated bundle IDs, and fetches
|
||||
* bundle details from the AirDC++ queue.
|
||||
*
|
||||
* @param {string} comicObjectId - ID of the comic book in the database
|
||||
* @param {any} ADCPPSocket - AirDC++ socket connection instance
|
||||
* @param {any} credentials - Authentication credentials for AirDC++
|
||||
* @returns {Function} Redux thunk function that dispatches AIRDCPP_BUNDLES_FETCHED
|
||||
* @throws {Error} If fetching comic or bundles fails
|
||||
*/
|
||||
export const getBundlesForComic =
|
||||
(comicObjectId: string, ADCPPSocket: { isConnected: () => boolean; connect: () => Promise<void>; get: (url: string) => Promise<AirDCPPBundle> }, credentials: unknown) =>
|
||||
async (dispatch: ThunkDispatch) => {
|
||||
try {
|
||||
if (!ADCPPSocket.isConnected()) {
|
||||
await ADCPPSocket.connect();
|
||||
}
|
||||
const comicObject = await axios({
|
||||
method: "POST",
|
||||
url: `${LIBRARY_SERVICE_BASE_URI}/getComicBookById`,
|
||||
headers: {
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
data: {
|
||||
id: `${comicObjectId}`,
|
||||
},
|
||||
});
|
||||
// get only the bundles applicable for the comic
|
||||
if (comicObject.data.acquisition.directconnect) {
|
||||
const filteredBundles =
|
||||
comicObject.data.acquisition.directconnect.downloads.map(
|
||||
async ({ bundleId }: AirDCPPDownloadItem) => {
|
||||
return await ADCPPSocket.get(`queue/bundles/${bundleId}`);
|
||||
},
|
||||
);
|
||||
dispatch({
|
||||
type: AIRDCPP_BUNDLES_FETCHED,
|
||||
bundles: await Promise.all(filteredBundles),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Redux thunk action creator to fetch all active transfers from AirDC++.
|
||||
* Retrieves current download bundles and groups library issues by their
|
||||
* associated bundle IDs for display in the UI.
|
||||
*
|
||||
* @param {any} ADCPPSocket - AirDC++ socket connection instance
|
||||
* @param {any} credentials - Authentication credentials for AirDC++
|
||||
* @returns {Function} Redux thunk function that dispatches AIRDCPP_TRANSFERS_FETCHED
|
||||
* and LIBRARY_ISSUE_BUNDLES actions
|
||||
* @throws {Error} If fetching transfers fails
|
||||
*/
|
||||
export const getTransfers =
|
||||
(ADCPPSocket: { isConnected: () => boolean; connect: () => Promise<void>; get: (url: string, options: Record<string, unknown>) => Promise<AirDCPPBundle[]> }, credentials: unknown) => async (dispatch: ThunkDispatch) => {
|
||||
try {
|
||||
if (!ADCPPSocket.isConnected()) {
|
||||
await ADCPPSocket.connect();
|
||||
}
|
||||
const bundles = await ADCPPSocket.get("queue/bundles/1/85", {});
|
||||
if (!isNil(bundles)) {
|
||||
dispatch({
|
||||
type: AIRDCPP_TRANSFERS_FETCHED,
|
||||
bundles,
|
||||
});
|
||||
const bundleIds = bundles.map((bundle: AirDCPPBundle) => bundle.id);
|
||||
// get issues with matching bundleIds
|
||||
const issue_bundles = await axios({
|
||||
url: `${SEARCH_SERVICE_BASE_URI}/groupIssuesByBundles`,
|
||||
method: "POST",
|
||||
data: { bundleIds },
|
||||
});
|
||||
dispatch({
|
||||
type: LIBRARY_ISSUE_BUNDLES,
|
||||
issue_bundles,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
@@ -1,339 +0,0 @@
|
||||
/**
|
||||
* @fileoverview Redux action creators for ComicVine API and comic book information.
|
||||
* Provides actions for searching ComicVine, fetching comic metadata, managing
|
||||
* library statistics, and applying ComicVine matches to local comic records.
|
||||
* @module actions/comicinfo
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import rateLimiter from "axios-rate-limit";
|
||||
import { setupCache } from "axios-cache-interceptor";
|
||||
import {
|
||||
CV_SEARCH_SUCCESS,
|
||||
CV_API_CALL_IN_PROGRESS,
|
||||
CV_API_GENERIC_FAILURE,
|
||||
IMS_COMIC_BOOK_DB_OBJECT_CALL_IN_PROGRESS,
|
||||
IMS_COMIC_BOOK_DB_OBJECT_FETCHED,
|
||||
CV_ISSUES_METADATA_CALL_IN_PROGRESS,
|
||||
CV_CLEANUP,
|
||||
IMS_COMIC_BOOKS_DB_OBJECTS_FETCHED,
|
||||
CV_ISSUES_MATCHES_IN_LIBRARY_FETCHED,
|
||||
CV_ISSUES_FOR_VOLUME_IN_LIBRARY_SUCCESS,
|
||||
CV_WEEKLY_PULLLIST_CALL_IN_PROGRESS,
|
||||
CV_WEEKLY_PULLLIST_FETCHED,
|
||||
LIBRARY_STATISTICS_CALL_IN_PROGRESS,
|
||||
LIBRARY_STATISTICS_FETCHED,
|
||||
} from "../constants/action-types";
|
||||
import {
|
||||
COMICVINE_SERVICE_URI,
|
||||
LIBRARY_SERVICE_BASE_URI,
|
||||
} from "../constants/endpoints";
|
||||
import type { Dispatch } from "react";
|
||||
|
||||
/** Redux action type for comic info actions */
|
||||
interface ComicInfoAction {
|
||||
type: string;
|
||||
data?: unknown;
|
||||
inProgress?: boolean;
|
||||
searchResults?: unknown;
|
||||
error?: unknown;
|
||||
issues?: unknown[];
|
||||
matches?: unknown;
|
||||
comicBookDetail?: unknown;
|
||||
comicBooks?: unknown[];
|
||||
IMS_inProgress?: boolean;
|
||||
}
|
||||
|
||||
/** Options for the weekly pull list */
|
||||
interface PullListOptions {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/** Options for the comicinfo API call */
|
||||
interface ComicInfoAPIOptions {
|
||||
callURIAction: string;
|
||||
callMethod: string;
|
||||
callParams: Record<string, unknown>;
|
||||
data?: unknown;
|
||||
}
|
||||
|
||||
/** Issue type from ComicVine */
|
||||
interface ComicVineIssue {
|
||||
id: string;
|
||||
name: string;
|
||||
issue_number: string;
|
||||
volume: {
|
||||
name: string;
|
||||
};
|
||||
}
|
||||
|
||||
/** Match object for ComicVine metadata */
|
||||
interface ComicVineMatch {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/** Thunk dispatch type */
|
||||
type ThunkDispatch = Dispatch<ComicInfoAction>;
|
||||
|
||||
/**
|
||||
* Rate-limited axios instance for ComicVine API calls.
|
||||
* Limited to 1 request per second to comply with API rate limits.
|
||||
* @constant {AxiosInstance}
|
||||
*/
|
||||
const http = rateLimiter(axios.create(), {
|
||||
maxRequests: 1,
|
||||
perMilliseconds: 1000,
|
||||
maxRPS: 1,
|
||||
});
|
||||
|
||||
/**
|
||||
* Cached axios instance for reducing redundant API calls.
|
||||
* @constant {AxiosInstance}
|
||||
*/
|
||||
const cachedAxios = setupCache(axios);
|
||||
|
||||
/**
|
||||
* Redux thunk action creator to fetch the weekly comic pull list.
|
||||
* Retrieves upcoming comic releases from the ComicVine service.
|
||||
*
|
||||
* @param {Object} options - Query parameters for the pull list request
|
||||
* @returns {Function} Redux thunk function that dispatches CV_WEEKLY_PULLLIST_FETCHED
|
||||
*/
|
||||
export const getWeeklyPullList = (options: PullListOptions) => async (dispatch: ThunkDispatch) => {
|
||||
try {
|
||||
dispatch({
|
||||
type: CV_WEEKLY_PULLLIST_CALL_IN_PROGRESS,
|
||||
});
|
||||
await cachedAxios(`${COMICVINE_SERVICE_URI}/getWeeklyPullList`, {
|
||||
method: "get",
|
||||
params: options,
|
||||
}).then((response) => {
|
||||
dispatch({
|
||||
type: CV_WEEKLY_PULLLIST_FETCHED,
|
||||
data: response.data.result,
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
// Error handling could be added here if needed
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic Redux thunk action creator for ComicVine API calls.
|
||||
* Handles rate-limited requests to the ComicVine service with configurable
|
||||
* endpoints, methods, and parameters.
|
||||
*
|
||||
* @param {Object} options - API call configuration options
|
||||
* @param {string} options.callURIAction - API endpoint action (e.g., "search")
|
||||
* @param {string} options.callMethod - HTTP method (GET, POST, etc.)
|
||||
* @param {Object} options.callParams - Query parameters for the request
|
||||
* @param {any} [options.data] - Request body data
|
||||
* @returns {Function} Redux thunk function that dispatches appropriate action based on callURIAction
|
||||
*/
|
||||
export const comicinfoAPICall = (options: ComicInfoAPIOptions) => async (dispatch: ThunkDispatch) => {
|
||||
try {
|
||||
dispatch({
|
||||
type: CV_API_CALL_IN_PROGRESS,
|
||||
inProgress: true,
|
||||
});
|
||||
const serviceURI = `${COMICVINE_SERVICE_URI}/${options.callURIAction}`;
|
||||
const response = await http(serviceURI, {
|
||||
method: options.callMethod,
|
||||
params: options.callParams,
|
||||
data: options.data ? options.data : null,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
},
|
||||
});
|
||||
|
||||
switch (options.callURIAction) {
|
||||
case "search":
|
||||
dispatch({
|
||||
type: CV_SEARCH_SUCCESS,
|
||||
searchResults: response.data,
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
dispatch({
|
||||
type: CV_API_GENERIC_FAILURE,
|
||||
error,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Redux thunk action creator to fetch all issues for a comic series.
|
||||
* Retrieves issue list from ComicVine for a given volume/series.
|
||||
*
|
||||
* @param {string} comicObjectID - ComicVine volume/series ID
|
||||
* @returns {Function} Redux thunk function that dispatches CV_ISSUES_FOR_VOLUME_IN_LIBRARY_SUCCESS
|
||||
*/
|
||||
export const getIssuesForSeries =
|
||||
(comicObjectID: string) => async (dispatch: ThunkDispatch) => {
|
||||
dispatch({
|
||||
type: CV_ISSUES_METADATA_CALL_IN_PROGRESS,
|
||||
});
|
||||
dispatch({
|
||||
type: CV_CLEANUP,
|
||||
});
|
||||
|
||||
const issues = await axios({
|
||||
url: `${COMICVINE_SERVICE_URI}/getIssuesForSeries`,
|
||||
method: "POST",
|
||||
params: {
|
||||
comicObjectID,
|
||||
},
|
||||
});
|
||||
dispatch({
|
||||
type: CV_ISSUES_FOR_VOLUME_IN_LIBRARY_SUCCESS,
|
||||
issues: issues.data.results,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Redux thunk action creator to analyze library issues against ComicVine data.
|
||||
* Maps issues to query objects and finds matching issues in the local library.
|
||||
*
|
||||
* @param {Array} issues - Array of ComicVine issue objects to analyze
|
||||
* @param {string} issues[].id - Issue ID
|
||||
* @param {string} issues[].name - Issue name
|
||||
* @param {string} issues[].issue_number - Issue number
|
||||
* @param {Object} issues[].volume - Volume information
|
||||
* @param {string} issues[].volume.name - Volume name
|
||||
* @returns {Function} Redux thunk function that dispatches CV_ISSUES_MATCHES_IN_LIBRARY_FETCHED
|
||||
*/
|
||||
export const analyzeLibrary = (issues: ComicVineIssue[]) => async (dispatch: ThunkDispatch) => {
|
||||
dispatch({
|
||||
type: CV_ISSUES_METADATA_CALL_IN_PROGRESS,
|
||||
});
|
||||
const queryObjects = issues.map((issue: ComicVineIssue) => {
|
||||
const { id, name, issue_number } = issue;
|
||||
return {
|
||||
issueId: id,
|
||||
issueName: name,
|
||||
volumeName: issue.volume.name,
|
||||
issueNumber: issue_number,
|
||||
};
|
||||
});
|
||||
const foo = await axios({
|
||||
url: `${LIBRARY_SERVICE_BASE_URI}/findIssueForSeries`,
|
||||
method: "POST",
|
||||
data: {
|
||||
queryObjects,
|
||||
},
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: CV_ISSUES_MATCHES_IN_LIBRARY_FETCHED,
|
||||
matches: foo.data,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Redux thunk action creator to fetch library statistics.
|
||||
* Retrieves aggregate statistics about the comic library.
|
||||
*
|
||||
* @returns {Function} Redux thunk function that dispatches LIBRARY_STATISTICS_FETCHED
|
||||
*/
|
||||
export const getLibraryStatistics = () => async (dispatch: ThunkDispatch) => {
|
||||
dispatch({
|
||||
type: LIBRARY_STATISTICS_CALL_IN_PROGRESS,
|
||||
});
|
||||
const result = await axios({
|
||||
url: `${LIBRARY_SERVICE_BASE_URI}/libraryStatistics`,
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: LIBRARY_STATISTICS_FETCHED,
|
||||
data: result.data,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Redux thunk action creator to fetch detailed comic book information.
|
||||
* Retrieves full comic book document from the library database by ID.
|
||||
*
|
||||
* @param {string} comicBookObjectId - Database ID of the comic book
|
||||
* @returns {Function} Redux thunk function that dispatches IMS_COMIC_BOOK_DB_OBJECT_FETCHED
|
||||
*/
|
||||
export const getComicBookDetailById =
|
||||
(comicBookObjectId: string) => async (dispatch: ThunkDispatch) => {
|
||||
dispatch({
|
||||
type: IMS_COMIC_BOOK_DB_OBJECT_CALL_IN_PROGRESS,
|
||||
IMS_inProgress: true,
|
||||
});
|
||||
const result = await axios.request({
|
||||
url: `${LIBRARY_SERVICE_BASE_URI}/getComicBookById`,
|
||||
method: "POST",
|
||||
data: {
|
||||
id: comicBookObjectId,
|
||||
},
|
||||
});
|
||||
dispatch({
|
||||
type: IMS_COMIC_BOOK_DB_OBJECT_FETCHED,
|
||||
comicBookDetail: result.data,
|
||||
IMS_inProgress: false,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Redux thunk action creator to fetch multiple comic books by their IDs.
|
||||
* Retrieves full comic book documents from the library database for a list of IDs.
|
||||
*
|
||||
* @param {Array<string>} comicBookObjectIds - Array of database IDs
|
||||
* @returns {Function} Redux thunk function that dispatches IMS_COMIC_BOOKS_DB_OBJECTS_FETCHED
|
||||
*/
|
||||
export const getComicBooksDetailsByIds =
|
||||
(comicBookObjectIds: Array<string>) => async (dispatch: ThunkDispatch) => {
|
||||
dispatch({
|
||||
type: IMS_COMIC_BOOK_DB_OBJECT_CALL_IN_PROGRESS,
|
||||
IMS_inProgress: true,
|
||||
});
|
||||
const result = await axios.request({
|
||||
url: `${LIBRARY_SERVICE_BASE_URI}/getComicBooksByIds`,
|
||||
method: "POST",
|
||||
data: {
|
||||
ids: comicBookObjectIds,
|
||||
},
|
||||
});
|
||||
dispatch({
|
||||
type: IMS_COMIC_BOOKS_DB_OBJECTS_FETCHED,
|
||||
comicBooks: result.data,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Redux thunk action creator to apply ComicVine metadata to a local comic.
|
||||
* Associates a ComicVine match with a comic book record in the database,
|
||||
* updating the comic with metadata from ComicVine.
|
||||
*
|
||||
* @param {Object} match - ComicVine match object containing metadata to apply
|
||||
* @param {string} comicObjectId - Database ID of the local comic book to update
|
||||
* @returns {Function} Redux thunk function that dispatches IMS_COMIC_BOOK_DB_OBJECT_FETCHED
|
||||
*/
|
||||
export const applyComicVineMatch =
|
||||
(match: ComicVineMatch, comicObjectId: string) => async (dispatch: ThunkDispatch) => {
|
||||
dispatch({
|
||||
type: IMS_COMIC_BOOK_DB_OBJECT_CALL_IN_PROGRESS,
|
||||
IMS_inProgress: true,
|
||||
});
|
||||
const result = await axios.request({
|
||||
url: `${LIBRARY_SERVICE_BASE_URI}/applyComicVineMetadata`,
|
||||
method: "POST",
|
||||
data: {
|
||||
match,
|
||||
comicObjectId,
|
||||
},
|
||||
});
|
||||
dispatch({
|
||||
type: IMS_COMIC_BOOK_DB_OBJECT_FETCHED,
|
||||
comicBookDetail: result.data,
|
||||
IMS_inProgress: false,
|
||||
});
|
||||
};
|
||||
@@ -1,446 +0,0 @@
|
||||
/**
|
||||
* @fileoverview Redux action creators for file operations and library management.
|
||||
* Provides actions for importing comics, searching the library, managing folders,
|
||||
* extracting comic archives, and analyzing images.
|
||||
* @module actions/fileops
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import { IFolderData } from "threetwo-ui-typings";
|
||||
import {
|
||||
COMICVINE_SERVICE_URI,
|
||||
IMAGETRANSFORMATION_SERVICE_BASE_URI,
|
||||
LIBRARY_SERVICE_BASE_URI,
|
||||
SEARCH_SERVICE_BASE_URI,
|
||||
JOB_QUEUE_SERVICE_BASE_URI,
|
||||
} from "../constants/endpoints";
|
||||
import {
|
||||
IMS_COMIC_BOOK_GROUPS_FETCHED,
|
||||
IMS_COMIC_BOOK_GROUPS_CALL_IN_PROGRESS,
|
||||
IMS_RECENT_COMICS_FETCHED,
|
||||
IMS_WANTED_COMICS_FETCHED,
|
||||
CV_API_CALL_IN_PROGRESS,
|
||||
CV_SEARCH_SUCCESS,
|
||||
CV_CLEANUP,
|
||||
IMS_CV_METADATA_IMPORT_CALL_IN_PROGRESS,
|
||||
IMS_CV_METADATA_IMPORT_SUCCESSFUL,
|
||||
IMS_CV_METADATA_IMPORT_FAILED,
|
||||
LS_IMPORT,
|
||||
IMG_ANALYSIS_CALL_IN_PROGRESS,
|
||||
IMG_ANALYSIS_DATA_FETCH_SUCCESS,
|
||||
IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_IN_PROGRESS,
|
||||
SS_SEARCH_RESULTS_FETCHED,
|
||||
SS_SEARCH_IN_PROGRESS,
|
||||
FILEOPS_STATE_RESET,
|
||||
LS_IMPORT_CALL_IN_PROGRESS,
|
||||
SS_SEARCH_FAILED,
|
||||
SS_SEARCH_RESULTS_FETCHED_SPECIAL,
|
||||
WANTED_COMICS_FETCHED,
|
||||
VOLUMES_FETCHED,
|
||||
LIBRARY_SERVICE_HEALTH,
|
||||
LS_SET_QUEUE_STATUS,
|
||||
LS_IMPORT_JOB_STATISTICS_FETCHED,
|
||||
} from "../constants/action-types";
|
||||
|
||||
import { isNil } from "lodash";
|
||||
import type { Dispatch } from "react";
|
||||
|
||||
/** Redux action type for fileops actions */
|
||||
interface FileOpsAction {
|
||||
type: string;
|
||||
data?: unknown;
|
||||
status?: unknown;
|
||||
searchResults?: unknown;
|
||||
searchQueryObject?: unknown;
|
||||
importResult?: unknown;
|
||||
importError?: unknown;
|
||||
result?: unknown;
|
||||
meta?: { remote: boolean };
|
||||
}
|
||||
|
||||
/** Options for fetching comic books */
|
||||
interface GetComicBooksOptions {
|
||||
paginationOptions: Record<string, unknown>;
|
||||
predicate: Record<string, unknown>;
|
||||
comicStatus: string;
|
||||
}
|
||||
|
||||
/** Search payload for ComicVine matching */
|
||||
interface CVSearchPayload {
|
||||
rawFileDetails: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/** Issue search query structure */
|
||||
interface IssueSearchQuery {
|
||||
inferredIssueDetails: {
|
||||
name: string;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
}
|
||||
|
||||
/** Search query options */
|
||||
interface SearchQueryOptions {
|
||||
trigger: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/** Match object for ComicVine */
|
||||
interface CVMatch {
|
||||
score?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/** Thunk dispatch type */
|
||||
type ThunkDispatch = Dispatch<FileOpsAction>;
|
||||
|
||||
/**
|
||||
* Redux thunk action creator to fetch library service health status.
|
||||
* Retrieves health information from the library microservice.
|
||||
*
|
||||
* @param {string} [serviceName] - Optional specific service name to check
|
||||
* @returns {Function} Redux thunk function that dispatches LIBRARY_SERVICE_HEALTH
|
||||
*/
|
||||
export const getServiceStatus = (serviceName?: string) => async (dispatch: ThunkDispatch) => {
|
||||
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>> {
|
||||
return axios
|
||||
.request<Array<IFolderData>>({
|
||||
url: `${LIBRARY_SERVICE_BASE_URI}/walkFolders`,
|
||||
method: "POST",
|
||||
data: {
|
||||
basePathToWalk: path,
|
||||
},
|
||||
transformResponse: (r: string) => JSON.parse(r),
|
||||
})
|
||||
.then((response) => {
|
||||
const { data } = response;
|
||||
return data;
|
||||
})
|
||||
.catch((error) => error);
|
||||
}
|
||||
/**
|
||||
* Fetches comic book covers along with some metadata
|
||||
* @return the comic book metadata
|
||||
*/
|
||||
export const fetchComicBookMetadata = () => async (dispatch: ThunkDispatch) => {
|
||||
dispatch({
|
||||
type: LS_IMPORT_CALL_IN_PROGRESS,
|
||||
});
|
||||
|
||||
// dispatch(
|
||||
// success({
|
||||
// // uid: 'once-please', // you can specify your own uid if required
|
||||
// title: "Import Started",
|
||||
// message: `<span class="icon-text has-text-success"><i class="fas fa-plug"></i></span> Socket <span class="has-text-info">${socket.id}</span> connected. <strong>${walkedFolders.length}</strong> comics scanned.`,
|
||||
// dismissible: "click",
|
||||
// position: "tr",
|
||||
// autoDismiss: 0,
|
||||
// }),
|
||||
// );
|
||||
const sessionId = localStorage.getItem("sessionId");
|
||||
dispatch({
|
||||
type: LS_IMPORT,
|
||||
});
|
||||
|
||||
await axios.request({
|
||||
url: `${LIBRARY_SERVICE_BASE_URI}/newImport`,
|
||||
method: "POST",
|
||||
data: { sessionId },
|
||||
});
|
||||
};
|
||||
|
||||
export const getImportJobResultStatistics = () => async (dispatch: ThunkDispatch) => {
|
||||
const result = await axios.request({
|
||||
url: `${JOB_QUEUE_SERVICE_BASE_URI}/getJobResultStatistics`,
|
||||
method: "GET",
|
||||
});
|
||||
dispatch({
|
||||
type: LS_IMPORT_JOB_STATISTICS_FETCHED,
|
||||
data: result.data,
|
||||
});
|
||||
};
|
||||
|
||||
export const setQueueControl =
|
||||
(queueAction: string, queueStatus: string) => async (dispatch: ThunkDispatch) => {
|
||||
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
|
||||
* @param options
|
||||
**/
|
||||
export const getComicBooks = (options: GetComicBooksOptions) => async (dispatch: ThunkDispatch) => {
|
||||
const { paginationOptions, predicate, comicStatus } = options;
|
||||
|
||||
const response = await axios.request({
|
||||
url: `${LIBRARY_SERVICE_BASE_URI}/getComicBooks`,
|
||||
method: "POST",
|
||||
data: {
|
||||
paginationOptions,
|
||||
predicate,
|
||||
},
|
||||
});
|
||||
|
||||
switch (comicStatus) {
|
||||
case "recent":
|
||||
dispatch({
|
||||
type: IMS_RECENT_COMICS_FETCHED,
|
||||
data: response.data,
|
||||
});
|
||||
break;
|
||||
case "wanted":
|
||||
dispatch({
|
||||
type: IMS_WANTED_COMICS_FETCHED,
|
||||
data: response.data.docs,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes a call to library service to import the comic book metadata into the ThreeTwo data store.
|
||||
* @returns Nothing.
|
||||
* @param payload
|
||||
*/
|
||||
export const importToDB =
|
||||
(sourceName: string, metadata?: unknown) => (dispatch: ThunkDispatch) => {
|
||||
try {
|
||||
const comicBookMetadata = {
|
||||
importType: "new",
|
||||
payload: {
|
||||
rawFileDetails: {
|
||||
name: "",
|
||||
},
|
||||
importStatus: {
|
||||
isImported: true,
|
||||
tagged: false,
|
||||
matchedResult: {
|
||||
score: "0",
|
||||
},
|
||||
},
|
||||
sourcedMetadata: metadata || null,
|
||||
acquisition: { source: { wanted: true, name: sourceName } },
|
||||
},
|
||||
};
|
||||
dispatch({
|
||||
type: IMS_CV_METADATA_IMPORT_CALL_IN_PROGRESS,
|
||||
});
|
||||
return axios
|
||||
.request({
|
||||
url: `${LIBRARY_SERVICE_BASE_URI}/rawImportToDb`,
|
||||
method: "POST",
|
||||
data: comicBookMetadata,
|
||||
// transformResponse: (r: string) => JSON.parse(r),
|
||||
})
|
||||
.then((response) => {
|
||||
const { data } = response;
|
||||
dispatch({
|
||||
type: IMS_CV_METADATA_IMPORT_SUCCESSFUL,
|
||||
importResult: data,
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
dispatch({
|
||||
type: IMS_CV_METADATA_IMPORT_FAILED,
|
||||
importError: error,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchVolumeGroups = () => async (dispatch: ThunkDispatch) => {
|
||||
try {
|
||||
dispatch({
|
||||
type: IMS_COMIC_BOOK_GROUPS_CALL_IN_PROGRESS,
|
||||
});
|
||||
const response = await axios.request({
|
||||
url: `${LIBRARY_SERVICE_BASE_URI}/getComicBookGroups`,
|
||||
method: "GET",
|
||||
});
|
||||
dispatch({
|
||||
type: IMS_COMIC_BOOK_GROUPS_FETCHED,
|
||||
data: response.data,
|
||||
});
|
||||
} catch (error) {
|
||||
// Error handling could be added here if needed
|
||||
}
|
||||
};
|
||||
export const fetchComicVineMatches =
|
||||
(searchPayload: CVSearchPayload, issueSearchQuery: IssueSearchQuery, seriesSearchQuery?: unknown) => async (dispatch: ThunkDispatch) => {
|
||||
try {
|
||||
dispatch({
|
||||
type: CV_API_CALL_IN_PROGRESS,
|
||||
});
|
||||
axios
|
||||
.request({
|
||||
url: `${COMICVINE_SERVICE_URI}/volumeBasedSearch`,
|
||||
method: "POST",
|
||||
data: {
|
||||
format: "json",
|
||||
// hack
|
||||
query: issueSearchQuery.inferredIssueDetails.name
|
||||
.replace(/[^a-zA-Z0-9 ]/g, "")
|
||||
.trim(),
|
||||
limit: "100",
|
||||
page: 1,
|
||||
resources: "volume",
|
||||
scorerConfiguration: {
|
||||
searchParams: issueSearchQuery.inferredIssueDetails,
|
||||
},
|
||||
rawFileDetails: searchPayload.rawFileDetails,
|
||||
},
|
||||
transformResponse: (r) => {
|
||||
const matches = JSON.parse(r);
|
||||
return matches;
|
||||
// return sortBy(matches, (match) => -match.score);
|
||||
},
|
||||
})
|
||||
.then((response) => {
|
||||
let matches: CVMatch[] = [];
|
||||
if (
|
||||
!isNil(response.data.results) &&
|
||||
response.data.results.length === 1
|
||||
) {
|
||||
matches = response.data.results;
|
||||
} else {
|
||||
matches = response.data.map((match: CVMatch) => match);
|
||||
}
|
||||
dispatch({
|
||||
type: CV_SEARCH_SUCCESS,
|
||||
searchResults: matches,
|
||||
searchQueryObject: {
|
||||
issue: issueSearchQuery,
|
||||
series: seriesSearchQuery,
|
||||
},
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
// Error handling could be added here if needed
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: CV_CLEANUP,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* This method is a proxy to `uncompressFullArchive` which uncompresses complete `rar` or `zip` archives
|
||||
* @param {string} path The path to the compressed archive
|
||||
* @param {any} options Options object
|
||||
* @returns {any}
|
||||
*/
|
||||
export const extractComicArchive =
|
||||
(path: string, options: Record<string, unknown>) =>
|
||||
async (dispatch: ThunkDispatch) => {
|
||||
dispatch({
|
||||
type: IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_IN_PROGRESS,
|
||||
});
|
||||
await axios({
|
||||
method: "POST",
|
||||
url: `${LIBRARY_SERVICE_BASE_URI}/uncompressFullArchive`,
|
||||
headers: {
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
data: {
|
||||
filePath: path,
|
||||
options,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Description
|
||||
* @param {any} query
|
||||
* @param {any} options
|
||||
* @returns {any}
|
||||
*/
|
||||
export const searchIssue = (query: Record<string, unknown>, options: SearchQueryOptions) => async (dispatch: ThunkDispatch) => {
|
||||
dispatch({
|
||||
type: SS_SEARCH_IN_PROGRESS,
|
||||
});
|
||||
|
||||
const response = await axios({
|
||||
url: `${SEARCH_SERVICE_BASE_URI}/searchIssue`,
|
||||
method: "POST",
|
||||
data: { ...query, ...options },
|
||||
});
|
||||
|
||||
if (response.data.code === 404) {
|
||||
dispatch({
|
||||
type: SS_SEARCH_FAILED,
|
||||
data: response.data,
|
||||
});
|
||||
}
|
||||
|
||||
switch (options.trigger) {
|
||||
case "wantedComicsPage":
|
||||
dispatch({
|
||||
type: WANTED_COMICS_FETCHED,
|
||||
data: response.data.hits,
|
||||
});
|
||||
break;
|
||||
case "globalSearchBar":
|
||||
dispatch({
|
||||
type: SS_SEARCH_RESULTS_FETCHED_SPECIAL,
|
||||
data: response.data.hits,
|
||||
});
|
||||
break;
|
||||
|
||||
case "libraryPage":
|
||||
dispatch({
|
||||
type: SS_SEARCH_RESULTS_FETCHED,
|
||||
data: response.data.hits,
|
||||
});
|
||||
break;
|
||||
case "volumesPage":
|
||||
dispatch({
|
||||
type: VOLUMES_FETCHED,
|
||||
data: response.data.hits,
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
export const analyzeImage =
|
||||
(imageFilePath: string | Buffer) => async (dispatch: ThunkDispatch) => {
|
||||
dispatch({
|
||||
type: FILEOPS_STATE_RESET,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: IMG_ANALYSIS_CALL_IN_PROGRESS,
|
||||
});
|
||||
|
||||
const foo = await axios({
|
||||
url: `${IMAGETRANSFORMATION_SERVICE_BASE_URI}/analyze`,
|
||||
method: "POST",
|
||||
data: {
|
||||
imageFilePath,
|
||||
},
|
||||
});
|
||||
dispatch({
|
||||
type: IMG_ANALYSIS_DATA_FETCH_SUCCESS,
|
||||
result: foo.data,
|
||||
});
|
||||
};
|
||||
@@ -1,81 +0,0 @@
|
||||
/**
|
||||
* @fileoverview Action creators for Metron comic database API integration.
|
||||
* Provides functions to fetch comic metadata resources from the Metron service.
|
||||
* @module actions/metron
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import { isNil } from "lodash";
|
||||
import { METRON_SERVICE_URI } from "../constants/endpoints";
|
||||
|
||||
/** Options for fetching Metron resources */
|
||||
interface MetronFetchOptions {
|
||||
query: {
|
||||
page: number;
|
||||
resource?: string;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/** Metron resource result item */
|
||||
interface MetronResultItem {
|
||||
name?: string;
|
||||
__str__?: string;
|
||||
id: number;
|
||||
}
|
||||
|
||||
/** Metron resource result format */
|
||||
interface MetronResourceResult {
|
||||
options: Array<{ label: string; value: number }>;
|
||||
hasMore: boolean;
|
||||
additional: {
|
||||
page: number | null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} MetronResourceResult
|
||||
* @property {Array<{label: string, value: number}>} options - Formatted options for select components
|
||||
* @property {boolean} hasMore - Whether more results are available
|
||||
* @property {Object} additional - Additional pagination data
|
||||
* @property {number|null} additional.page - Next page number, or null if no more pages
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fetches comic resources from the Metron API service.
|
||||
* Returns formatted data suitable for async select/dropdown components.
|
||||
*
|
||||
* @async
|
||||
* @param {Object} options - Request options for the Metron API
|
||||
* @param {Object} options.query - Query parameters
|
||||
* @param {number} options.query.page - Current page number for pagination
|
||||
* @returns {Promise<MetronResourceResult>} Formatted results with pagination info
|
||||
* @example
|
||||
* const results = await fetchMetronResource({
|
||||
* query: { page: 1, resource: "publishers" }
|
||||
* });
|
||||
* // Returns: { options: [{label: "DC Comics", value: 1}], hasMore: true, additional: { page: 2 } }
|
||||
*/
|
||||
export const fetchMetronResource = async (options: MetronFetchOptions): Promise<MetronResourceResult> => {
|
||||
const metronResourceResults = await axios.post(
|
||||
`${METRON_SERVICE_URI}/fetchResource`,
|
||||
options,
|
||||
);
|
||||
const results = metronResourceResults.data.results.map((result: MetronResultItem) => {
|
||||
return {
|
||||
label: result.name || result.__str__,
|
||||
value: result.id,
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
options: results,
|
||||
hasMore: !isNil(metronResourceResults.data.next),
|
||||
additional: {
|
||||
page: !isNil(metronResourceResults.data.next)
|
||||
? options.query.page + 1
|
||||
: null,
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,150 +0,0 @@
|
||||
/**
|
||||
* @fileoverview Redux action creators for application settings management.
|
||||
* Provides actions for fetching, deleting, and managing settings, as well as
|
||||
* integrations with external services like qBittorrent and Prowlarr.
|
||||
* @module actions/settings
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import {
|
||||
SETTINGS_OBJECT_FETCHED,
|
||||
SETTINGS_CALL_IN_PROGRESS,
|
||||
SETTINGS_DB_FLUSH_SUCCESS,
|
||||
SETTINGS_QBITTORRENT_TORRENTS_LIST_FETCHED,
|
||||
} from "../constants/action-types";
|
||||
import {
|
||||
LIBRARY_SERVICE_BASE_URI,
|
||||
SETTINGS_SERVICE_BASE_URI,
|
||||
QBITTORRENT_SERVICE_BASE_URI,
|
||||
} from "../constants/endpoints";
|
||||
import type { Dispatch } from "react";
|
||||
|
||||
/** Redux action type for settings actions */
|
||||
interface SettingsAction {
|
||||
type: string;
|
||||
data?: unknown;
|
||||
}
|
||||
|
||||
/** Host info for qBittorrent */
|
||||
interface QBittorrentHostInfo {
|
||||
host: string;
|
||||
port: number;
|
||||
username?: string;
|
||||
password?: string;
|
||||
}
|
||||
|
||||
/** Host info for Prowlarr */
|
||||
interface ProwlarrHostInfo {
|
||||
host: string;
|
||||
port: number;
|
||||
apiKey: string;
|
||||
}
|
||||
|
||||
/** Thunk dispatch type */
|
||||
type ThunkDispatch = Dispatch<SettingsAction>;
|
||||
|
||||
/**
|
||||
* Redux thunk action creator to fetch application settings.
|
||||
* Can retrieve all settings or a specific settings key.
|
||||
*
|
||||
* @param {string} [settingsKey] - Optional specific settings key to fetch
|
||||
* @returns {Function} Redux thunk function that dispatches SETTINGS_OBJECT_FETCHED
|
||||
*/
|
||||
export const getSettings = (settingsKey?: string) => async (dispatch: ThunkDispatch) => {
|
||||
const result = await axios({
|
||||
url: `${SETTINGS_SERVICE_BASE_URI}/getSettings`,
|
||||
method: "POST",
|
||||
data: settingsKey,
|
||||
});
|
||||
{
|
||||
dispatch({
|
||||
type: SETTINGS_OBJECT_FETCHED,
|
||||
data: result.data,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Redux thunk action creator to delete all application settings.
|
||||
* Clears the settings from the database and resets state to empty object.
|
||||
*
|
||||
* @returns {Function} Redux thunk function that dispatches SETTINGS_OBJECT_FETCHED with empty data
|
||||
*/
|
||||
export const deleteSettings = () => async (dispatch: ThunkDispatch) => {
|
||||
const result = await axios({
|
||||
url: `${SETTINGS_SERVICE_BASE_URI}/deleteSettings`,
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
if (result.data.ok === 1) {
|
||||
dispatch({
|
||||
type: SETTINGS_OBJECT_FETCHED,
|
||||
data: {},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Redux thunk action creator to flush the entire database.
|
||||
* WARNING: This action is destructive and removes all data from the library database.
|
||||
*
|
||||
* @returns {Function} Redux thunk function that dispatches SETTINGS_DB_FLUSH_SUCCESS
|
||||
*/
|
||||
export const flushDb = () => async (dispatch: ThunkDispatch) => {
|
||||
dispatch({
|
||||
type: SETTINGS_CALL_IN_PROGRESS,
|
||||
});
|
||||
|
||||
const flushDbResult = await axios({
|
||||
url: `${LIBRARY_SERVICE_BASE_URI}/flushDb`,
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
if (flushDbResult) {
|
||||
dispatch({
|
||||
type: SETTINGS_DB_FLUSH_SUCCESS,
|
||||
data: flushDbResult.data,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Redux thunk action creator to connect and fetch qBittorrent client information.
|
||||
* Establishes connection to qBittorrent client and retrieves torrent list.
|
||||
*
|
||||
* @param {Object} hostInfo - Connection details for qBittorrent
|
||||
* @param {string} hostInfo.host - qBittorrent server hostname
|
||||
* @param {number} hostInfo.port - qBittorrent server port
|
||||
* @param {string} [hostInfo.username] - Authentication username
|
||||
* @param {string} [hostInfo.password] - Authentication password
|
||||
* @returns {Function} Redux thunk function that dispatches SETTINGS_QBITTORRENT_TORRENTS_LIST_FETCHED
|
||||
*/
|
||||
export const getQBitTorrentClientInfo = (hostInfo: QBittorrentHostInfo) => async (dispatch: ThunkDispatch) => {
|
||||
await axios.request({
|
||||
url: `${QBITTORRENT_SERVICE_BASE_URI}/connect`,
|
||||
method: "POST",
|
||||
data: hostInfo,
|
||||
});
|
||||
const qBittorrentClientInfo = await axios.request({
|
||||
url: `${QBITTORRENT_SERVICE_BASE_URI}/getClientInfo`,
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: SETTINGS_QBITTORRENT_TORRENTS_LIST_FETCHED,
|
||||
data: qBittorrentClientInfo.data,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Redux thunk action creator to test Prowlarr connection.
|
||||
* Verifies connection to Prowlarr indexer management service.
|
||||
*
|
||||
* @param {Object} hostInfo - Connection details for Prowlarr
|
||||
* @param {string} hostInfo.host - Prowlarr server hostname
|
||||
* @param {number} hostInfo.port - Prowlarr server port
|
||||
* @param {string} hostInfo.apiKey - Prowlarr API key
|
||||
* @returns {Function} Redux thunk function (currently not implemented)
|
||||
* @todo Implement Prowlarr connection verification
|
||||
*/
|
||||
export const getProwlarrConnectionInfo = (hostInfo: ProwlarrHostInfo) => async (_dispatch: ThunkDispatch) => {};
|
||||
@@ -1,7 +1,9 @@
|
||||
import React, { ReactElement, useCallback, useState } from "react";
|
||||
import { fetchMetronResource } from "../../../actions/metron.actions";
|
||||
import axios from "axios";
|
||||
import { isNil } from "lodash";
|
||||
import Creatable from "react-select/creatable";
|
||||
import { withAsyncPaginate } from "react-select-async-paginate";
|
||||
import { METRON_SERVICE_URI } from "../../../constants/endpoints";
|
||||
|
||||
const CreatableAsyncPaginate = withAsyncPaginate(Creatable);
|
||||
|
||||
@@ -20,6 +22,12 @@ interface AdditionalType {
|
||||
page: number | null;
|
||||
}
|
||||
|
||||
interface MetronResultItem {
|
||||
name?: string;
|
||||
__str__?: string;
|
||||
id: number;
|
||||
}
|
||||
|
||||
export const AsyncSelectPaginate = (props: AsyncSelectPaginateProps): ReactElement => {
|
||||
const [isAddingInProgress, setIsAddingInProgress] = useState(false);
|
||||
|
||||
@@ -29,14 +37,23 @@ export const AsyncSelectPaginate = (props: AsyncSelectPaginateProps): ReactEleme
|
||||
additional?: AdditionalType
|
||||
) => {
|
||||
const page = additional?.page ?? 1;
|
||||
return fetchMetronResource({
|
||||
const options = {
|
||||
method: "GET",
|
||||
resource: props.metronResource || "",
|
||||
query: {
|
||||
name: query,
|
||||
page,
|
||||
query: { name: query, page },
|
||||
};
|
||||
const response = await axios.post(`${METRON_SERVICE_URI}/fetchResource`, options);
|
||||
const results = response.data.results.map((result: MetronResultItem) => ({
|
||||
label: result.name || result.__str__,
|
||||
value: result.id,
|
||||
}));
|
||||
return {
|
||||
options: results,
|
||||
hasMore: !isNil(response.data.next),
|
||||
additional: {
|
||||
page: !isNil(response.data.next) ? page + 1 : null,
|
||||
},
|
||||
});
|
||||
};
|
||||
}, [props.metronResource]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { useCallback } from "react";
|
||||
import { Form, Field } from "react-final-form";
|
||||
import { ValidationErrors } from "final-form";
|
||||
import { fetchComicVineMatches } from "../../actions/fileops.actions";
|
||||
|
||||
interface ComicVineSearchFormProps {
|
||||
rawFileDetails?: Record<string, unknown>;
|
||||
|
||||
@@ -1,44 +1,28 @@
|
||||
import { debounce, isEmpty, map } from "lodash";
|
||||
import React, { ReactElement, useCallback, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import axios from "axios";
|
||||
import Card from "../shared/Carda";
|
||||
|
||||
import { searchIssue } from "../../actions/fileops.actions";
|
||||
import MetadataPanel from "../shared/MetadataPanel";
|
||||
import { SEARCH_SERVICE_BASE_URI } from "../../constants/endpoints";
|
||||
import type { GlobalSearchBarProps } from "../../types";
|
||||
|
||||
interface AppRootState {
|
||||
fileOps: {
|
||||
librarySearchResultsFormatted: Record<string, unknown>[];
|
||||
};
|
||||
}
|
||||
|
||||
export const SearchBar = (data: GlobalSearchBarProps): ReactElement => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const dispatch = useDispatch<any>();
|
||||
const searchResults = useSelector(
|
||||
(state: AppRootState) => state.fileOps.librarySearchResultsFormatted,
|
||||
);
|
||||
const [searchResults, setSearchResults] = useState<Record<string, unknown>[]>([]);
|
||||
|
||||
const performSearch = useCallback(
|
||||
debounce((e) => {
|
||||
dispatch(
|
||||
searchIssue(
|
||||
{
|
||||
query: {
|
||||
volumeName: e.target.value,
|
||||
},
|
||||
},
|
||||
{
|
||||
pagination: {
|
||||
size: 25,
|
||||
from: 0,
|
||||
},
|
||||
type: "volumeName",
|
||||
trigger: "globalSearchBar",
|
||||
},
|
||||
),
|
||||
);
|
||||
debounce(async (e) => {
|
||||
const response = await axios({
|
||||
url: `${SEARCH_SERVICE_BASE_URI}/searchIssue`,
|
||||
method: "POST",
|
||||
data: {
|
||||
query: { volumeName: e.target.value },
|
||||
pagination: { size: 25, from: 0 },
|
||||
type: "volumeName",
|
||||
trigger: "globalSearchBar",
|
||||
},
|
||||
});
|
||||
setSearchResults(response.data?.hits ?? []);
|
||||
}, 500),
|
||||
[data],
|
||||
);
|
||||
|
||||
@@ -8,14 +8,14 @@ import {
|
||||
import { useTable, usePagination } from "react-table";
|
||||
import prettyBytes from "pretty-bytes";
|
||||
import ellipsize from "ellipsize";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { getComicBooks } from "../../actions/fileops.actions";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import axios from "axios";
|
||||
import { isNil, isEmpty, isUndefined } from "lodash";
|
||||
import Masonry from "react-masonry-css";
|
||||
import Card from "../shared/Carda";
|
||||
import { detectIssueTypes } from "../../shared/utils/tradepaperback.utils";
|
||||
import { Link } from "react-router-dom";
|
||||
import { LIBRARY_SERVICE_HOST } from "../../constants/endpoints";
|
||||
import { LIBRARY_SERVICE_HOST, LIBRARY_SERVICE_BASE_URI } from "../../constants/endpoints";
|
||||
import type { LibraryGridProps } from "../../types";
|
||||
|
||||
interface ComicDoc {
|
||||
@@ -39,22 +39,21 @@ interface ComicDoc {
|
||||
};
|
||||
}
|
||||
|
||||
interface AppRootState {
|
||||
fileOps: {
|
||||
recentComics: {
|
||||
docs: ComicDoc[];
|
||||
totalDocs: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const LibraryGrid = (libraryGridProps: LibraryGridProps) => {
|
||||
const data = useSelector(
|
||||
(state: AppRootState) => state.fileOps.recentComics.docs,
|
||||
);
|
||||
const pageTotal = useSelector(
|
||||
(state: AppRootState) => state.fileOps.recentComics.totalDocs,
|
||||
);
|
||||
const { data: comicsData } = useQuery({
|
||||
queryKey: ["recentComics"],
|
||||
queryFn: async () =>
|
||||
axios({
|
||||
url: `${LIBRARY_SERVICE_BASE_URI}/getComicBooks`,
|
||||
method: "POST",
|
||||
data: {
|
||||
paginationOptions: { size: 25, from: 0 },
|
||||
predicate: {},
|
||||
},
|
||||
}),
|
||||
});
|
||||
const data: ComicDoc[] = comicsData?.data?.docs ?? [];
|
||||
const pageTotal: number = comicsData?.data?.totalDocs ?? 0;
|
||||
const breakpointColumnsObj = {
|
||||
default: 5,
|
||||
1100: 4,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { ReactElement, useEffect, useMemo, useState } from "react";
|
||||
import T2Table from "../shared/T2Table";
|
||||
import { getWeeklyPullList } from "../../actions/comicinfo.actions";
|
||||
import Card from "../shared/Carda";
|
||||
import ellipsize from "ellipsize";
|
||||
import { isNil } from "lodash";
|
||||
|
||||
@@ -1,23 +1,15 @@
|
||||
import React, { ReactElement } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useEffect } from "react";
|
||||
import { getServiceStatus } from "../../actions/fileops.actions";
|
||||
|
||||
interface AppRootState {
|
||||
fileOps: {
|
||||
libraryServiceStatus: unknown;
|
||||
};
|
||||
}
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import axios from "axios";
|
||||
import { LIBRARY_SERVICE_BASE_URI } from "../../constants/endpoints";
|
||||
|
||||
export const ServiceStatuses = (): ReactElement => {
|
||||
const serviceStatus = useSelector(
|
||||
(state: AppRootState) => state.fileOps.libraryServiceStatus,
|
||||
);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const dispatch = useDispatch<any>();
|
||||
useEffect(() => {
|
||||
dispatch(getServiceStatus());
|
||||
}, [dispatch]);
|
||||
const { data } = useQuery({
|
||||
queryKey: ["serviceStatus"],
|
||||
queryFn: async () =>
|
||||
axios({ url: `${LIBRARY_SERVICE_BASE_URI}/getHealthInformation`, method: "GET" }),
|
||||
});
|
||||
const serviceStatus = data?.data;
|
||||
return (
|
||||
<div className="is-clearfix">
|
||||
<div className="mt-4">
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { isArray, map } from "lodash";
|
||||
import React, { useEffect, ReactElement } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { getComicBooksDetailsByIds } from "../../actions/comicinfo.actions";
|
||||
import React, { ReactElement } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import axios from "axios";
|
||||
import { Card } from "../shared/Carda";
|
||||
import ellipsize from "ellipsize";
|
||||
import { LIBRARY_SERVICE_HOST } from "../../constants/endpoints";
|
||||
import { LIBRARY_SERVICE_HOST, LIBRARY_SERVICE_BASE_URI } from "../../constants/endpoints";
|
||||
import { escapePoundSymbol } from "../../shared/utils/formatting.utils";
|
||||
import prettyBytes from "pretty-bytes";
|
||||
|
||||
@@ -24,21 +24,18 @@ interface ComicBookMatch {
|
||||
};
|
||||
}
|
||||
|
||||
// Local state type for redux selector
|
||||
interface LocalRootState {
|
||||
comicInfo?: {
|
||||
comicBooksDetails?: ComicBookMatch[];
|
||||
};
|
||||
}
|
||||
|
||||
const PotentialLibraryMatches = (props: PotentialLibraryMatchesProps): ReactElement => {
|
||||
const dispatch = useDispatch();
|
||||
const comicBooks = useSelector(
|
||||
(state: LocalRootState) => state.comicInfo?.comicBooksDetails,
|
||||
);
|
||||
useEffect(() => {
|
||||
dispatch(getComicBooksDetailsByIds(props.matches));
|
||||
}, []);
|
||||
const { data } = useQuery({
|
||||
queryKey: ["comicBooksDetails", props.matches],
|
||||
queryFn: async () =>
|
||||
axios({
|
||||
url: `${LIBRARY_SERVICE_BASE_URI}/getComicBooksByIds`,
|
||||
method: "POST",
|
||||
data: { ids: props.matches },
|
||||
}),
|
||||
enabled: props.matches.length > 0,
|
||||
});
|
||||
const comicBooks: ComicBookMatch[] = data?.data ?? [];
|
||||
return (
|
||||
<div className="potential-matches-container mt-10">
|
||||
{isArray(comicBooks) ? (
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { isEmpty, isNil, isUndefined, map, partialRight, pick } from "lodash";
|
||||
import React, { ReactElement, useState, useCallback } from "react";
|
||||
import { useParams } from "react-router";
|
||||
import { analyzeLibrary } from "../../actions/comicinfo.actions";
|
||||
import { useQuery, useMutation } from "@tanstack/react-query";
|
||||
import PotentialLibraryMatches from "./PotentialLibraryMatches";
|
||||
import { Card } from "../shared/Carda";
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
/**
|
||||
* @fileoverview Redux action type constants for the application.
|
||||
* Organizes action types by feature/domain for better maintainability.
|
||||
* @module constants/action-types
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
// COMICVINE API ACTION TYPES
|
||||
// =============================================================================
|
||||
|
||||
/** @constant {string} CV_API_CALL_IN_PROGRESS - ComicVine API call started */
|
||||
export const CV_API_CALL_IN_PROGRESS = "CV_SEARCH_IN_PROGRESS";
|
||||
/** @constant {string} CV_SEARCH_FAILURE - ComicVine search failed */
|
||||
export const CV_SEARCH_FAILURE = "CV_SEARCH_FAILURE";
|
||||
/** @constant {string} CV_SEARCH_SUCCESS - ComicVine search completed successfully */
|
||||
export const CV_SEARCH_SUCCESS = "CV_SEARCH_SUCCESS";
|
||||
/** @constant {string} CV_CLEANUP - Reset ComicVine state */
|
||||
export const CV_CLEANUP = "CV_CLEANUP";
|
||||
|
||||
/** @constant {string} CV_API_GENERIC_FAILURE - Generic ComicVine API error */
|
||||
export const CV_API_GENERIC_FAILURE = "CV_API_GENERIC_FAILURE";
|
||||
|
||||
export const IMS_COMICBOOK_METADATA_FETCHED = "IMS_SOCKET_DATA_FETCHED";
|
||||
|
||||
export const IMS_RAW_IMPORT_SUCCESSFUL = "IMS_RAW_IMPORT_SUCCESSFUL";
|
||||
export const IMS_RAW_IMPORT_FAILED = "IMS_RAW_IMPORT_FAILED";
|
||||
|
||||
// Library service generic action types
|
||||
export const LS_IMPORT_CALL_IN_PROGRESS = "LS_IMPORT_CALL_IN_PROGRESS";
|
||||
// Library import bull mq queue control
|
||||
export const LS_TOGGLE_IMPORT_QUEUE = "LS_TOGGLE_IMPORT_QUEUE";
|
||||
|
||||
// ComicVine Metadata
|
||||
export const IMS_CV_METADATA_IMPORT_CALL_IN_PROGRESS =
|
||||
"IMS_CV_METADATA_IMPORT_CALL_IN_PROGRESS";
|
||||
export const IMS_CV_METADATA_IMPORT_SUCCESSFUL =
|
||||
"IMS_CV_METADATA_IMPORT_SUCCESSFUL";
|
||||
export const IMS_CV_METADATA_IMPORT_FAILED = "IMS_CV_METADATA_IMPORT_FAILED";
|
||||
|
||||
export const IMS_RECENT_COMICS_FETCHED = "IMS_RECENT_COMICS_FETCHED";
|
||||
export const IMS_DATA_FETCH_ERROR = "IMS_DATA_FETCH_ERROR";
|
||||
|
||||
// Weekly pull list
|
||||
export const CV_WEEKLY_PULLLIST_CALL_IN_PROGRESS =
|
||||
"CV_WEEKLY_PULLLIST_CALL_IN_PROGRESS";
|
||||
export const CV_WEEKLY_PULLLIST_FETCHED = "CV_WEEKLY_PULLLIST_FETCHED";
|
||||
export const CV_WEEKLY_PULLLIST_ERROR = "CV_WEEKLY_PULLLIST_ERROR";
|
||||
|
||||
// Single or multiple comic book mongo objects
|
||||
export const IMS_COMIC_BOOK_DB_OBJECT_FETCHED =
|
||||
"IMS_COMIC_BOOK_DB_OBJECT_FETCHED";
|
||||
export const IMS_COMIC_BOOKS_DB_OBJECTS_FETCHED =
|
||||
"IMS_COMIC_BOOKS_DB_OBJECTS_FETCHED";
|
||||
export const IMS_COMIC_BOOK_DB_OBJECT_CALL_IN_PROGRESS =
|
||||
"IMS_COMIC_BOOK_DB_OBJECT_CALL_IN_PROGRESS";
|
||||
export const IMS_COMIC_BOOK_DB_OBJECT_CALL_FAILED =
|
||||
"IMS_COMIC_BOOK_DB_OBJECT_CALL_FAILED";
|
||||
|
||||
// wanted comics from CV, LoCG and other sources
|
||||
export const IMS_WANTED_COMICS_FETCHED = "IMS_WANTED_COMICS_FETCHED";
|
||||
|
||||
// volume groups
|
||||
export const IMS_COMIC_BOOK_GROUPS_FETCHED = "IMS_COMIC_BOOK_GROUPS_FETCHED";
|
||||
export const IMS_COMIC_BOOK_GROUPS_CALL_IN_PROGRESS =
|
||||
"IMS_COMIC_BOOK_GROUPS_CALL_IN_PROGRESS";
|
||||
export const IMS_COMIC_BOOK_GROUPS_CALL_FAILED =
|
||||
"IMS_COMIC_BOOK_GROUPS_CALL_FAILED";
|
||||
export const VOLUMES_FETCHED = "VOLUMES_FETCHED";
|
||||
|
||||
// search results from the Search service
|
||||
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_IN_PROGRESS = "SS_SEARCH_IN_PROGRESS";
|
||||
export const SS_SEARCH_FAILED = "SS_SEARCH_FAILED";
|
||||
|
||||
// issues for a given volume
|
||||
export const CV_ISSUES_METADATA_CALL_IN_PROGRESS =
|
||||
"CV_ISSUES_METADATA_CALL_IN_PROGRESS";
|
||||
export const CV_ISSUES_METADATA_FETCH_SUCCESS =
|
||||
"CV_ISSUES_METADATA_FETCH_SUCCESS";
|
||||
export const CV_ISSUES_METADATA_FETCH_FAILED =
|
||||
"CV_ISSUES_METADATA_FETCH_FAILED";
|
||||
export const CV_ISSUES_FOR_VOLUME_IN_LIBRARY_SUCCESS =
|
||||
"CV_ISSUES_FOR_VOLUME_IN_LIBRARY_SUCCESS";
|
||||
export const CV_ISSUES_FOR_VOLUME_IN_LIBRARY_UPDATED =
|
||||
"CV_ISSUES_FOR_VOLUME_IN_LIBRARY_UPDATED";
|
||||
export const CV_ISSUES_MATCHES_IN_LIBRARY_FETCHED =
|
||||
"CV_ISSUES_MATCHES_IN_LIBRARY_FETCHED";
|
||||
|
||||
// extracted comic archive
|
||||
export const IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_SUCCESS =
|
||||
"IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_SUCCESS";
|
||||
export const IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_IN_PROGRESS =
|
||||
"IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_IN_PROGRESS";
|
||||
export const IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_FAILED =
|
||||
"IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_FAILED";
|
||||
|
||||
export const COMICBOOK_EXTRACTION_SUCCESS = "COMICBOOK_EXTRACTION_SUCCESS";
|
||||
|
||||
// Image file stats
|
||||
export const IMG_ANALYSIS_CALL_IN_PROGRESS = "IMG_ANALYSIS_CALL_IN_PROGRESS";
|
||||
export const IMG_ANALYSIS_DATA_FETCH_SUCCESS =
|
||||
"IMG_ANALYSIS_DATA_FETCH_SUCCESS";
|
||||
export const IMG_ANALYSIS_DATA_FETCH_ERROR = "IMG_ANALYSIS_DATA_FETCH_ERROR";
|
||||
|
||||
// library statistics
|
||||
export const LIBRARY_STATISTICS_CALL_IN_PROGRESS =
|
||||
"LIBRARY_STATISTICS_CALL_IN_PROGRESS";
|
||||
export const LIBRARY_STATISTICS_FETCHED = "LIBRARY_STATISTICS_FETCHED";
|
||||
export const LIBRARY_STATISTICS_FETCH_ERROR = "LIBRARY_STATISTICS_FETCH_ERROR";
|
||||
|
||||
// fileops cleanup
|
||||
export const FILEOPS_STATE_RESET = "FILEOPS_STATE_RESET";
|
||||
|
||||
// AirDC++
|
||||
export const AIRDCPP_SEARCH_IN_PROGRESS = "AIRDCPP_SEARCH_IN_PROGRESS";
|
||||
export const AIRDCPP_SEARCH_RESULTS_ADDED = "AIRDCPP_SEARCH_RESULTS_ADDED";
|
||||
export const AIRDCPP_SEARCH_RESULTS_UPDATED = "AIRDCPP_SEARCH_RESULTS_UPDATED";
|
||||
export const AIRDCPP_SEARCH_COMPLETE = "AIRDCPP_SEARCH_COMPLETE";
|
||||
|
||||
// AirDC++ related library query for issues with bundles associated with them
|
||||
export const LIBRARY_ISSUE_BUNDLES = "LIBRARY_ISSUE_BUNDLES";
|
||||
|
||||
export const AIRDCPP_HUB_SEARCHES_SENT = "AIRDCPP_HUB_SEARCHES_SENT";
|
||||
export const AIRDCPP_RESULT_DOWNLOAD_INITIATED =
|
||||
"AIRDCPP_RESULT_DOWNLOAD_INITIATED";
|
||||
export const AIRDCPP_FILE_DOWNLOAD_COMPLETED =
|
||||
"AIRDCPP_FILE_DOWNLOAD_COMPLETED";
|
||||
export const LS_SINGLE_IMPORT = "LS_SINGLE_IMPORT";
|
||||
export const AIRDCPP_BUNDLES_FETCHED = "AIRDCPP_BUNDLES_FETCHED";
|
||||
export const AIRDCPP_DOWNLOAD_PROGRESS_TICK = "AIRDCPP_DOWNLOAD_PROGRESS_TICK";
|
||||
export const AIRDCPP_SOCKET_CONNECTED = "AIRDCPP_SOCKET_CONNECTED";
|
||||
export const AIRDCPP_SOCKET_DISCONNECTED = "AIRDCPP_SOCKET_DISCONNECTED";
|
||||
|
||||
// Transfers
|
||||
export const AIRDCPP_TRANSFERS_FETCHED = "AIRDCPP_TRANSFERS_FETCHED";
|
||||
|
||||
// Comics marked as "wanted"
|
||||
export const WANTED_COMICS_FETCHED = "WANTED_COMICS_FETCHED";
|
||||
|
||||
// LIBRARY Service import queue-related action types
|
||||
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";
|
||||
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
|
||||
export const SETTINGS_CALL_IN_PROGRESS = "SETTINGS_CALL_IN_PROGRESS";
|
||||
export const SETTINGS_OBJECT_FETCHED = "SETTINGS_OBJECT_FETCHED";
|
||||
export const SETTINGS_CALL_FAILED = "SETTINGS_CALL_FAILED";
|
||||
export const SETTINGS_OBJECT_DELETED = "SETTINGS_OBJECT_DELETED";
|
||||
export const SETTINGS_DB_FLUSH_SUCCESS = "SETTINGS_DB_FLUSH_SUCCESS";
|
||||
|
||||
export const SETTINGS_QBITTORRENT_TORRENTS_LIST_FETCHED = "SETTINGS_QBITTORRENT_TORRENTS_LIST_FETCHED";
|
||||
|
||||
// Metron Metadata
|
||||
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_ERROR = "METRON_DATA_FETCH_ERROR";
|
||||
|
||||
// service health statuses
|
||||
export const LIBRARY_SERVICE_HEALTH = "LIBRARY_SERVICE_HEALTH";
|
||||
Reference in New Issue
Block a user