import React, { useState, ReactElement, useCallback } from "react"; import { useParams } from "react-router-dom"; import Card from "../shared/Carda"; import { ComicVineMatchPanel } from "./ComicVineMatchPanel"; import { RawFileDetails } from "./RawFileDetails"; import { ComicVineSearchForm } from "./ComicVineSearchForm"; import TabControls from "./TabControls"; import { EditMetadataPanel } from "./EditMetadataPanel"; import { Menu } from "./ActionMenu/Menu"; import { ArchiveOperations } from "./Tabs/ArchiveOperations"; import { ComicInfoXML } from "./Tabs/ComicInfoXML"; import AcquisitionPanel from "./AcquisitionPanel"; import DownloadsPanel from "./DownloadsPanel"; import { VolumeInformation } from "./Tabs/VolumeInformation"; import { isEmpty, isUndefined, isNil, filter } from "lodash"; import { components } from "react-select"; import { RootState } from "threetwo-ui-typings"; import "react-sliding-pane/dist/react-sliding-pane.css"; import "react-loader-spinner/dist/loader/css/react-spinner-loader.css"; import Loader from "react-loader-spinner"; import SlidingPane from "react-sliding-pane"; import Modal from "react-modal"; import ComicViewer from "react-comic-viewer"; import { extractComicArchive } from "../../actions/fileops.actions"; import { determineCoverFile } from "../../shared/utils/metadata.utils"; import axios from "axios"; import { styled } from "styled-components"; import { COMICVINE_SERVICE_URI } from "../../constants/endpoints"; import { refineQuery } from "filename-parser"; type ComicDetailProps = {}; /** * Component for displaying the metadata for a comic in greater detail. * * @component * @example * return ( * * ) */ export const ComicDetail = (data: ComicDetailProps): ReactElement => { const { data: { _id, rawFileDetails, inferredMetadata, sourcedMetadata: { comicvine, locg, comicInfo }, acquisition, createdAt, updatedAt, }, userSettings, } = data; const [page, setPage] = useState(1); const [visible, setVisible] = useState(false); const [slidingPanelContentId, setSlidingPanelContentId] = useState(""); const [modalIsOpen, setIsOpen] = useState(false); const [comicVineMatches, setComicVineMatches] = useState([]); const { comicObjectId } = useParams<{ comicObjectId: string }>(); // const dispatch = useDispatch(); const openModal = useCallback((filePath) => { setIsOpen(true); // dispatch( // extractComicArchive(filePath, { // type: "full", // purpose: "reading", // imageResizeOptions: { // baseWidth: 1024, // }, // }), // ); }, []); // overridden with some styles const StyledSlidingPanel = styled(SlidingPane)` background: #ccc; `; const afterOpenModal = useCallback((things) => { // references are now sync'd and can be accessed. // subtitle.style.color = "#f00"; console.log("kolaveri", things); }, []); const closeModal = useCallback(() => { setIsOpen(false); }, []); // sliding panel init const contentForSlidingPanel = { CVMatches: { content: (props) => ( <>

Searching for:

{inferredMetadata.issue ? ( <> {inferredMetadata.issue.name} # {inferredMetadata.issue.number} ) : null}
), }, editComicBookMetadata: { content: () => , }, }; // Actions const fetchComicVineMatches = async ( searchPayload, issueSearchQuery, seriesSearchQuery, ) => { try { const response = await axios({ 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, }, transformResponse: (r) => { const matches = JSON.parse(r); return matches; // return sortBy(matches, (match) => -match.score); }, }); let matches: any = []; if (!isNil(response.data.results) && response.data.results.length === 1) { matches = response.data.results; } else { matches = response.data.map((match) => match); } const scoredMatches = matches.sort((a, b) => b.score - a.score); setComicVineMatches(scoredMatches); } 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(rawFileDetails, 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); // check for the availability of rawFileDetails const areRawFileDetailsAvailable = !isUndefined(rawFileDetails) && !isEmpty(rawFileDetails); const { issueName, url } = determineCoverFile({ rawFileDetails, comicvine, locg, }); // query for airdc++ const airDCPPQuery = { issue: { name: issueName, }, }; // Tab content and header details const tabGroup = [ { id: 1, name: "Volume Information", icon: , content: isComicBookMetadataAvailable ? ( ) : null, shouldShow: isComicBookMetadataAvailable, }, { id: 2, name: "ComicInfo.xml", icon: ( ), content: (
{!isNil(comicInfo) && }
), shouldShow: !isEmpty(comicInfo), }, { id: 3, icon: ( ), name: "Archive Operations", content: , shouldShow: areRawFileDetailsAvailable, }, { id: 4, icon: ( ), name: "DC++ Search", content: ( ), shouldShow: true, }, { id: 5, icon: ( ), name: "Torrent Search", content: <>Torrents, shouldShow: true, }, { id: 6, name: "Downloads", icon: <>{acquisition?.directconnect?.downloads?.length}, content: !isNil(data.data) && !isEmpty(data.data) ? ( ) : (
AirDC++ is not configured. Please configure it in{" "} Settings.
), shouldShow: true, }, ]; // filtered Tabs const filteredTabs = tabGroup.filter((tab) => tab.shouldShow); // Determine which cover image to use: // 1. from the locally imported or // 2. from the CV-scraped version return (
{!isNil(data) && !isEmpty(data) && ( <>
{/* raw file details */} {!isUndefined(rawFileDetails) && !isEmpty(rawFileDetails.cover) && (
{/* action dropdown */}
{/* {extractedComicBook && ( )} */}
)}
setVisible(false)} title={"Comic Vine Search Matches"} width={"600px"} > {slidingPanelContentId !== "" && contentForSlidingPanel[slidingPanelContentId].content()} )}
); }; export default ComicDetail;