📔 JsDoc for index.ts
This commit is contained in:
@@ -307,7 +307,7 @@ export const Import = (props: IProps): ReactElement => {
|
||||
<td className="whitespace-nowrap px-2 py-2 text-gray-700 dark:text-slate-300">
|
||||
{format(
|
||||
new Date(jobResult.earliestTimestamp),
|
||||
"EEEE, hh:mma, do LLLL Y",
|
||||
"EEEE, hh:mma, do LLLL y",
|
||||
)}
|
||||
</td>
|
||||
<td className="whitespace-nowrap px-2 py-2 text-gray-700 dark:text-slate-300">
|
||||
|
||||
@@ -278,23 +278,9 @@ export type InferredMetadataInput = {
|
||||
|
||||
export type Issue = {
|
||||
__typename?: 'Issue';
|
||||
api_detail_url?: Maybe<Scalars['String']['output']>;
|
||||
character_credits?: Maybe<Array<CharacterCredit>>;
|
||||
cover_date?: Maybe<Scalars['String']['output']>;
|
||||
description?: Maybe<Scalars['String']['output']>;
|
||||
id: Scalars['Int']['output'];
|
||||
image?: Maybe<ImageUrls>;
|
||||
issue_number?: Maybe<Scalars['String']['output']>;
|
||||
location_credits?: Maybe<Array<LocationCredit>>;
|
||||
name?: Maybe<Scalars['String']['output']>;
|
||||
number?: Maybe<Scalars['Int']['output']>;
|
||||
person_credits?: Maybe<Array<PersonCredit>>;
|
||||
site_detail_url?: Maybe<Scalars['String']['output']>;
|
||||
store_date?: Maybe<Scalars['String']['output']>;
|
||||
story_arc_credits?: Maybe<Array<StoryArcCredit>>;
|
||||
subtitle?: Maybe<Scalars['String']['output']>;
|
||||
team_credits?: Maybe<Array<TeamCredit>>;
|
||||
volume?: Maybe<Volume>;
|
||||
year?: Maybe<Scalars['String']['output']>;
|
||||
};
|
||||
|
||||
@@ -534,9 +520,10 @@ export type PullListItem = {
|
||||
__typename?: 'PullListItem';
|
||||
cover?: Maybe<Scalars['String']['output']>;
|
||||
description?: Maybe<Scalars['String']['output']>;
|
||||
name: Scalars['String']['output'];
|
||||
name?: Maybe<Scalars['String']['output']>;
|
||||
potw?: Maybe<Scalars['Int']['output']>;
|
||||
price?: Maybe<Scalars['String']['output']>;
|
||||
publicationDate?: Maybe<Scalars['String']['output']>;
|
||||
publisher?: Maybe<Scalars['String']['output']>;
|
||||
pulls?: Maybe<Scalars['Int']['output']>;
|
||||
rating?: Maybe<Scalars['Float']['output']>;
|
||||
@@ -1009,7 +996,7 @@ export type GetWeeklyPullListQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type GetWeeklyPullListQuery = { __typename?: 'Query', getWeeklyPullList: { __typename?: 'PullListResponse', result: Array<{ __typename?: 'PullListItem', name: string, publisher?: string | null, cover?: string | null }> } };
|
||||
export type GetWeeklyPullListQuery = { __typename?: 'Query', getWeeklyPullList: { __typename?: 'PullListResponse', result: Array<{ __typename?: 'PullListItem', name?: string | null, publisher?: string | null, cover?: string | null }> } };
|
||||
|
||||
export type GetLibraryComicsQueryVariables = Exact<{
|
||||
page?: InputMaybe<Scalars['Int']['input']>;
|
||||
|
||||
@@ -1,177 +1,120 @@
|
||||
import { create } from "zustand";
|
||||
import io, { Socket } from "socket.io-client";
|
||||
import { SOCKET_BASE_URI } from "../constants/endpoints";
|
||||
import { isNil } from "lodash";
|
||||
import { QueryClient } from "@tanstack/react-query";
|
||||
import { toast } from "react-toastify";
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
// Type for global state
|
||||
/**
|
||||
* Global application state interface
|
||||
*/
|
||||
interface StoreState {
|
||||
/** Active socket.io connections by namespace */
|
||||
socketInstances: Record<string, Socket>;
|
||||
/**
|
||||
* Get or create socket connection for namespace
|
||||
* @param namespace - Socket namespace (default: "/")
|
||||
* @returns Socket instance
|
||||
*/
|
||||
getSocket: (namespace?: string) => Socket;
|
||||
/**
|
||||
* Disconnect and remove socket instance
|
||||
* @param namespace - Socket namespace to disconnect
|
||||
*/
|
||||
disconnectSocket: (namespace: string) => void;
|
||||
|
||||
/** ComicVine scraping state */
|
||||
comicvine: {
|
||||
scrapingStatus: string;
|
||||
};
|
||||
|
||||
/** Import job queue state and actions */
|
||||
importJobQueue: {
|
||||
successfulJobCount: number;
|
||||
failedJobCount: number;
|
||||
status: string | undefined;
|
||||
mostRecentImport: string | null;
|
||||
|
||||
setStatus: (status: string) => void;
|
||||
setJobCount: (jobType: string, count: number) => void;
|
||||
setMostRecentImport: (fileName: string) => void;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Zustand store for global app state and socket management
|
||||
*/
|
||||
export const useStore = create<StoreState>((set, get) => ({
|
||||
socketInstances: {},
|
||||
|
||||
getSocket: (namespace = "/") => {
|
||||
const fullNamespace = namespace === "/" ? "" : namespace;
|
||||
const ns = namespace === "/" ? "" : namespace;
|
||||
const existing = get().socketInstances[namespace];
|
||||
|
||||
if (existing && existing.connected) return existing;
|
||||
if (existing?.connected) return existing;
|
||||
|
||||
const sessionId = localStorage.getItem("sessionId");
|
||||
const socket = io(`${SOCKET_BASE_URI}${fullNamespace}`, {
|
||||
const socket = io(`${SOCKET_BASE_URI}${ns}`, {
|
||||
transports: ["websocket"],
|
||||
withCredentials: true,
|
||||
query: { sessionId },
|
||||
});
|
||||
|
||||
socket.on("connect", () => {
|
||||
// Socket connected successfully
|
||||
});
|
||||
socket.on("sessionInitialized", (id) => localStorage.setItem("sessionId", id));
|
||||
if (sessionId) socket.emit("call", "socket.resumeSession", { sessionId, namespace });
|
||||
|
||||
// Always listen for sessionInitialized in case backend creates a new session
|
||||
socket.on("sessionInitialized", (id) => {
|
||||
localStorage.setItem("sessionId", id);
|
||||
});
|
||||
socket.on("RESTORE_JOB_COUNTS_AFTER_SESSION_RESTORATION", ({ completedJobCount, failedJobCount, queueStatus }) =>
|
||||
set((s) => ({ importJobQueue: { ...s.importJobQueue, successfulJobCount: completedJobCount, failedJobCount, status: queueStatus } }))
|
||||
);
|
||||
|
||||
if (sessionId) {
|
||||
socket.emit("call", "socket.resumeSession", { sessionId, namespace });
|
||||
}
|
||||
socket.on("LS_COVER_EXTRACTED", ({ completedJobCount, importResult }) =>
|
||||
set((s) => ({ importJobQueue: { ...s.importJobQueue, successfulJobCount: completedJobCount, mostRecentImport: importResult.data.rawFileDetails.name } }))
|
||||
);
|
||||
|
||||
socket.on("RESTORE_JOB_COUNTS_AFTER_SESSION_RESTORATION", (data) => {
|
||||
const { completedJobCount, failedJobCount, queueStatus } = data;
|
||||
set((state) => ({
|
||||
importJobQueue: {
|
||||
...state.importJobQueue,
|
||||
successfulJobCount: completedJobCount,
|
||||
failedJobCount,
|
||||
status: queueStatus,
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
socket.on("LS_COVER_EXTRACTED", ({ completedJobCount, importResult }) => {
|
||||
set((state) => ({
|
||||
importJobQueue: {
|
||||
...state.importJobQueue,
|
||||
successfulJobCount: completedJobCount,
|
||||
mostRecentImport: importResult.data.rawFileDetails.name,
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
socket.on("LS_COVER_EXTRACTION_FAILED", ({ failedJobCount }) => {
|
||||
set((state) => ({
|
||||
importJobQueue: {
|
||||
...state.importJobQueue,
|
||||
failedJobCount,
|
||||
},
|
||||
}));
|
||||
});
|
||||
socket.on("LS_COVER_EXTRACTION_FAILED", ({ failedJobCount }) =>
|
||||
set((s) => ({ importJobQueue: { ...s.importJobQueue, failedJobCount } }))
|
||||
);
|
||||
|
||||
socket.on("LS_IMPORT_QUEUE_DRAINED", () => {
|
||||
set((state) => ({
|
||||
importJobQueue: {
|
||||
...state.importJobQueue,
|
||||
status: "drained",
|
||||
},
|
||||
}));
|
||||
// Delay query invalidation and sessionId removal to ensure backend has persisted data
|
||||
set((s) => ({ importJobQueue: { ...s.importJobQueue, status: "drained" } }));
|
||||
setTimeout(() => {
|
||||
queryClient.invalidateQueries({ queryKey: ["allImportJobResults"] });
|
||||
localStorage.removeItem("sessionId");
|
||||
}, 500);
|
||||
});
|
||||
|
||||
socket.on("CV_SCRAPING_STATUS", (data) => {
|
||||
set((state) => ({
|
||||
comicvine: {
|
||||
...state.comicvine,
|
||||
scrapingStatus: data.message,
|
||||
},
|
||||
}));
|
||||
});
|
||||
socket.on("CV_SCRAPING_STATUS", ({ message }) =>
|
||||
set((s) => ({ comicvine: { ...s.comicvine, scrapingStatus: message } }))
|
||||
);
|
||||
|
||||
socket.on("searchResultsAvailable", (data) => {
|
||||
toast(`Results found for query: ${JSON.stringify(data.query, null, 2)}`);
|
||||
});
|
||||
|
||||
set((state) => ({
|
||||
socketInstances: {
|
||||
...state.socketInstances,
|
||||
[namespace]: socket,
|
||||
},
|
||||
}));
|
||||
socket.on("searchResultsAvailable", ({ query }) =>
|
||||
toast(`Results found for query: ${JSON.stringify(query, null, 2)}`)
|
||||
);
|
||||
|
||||
set((s) => ({ socketInstances: { ...s.socketInstances, [namespace]: socket } }));
|
||||
return socket;
|
||||
},
|
||||
|
||||
disconnectSocket: (namespace: string) => {
|
||||
disconnectSocket: (namespace) => {
|
||||
const socket = get().socketInstances[namespace];
|
||||
if (socket) {
|
||||
socket.disconnect();
|
||||
set((state) => {
|
||||
const { [namespace]: _, ...rest } = state.socketInstances;
|
||||
set((s) => {
|
||||
const { [namespace]: _, ...rest } = s.socketInstances;
|
||||
return { socketInstances: rest };
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
comicvine: {
|
||||
scrapingStatus: "",
|
||||
},
|
||||
comicvine: { scrapingStatus: "" },
|
||||
|
||||
importJobQueue: {
|
||||
successfulJobCount: 0,
|
||||
failedJobCount: 0,
|
||||
status: undefined,
|
||||
mostRecentImport: null,
|
||||
|
||||
setStatus: (status: string) =>
|
||||
set((state) => ({
|
||||
importJobQueue: {
|
||||
...state.importJobQueue,
|
||||
status,
|
||||
},
|
||||
})),
|
||||
|
||||
setJobCount: (jobType: string, count: number) =>
|
||||
set((state) => ({
|
||||
importJobQueue: {
|
||||
...state.importJobQueue,
|
||||
...(jobType === "successful"
|
||||
? { successfulJobCount: count }
|
||||
: { failedJobCount: count }),
|
||||
},
|
||||
})),
|
||||
|
||||
setMostRecentImport: (fileName: string) =>
|
||||
set((state) => ({
|
||||
importJobQueue: {
|
||||
...state.importJobQueue,
|
||||
mostRecentImport: fileName,
|
||||
},
|
||||
})),
|
||||
setStatus: (status) => set((s) => ({ importJobQueue: { ...s.importJobQueue, status } })),
|
||||
setJobCount: (jobType, count) => set((s) => ({
|
||||
importJobQueue: { ...s.importJobQueue, [jobType === "successful" ? "successfulJobCount" : "failedJobCount"]: count }
|
||||
})),
|
||||
setMostRecentImport: (fileName) => set((s) => ({ importJobQueue: { ...s.importJobQueue, mostRecentImport: fileName } })),
|
||||
},
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user