Removed react-redux from project

This commit is contained in:
2026-04-15 20:15:19 -04:00
parent 3ea9b83ed9
commit 00adbb2c4a
16 changed files with 3410 additions and 4621 deletions

View File

@@ -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",

View File

@@ -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;
}
};

View File

@@ -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,
});
};

View File

@@ -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,
});
};

View File

@@ -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,
},
};
};

View File

@@ -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) => {};

View File

@@ -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 (

View File

@@ -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>;

View File

@@ -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],
);

View File

@@ -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,

View File

@@ -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";

View File

@@ -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">

View File

@@ -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) ? (

View File

@@ -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";

View File

@@ -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";

6408
yarn.lock

File diff suppressed because it is too large Load Diff