import React, { useState, ReactElement, useCallback } from "react"; import { useParams } from "react-router-dom"; import Card from "../shared/Carda"; import { RawFileDetails } from "./RawFileDetails"; import TabControls from "./TabControls"; import { Menu } from "./ActionMenu/Menu"; import { isEmpty, isUndefined, isNil, filter } from "lodash"; import { components } from "react-select"; import "react-sliding-pane/dist/react-sliding-pane.css"; import SlidingPane from "react-sliding-pane"; import { determineCoverFile } from "../../shared/utils/metadata.utils"; import { styled } from "styled-components"; import { RawFileDetails as RawFileDetailsType } from "../../graphql/generated"; // Extracted modules import { useComicVineMatching } from "./useComicVineMatching"; import { createTabConfig } from "./tabConfig"; import { actionOptions, customStyles, ActionOption } from "./actionMenuConfig"; import { CVMatchesPanel, EditMetadataPanelWrapper } from "./SlidingPanelContent"; // Styled component - moved outside to prevent recreation const StyledSlidingPanel = styled(SlidingPane)` background: #ccc; `; type InferredIssue = { name?: string; number?: number; year?: string; subtitle?: string; [key: string]: any; }; type ComicVineMetadata = { name?: string; volumeInformation?: any; [key: string]: any; }; type Acquisition = { directconnect?: { downloads?: any[]; }; torrent?: any[]; [key: string]: any; }; type ComicDetailProps = { data: { _id: string; rawFileDetails?: RawFileDetailsType; inferredMetadata: { issue?: InferredIssue; }; sourcedMetadata: { comicvine?: ComicVineMetadata; locg?: any; comicInfo?: any; }; acquisition?: Acquisition; createdAt: string; updatedAt: string; }; userSettings?: any; queryClient?: any; comicObjectId?: string; }; /** * Displays full comic detail: cover, file info, action menu, and tabbed panels * for metadata, archive operations, and acquisition. */ export const ComicDetail = (data: ComicDetailProps): ReactElement => { const { data: { _id, rawFileDetails, inferredMetadata, sourcedMetadata: { comicvine, locg, comicInfo }, acquisition, createdAt, }, userSettings, queryClient, comicObjectId: comicObjectIdProp, } = data; const [activeTab, setActiveTab] = useState(undefined); const [visible, setVisible] = useState(false); const [slidingPanelContentId, setSlidingPanelContentId] = useState(""); const { comicObjectId } = useParams<{ comicObjectId: string }>(); const { comicVineMatches, prepareAndFetchMatches } = useComicVineMatching(); // Action event handlers const openDrawerWithCVMatches = () => { prepareAndFetchMatches(rawFileDetails, comicvine); setSlidingPanelContentId("CVMatches"); setVisible(true); }; const openEditMetadataPanel = useCallback(() => { setSlidingPanelContentId("editComicBookMetadata"); setVisible(true); }, []); // Action menu handler const Placeholder = components.Placeholder; const filteredActionOptions = filter(actionOptions, (item) => { if (isUndefined(rawFileDetails)) { return item.value !== "match-on-comic-vine"; } return item; }); const handleActionSelection = (action: ActionOption) => { switch (action.value) { case "match-on-comic-vine": openDrawerWithCVMatches(); break; case "edit-metdata": openEditMetadataPanel(); break; default: break; } }; // Check for metadata availability const isComicBookMetadataAvailable = !isUndefined(comicvine) && !isUndefined(comicvine?.volumeInformation); const areRawFileDetailsAvailable = !isUndefined(rawFileDetails) && !isEmpty(rawFileDetails); const { issueName, url } = determineCoverFile({ rawFileDetails, comicvine, locg, }); // Query for airdc++ const airDCPPQuery = { issue: { name: issueName, }, }; // Create tab configuration const tabGroup = createTabConfig({ data: data.data, comicInfo, isComicBookMetadataAvailable, areRawFileDetailsAvailable, airDCPPQuery, comicObjectId: _id, userSettings, issueName, acquisition, }); const filteredTabs = tabGroup.filter((tab) => tab.shouldShow); // Sliding panel content mapping const renderSlidingPanelContent = () => { switch (slidingPanelContentId) { case "CVMatches": return ( { setVisible(false); setActiveTab(1); }} /> ); case "editComicBookMetadata": return ; default: return null; } }; return (
{!isNil(data) && !isEmpty(data) && ( <>
{/* raw file details */} {!isUndefined(rawFileDetails) && !isEmpty(rawFileDetails?.cover) && (
{/* action dropdown */}
)}
setVisible(false)} title={"Comic Vine Search Matches"} width={"600px"} > {renderSlidingPanelContent()} )}
); }; export default ComicDetail;