import React, { useState, useEffect, ReactElement, useContext, useCallback, } from "react"; import { useDispatch, useSelector } from "react-redux"; import { useParams } from "react-router-dom"; import Card from "./Carda"; import { ComicVineMatchPanel } from "./ComicDetail/ComicVineMatchPanel"; import { ArchiveOperations } from "./ComicDetail/Tabs/ArchiveOperations"; import { ComicInfoXML } from "./ComicDetail/Tabs/ComicInfoXML"; import AcquisitionPanel from "./ComicDetail/AcquisitionPanel"; import DownloadsPanel from "./ComicDetail/DownloadsPanel"; import { VolumeInformation } from "./ComicDetail/Tabs/VolumeInformation"; import { RawFileDetails } from "./ComicDetail/RawFileDetails"; import TabControls from "./ComicDetail/TabControls"; import { EditMetadataPanel } from "./ComicDetail/EditMetadataPanel"; import { Menu } from "./ComicDetail/ActionMenu/Menu"; import { isEmpty, isUndefined, isNil } from "lodash"; import { RootState } from "threetwo-ui-typings"; import { getComicBookDetailById } from "../actions/comicinfo.actions"; 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 { escapePoundSymbol } from "../shared/utils/formatting.utils"; import { LIBRARY_SERVICE_HOST } from "../constants/endpoints"; import { getSettings } from "../actions/settings.actions"; import { AirDCPPSocketContext } from "../context/AirDCPPSocket"; import AirDCPPSocket from "../services/DcppSearchService"; import { extractComicArchive } from "../actions/fileops.actions"; type ComicDetailProps = {}; /** * Component for displaying the metadata for a comic in greater detail. * * @component * @example * return ( * * ) */ export const ComicDetail = ({}: ComicDetailProps): ReactElement => { const [page, setPage] = useState(1); const [visible, setVisible] = useState(false); const [slidingPanelContentId, setSlidingPanelContentId] = useState(""); const [modalIsOpen, setIsOpen] = useState(false); const comicVineSearchResults = useSelector( (state: RootState) => state.comicInfo.searchResults, ); const comicVineSearchQueryObject = useSelector( (state: RootState) => state.comicInfo.searchQuery, ); const comicVineAPICallProgress = useSelector( (state: RootState) => state.comicInfo.inProgress, ); const comicBookDetailData = useSelector( (state: RootState) => state.comicInfo.comicBookDetail, ); const extractedComicBook = useSelector( (state: RootState) => state.fileOps.extractedComicBookArchive, ); const { comicObjectId } = useParams<{ comicObjectId: string }>(); const userSettings = useSelector((state: RootState) => state.settings.data); const { ADCPPSocket, setADCPPSocket } = useContext(AirDCPPSocketContext); const dispatch = useDispatch(); const openModal = useCallback((filePath) => { setIsOpen(true); dispatch(extractComicArchive(filePath)); }, []); const afterOpenModal = useCallback(() => { // references are now sync'd and can be accessed. // subtitle.style.color = "#f00"; }, []); const closeModal = useCallback(() => { setIsOpen(false); }, []); useEffect(() => { dispatch(getComicBookDetailById(comicObjectId)); dispatch(getSettings()); }, [page, dispatch]); useEffect(() => { if (isEmpty(ADCPPSocket) && !isNil(userSettings.directConnect)) { setADCPPSocket( new AirDCPPSocket({ protocol: `${userSettings.directConnect.client.host.protocol}`, hostname: `${userSettings.directConnect.client.host.hostname}`, }), ); } }, [userSettings]); // sliding panel init const contentForSlidingPanel = { CVMatches: { content: () => { if (!comicVineAPICallProgress) { return ( ); } else { return (
); } }, }, editComicBookMetadata: { content: () => , }, }; // check for the availability of CV metadata const isComicBookMetadataAvailable = comicBookDetailData.sourcedMetadata && !isUndefined(comicBookDetailData.sourcedMetadata.comicvine) && !isUndefined( comicBookDetailData.sourcedMetadata.comicvine.volumeInformation, ) && !isEmpty(comicBookDetailData.sourcedMetadata); // check for the availability of rawFileDetails const areRawFileDetailsAvailable = !isUndefined(comicBookDetailData.rawFileDetails) && !isEmpty(comicBookDetailData.rawFileDetails.cover); // query for airdc++ const airDCPPQuery = {}; if (isComicBookMetadataAvailable) { Object.assign(airDCPPQuery, { issue: { name: comicBookDetailData.sourcedMetadata.comicvine.volumeInformation .name, }, }); } else if (areRawFileDetailsAvailable) { Object.assign(airDCPPQuery, { issue: { name: comicBookDetailData.inferredMetadata.issue.name, number: comicBookDetailData.inferredMetadata.issue.number, }, }); } // 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(comicBookDetailData.sourcedMetadata) && !isNil(comicBookDetailData.sourcedMetadata.comicInfo) && ( )}
), shouldShow: !isUndefined(comicBookDetailData.sourcedMetadata) && !isEmpty(comicBookDetailData.sourcedMetadata.comicInfo), }, { id: 3, icon: , name: "Archive Operations", content: , shouldShow: areRawFileDetailsAvailable, }, { id: 4, icon: , name: "Acquisition", content: ( ), shouldShow: true, }, { id: 5, icon: null, name: !isNil(comicBookDetailData) && !isEmpty(comicBookDetailData) ? ( Downloads ) : ( "Downloads" ), content: !isNil(comicBookDetailData) && !isEmpty(comicBookDetailData) && ( ), shouldShow: true, }, ]; // filtered Tabs const filteredTabs = tabGroup.filter((tab) => tab.shouldShow); const filteredTabIds = tabGroup .map((tab) => { if (tab.shouldShow) { return tab.id; } }) .filter((tab) => !isNil(tab)); // Determine which cover image to use: // 1. from the locally imported or // 2. from the CV-scraped version let imagePath = ""; let comicBookTitle = ""; if (areRawFileDetailsAvailable) { const encodedFilePath = encodeURI( `${LIBRARY_SERVICE_HOST}/${comicBookDetailData.rawFileDetails.cover.filePath}`, ); imagePath = escapePoundSymbol(encodedFilePath); comicBookTitle = comicBookDetailData.rawFileDetails.name; } else if (isComicBookMetadataAvailable) { imagePath = comicBookDetailData.sourcedMetadata.comicvine.image.small_url; comicBookTitle = comicBookDetailData.sourcedMetadata.comicvine.name; } return (
{!isNil(comicBookDetailData) && !isEmpty(comicBookDetailData) && ( <>

{comicBookTitle}

{/* action dropdown */}
{/* raw file details */}
{!isUndefined(comicBookDetailData.rawFileDetails) && !isEmpty(comicBookDetailData.rawFileDetails.cover) && ( <> {/* Read comic button */} {extractedComicBook && ( )} )}
{ } setVisible(false)} title={"Comic Vine Search Matches"} width={"600px"} > {slidingPanelContentId !== "" && contentForSlidingPanel[slidingPanelContentId].content()} )}
); };