+
@@ -114,29 +119,13 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
# {inferredMetadata.issue.number}
) : null}
- {!comicVineAPICallProgress ? (
-
- ) : (
-
- )} */}
+
+
>
),
},
@@ -146,6 +135,154 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
},
};
+ // Actions
+
+ const fetchComicVineMatches = async (
+ searchPayload,
+ issueSearchQuery,
+ seriesSearchQuery,
+ ) => {
+ try {
+ await 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) => {
+ console.log(response);
+ let matches: any = [];
+ if (
+ !isNil(response.data.results) &&
+ response.data.results.length === 1
+ ) {
+ matches = response.data.results;
+ } else {
+ matches = response.data.map((match) => match);
+ }
+ setComicVineMatches(matches);
+ });
+ } catch (err) {
+ console.log(err);
+ }
+ };
+
+ // Action event handlers
+ const openDrawerWithCVMatches = () => {
+ let seriesSearchQuery: IComicVineSearchQuery = {} as IComicVineSearchQuery;
+ let issueSearchQuery: IComicVineSearchQuery = {} as IComicVineSearchQuery;
+
+ if (!isUndefined(rawFileDetails)) {
+ issueSearchQuery = refineQuery(rawFileDetails.name);
+ } else if (!isEmpty(comicvine)) {
+ issueSearchQuery = refineQuery(comicvine.name);
+ }
+ fetchComicVineMatches(data, issueSearchQuery, seriesSearchQuery);
+ setSlidingPanelContentId("CVMatches");
+ setVisible(true);
+ };
+
+ const openEditMetadataPanel = useCallback(() => {
+ setSlidingPanelContentId("editComicBookMetadata");
+ setVisible(true);
+ }, []);
+
+ // Actions menu options and handler
+ const CVMatchLabel = (
+
+
+
+
+ Match on ComicVine
+
+ );
+ const editLabel = (
+
+
+
+
+ Edit Metadata
+
+ );
+ const deleteLabel = (
+
+
+
+
+ Delete Comic
+
+ );
+ const Placeholder = (props) => {
+ return
;
+ };
+ const actionOptions = [
+ { value: "match-on-comic-vine", label: CVMatchLabel },
+ { value: "edit-metdata", label: editLabel },
+ { value: "delete-comic", label: deleteLabel },
+ ];
+
+ const filteredActionOptions = filter(actionOptions, (item) => {
+ if (isUndefined(rawFileDetails)) {
+ return item.value !== "match-on-comic-vine";
+ }
+ return item;
+ });
+ const handleActionSelection = (action) => {
+ switch (action.value) {
+ case "match-on-comic-vine":
+ openDrawerWithCVMatches();
+ break;
+ case "edit-metdata":
+ openEditMetadataPanel();
+ break;
+ default:
+ console.log("No valid action selected.");
+ break;
+ }
+ };
+ const customStyles = {
+ menu: (base) => ({
+ ...base,
+ backgroundColor: "rgb(156, 163, 175)",
+ }),
+ placeholder: (base) => ({
+ ...base,
+ color: "black",
+ }),
+ option: (base, { data, isDisabled, isFocused, isSelected }) => ({
+ ...base,
+ backgroundColor: isFocused ? "gray" : "rgb(156, 163, 175)",
+ }),
+ singleValue: (base) => ({
+ ...base,
+ paddingTop: "0.4rem",
+ }),
+ control: (base) => ({
+ ...base,
+ backgroundColor: "rgb(156, 163, 175)",
+ color: "black",
+ border: "1px solid rgb(156, 163, 175)",
+ }),
+ };
+
// check for the availability of CV metadata
const isComicBookMetadataAvailable =
!isUndefined(comicvine) && !isUndefined(comicvine.volumeInformation);
@@ -285,6 +422,12 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
diff --git a/src/client/components/ComicDetail/ComicVineMatchPanel.tsx b/src/client/components/ComicDetail/ComicVineMatchPanel.tsx
index dd731b2..2b204d9 100644
--- a/src/client/components/ComicDetail/ComicVineMatchPanel.tsx
+++ b/src/client/components/ComicDetail/ComicVineMatchPanel.tsx
@@ -2,22 +2,26 @@ import React, { ReactElement } from "react";
import { ComicVineSearchForm } from "../ComicVineSearchForm";
import MatchResult from "./MatchResult";
import { isEmpty } from "lodash";
+import { useStore } from "../../store";
+import { useShallow } from "zustand/react/shallow";
export const ComicVineMatchPanel = (comicVineData): ReactElement => {
- const {
- comicObjectId,
- comicVineSearchQueryObject,
- comicVineAPICallProgress,
- comicVineSearchResults,
- } = comicVineData.props;
+ const { comicObjectId, comicVineMatches } = comicVineData.props;
+ const { comicvine } = useStore(
+ useShallow((state) => ({
+ comicvine: state.comicvine,
+ })),
+ );
return (
<>
-
- {!isEmpty(comicVineSearchResults) && (
+
+ {!isEmpty(comicVineMatches) ? (
+ ) : (
+ <>{comicvine.scrapingStatus}>
)}
>
diff --git a/src/client/components/ComicDetail/MatchResult.tsx b/src/client/components/ComicDetail/MatchResult.tsx
index 7b13ba0..bc4e968 100644
--- a/src/client/components/ComicDetail/MatchResult.tsx
+++ b/src/client/components/ComicDetail/MatchResult.tsx
@@ -1,8 +1,9 @@
-import React, { useCallback } from "react";
+import React from "react";
import { isNil, map } from "lodash";
-import { applyComicVineMatch } from "../../actions/comicinfo.actions";
import { convert } from "html-to-text";
import ellipsize from "ellipsize";
+import { LIBRARY_SERVICE_BASE_URI } from "../../constants/endpoints";
+import axios from "axios";
interface MatchResultProps {
matchData: any;
@@ -14,12 +15,16 @@ const handleBrokenImage = (e) => {
};
export const MatchResult = (props: MatchResultProps) => {
- const applyCVMatch = useCallback(
- // (match, comicObjectId) => {
- // dispatch(applyComicVineMatch(match, comicObjectId));
- // },
- [],
- );
+ const applyCVMatch = async (match, comicObjectId) => {
+ return await axios.request({
+ url: `${LIBRARY_SERVICE_BASE_URI}/applyComicVineMetadata`,
+ method: "POST",
+ data: {
+ match,
+ comicObjectId,
+ },
+ });
+ };
return (
<>
{map(props.matchData, (match, idx) => {
@@ -32,45 +37,29 @@ export const MatchResult = (props: MatchResultProps) => {
});
}
return (
-
-
-
+
+
+
-
-
-
{match.name}
-
-
-
-
- Number
-
- {match.issue_number}
-
-
-
-
-
- Cover Date
- {match.cover_date}
-
-
-
-
+
+
{match.name}
+
Number
+
{match.issue_number}
+
Cover Date
+
{match.cover_date}
+
{ellipsize(issueDescription, 300)}
-
-
-
-
+
+

{
/>
-
-
{match.volume.name}
-
-
-
- Total Issues
-
- {match.volumeInformation.results.count_of_issues}
-
-
-
-
-
-
- Publisher
-
- {match.volumeInformation.results.publisher.name}
-
-
-
+
+
{match.volume.name}
+
+ Total Issues
+
+ {match.volumeInformation.results.count_of_issues}
+
+
+
Publisher
+
+ {match.volumeInformation.results.publisher.name}
+
-
-
-
-
-
+
);
})}
diff --git a/src/client/store/index.ts b/src/client/store/index.ts
index 6de5b96..d5eb125 100644
--- a/src/client/store/index.ts
+++ b/src/client/store/index.ts
@@ -1,5 +1,5 @@
import { create } from "zustand";
-import { isEmpty, isNil, isUndefined } from "lodash";
+import { isNil } from "lodash";
import io from "socket.io-client";
import { SOCKET_BASE_URI } from "../constants/endpoints";
import { produce } from "immer";
@@ -28,6 +28,11 @@ export const useStore = create((set, get) => ({
// Socket.io state
socketIOInstance: {},
+ // ComicVine Scraping status
+ comicvine: {
+ scrapingStatus: "",
+ },
+
// Import job queue and associated statuses
importJobQueue: {
successfulJobCount: 0,
@@ -153,6 +158,16 @@ socketIOInstance.on("LS_IMPORT_QUEUE_DRAINED", (data) => {
queryClient.invalidateQueries({ queryKey: ["allImportJobResults"] });
});
+// ComicVine Scraping status
+socketIOInstance.on("CV_SCRAPING_STATUS", (data) => {
+ setState((state) => ({
+ comicvine: {
+ ...state.comicvine,
+ scrapingStatus: data.message,
+ },
+ }));
+});
+
/**
* Method to init AirDC++ Socket with supplied settings
* @param configuration - credentials, and hostname details to init AirDC++ connection