🔨 Added JsDoc, WIP metadata reconciliation
This commit is contained in:
@@ -10,7 +10,7 @@ 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";
|
||||
import type { RawFileDetails as RawFileDetailsType, InferredMetadata } from "../../graphql/generated";
|
||||
|
||||
// Extracted modules
|
||||
import { useComicVineMatching } from "./useComicVineMatching";
|
||||
@@ -23,52 +23,47 @@ const StyledSlidingPanel = styled(SlidingPane)`
|
||||
background: #ccc;
|
||||
`;
|
||||
|
||||
type InferredIssue = {
|
||||
interface ComicVineMetadata {
|
||||
name?: string;
|
||||
number?: number;
|
||||
year?: string;
|
||||
subtitle?: string;
|
||||
[key: string]: any;
|
||||
};
|
||||
volumeInformation?: Record<string, unknown>;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
type ComicVineMetadata = {
|
||||
name?: string;
|
||||
volumeInformation?: any;
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
type Acquisition = {
|
||||
interface Acquisition {
|
||||
directconnect?: {
|
||||
downloads?: any[];
|
||||
downloads?: unknown[];
|
||||
};
|
||||
torrent?: any[];
|
||||
[key: string]: any;
|
||||
};
|
||||
torrent?: unknown[];
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
type ComicDetailProps = {
|
||||
interface ComicDetailProps {
|
||||
data: {
|
||||
_id: string;
|
||||
rawFileDetails?: RawFileDetailsType;
|
||||
inferredMetadata: {
|
||||
issue?: InferredIssue;
|
||||
};
|
||||
inferredMetadata: InferredMetadata;
|
||||
sourcedMetadata: {
|
||||
comicvine?: ComicVineMetadata;
|
||||
locg?: any;
|
||||
comicInfo?: any;
|
||||
locg?: Record<string, unknown>;
|
||||
comicInfo?: Record<string, unknown>;
|
||||
};
|
||||
acquisition?: Acquisition;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
userSettings?: any;
|
||||
queryClient?: any;
|
||||
userSettings?: Record<string, unknown>;
|
||||
queryClient?: unknown;
|
||||
comicObjectId?: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays full comic detail: cover, file info, action menu, and tabbed panels
|
||||
* for metadata, archive operations, and acquisition.
|
||||
*
|
||||
* @param data.queryClient - react-query client passed through to the CV match
|
||||
* panel so it can invalidate queries after a match is applied.
|
||||
* @param data.comicObjectId - optional override for the comic ID; used when the
|
||||
* component is rendered outside a route that provides the ID via `useParams`.
|
||||
*/
|
||||
export const ComicDetail = (data: ComicDetailProps): ReactElement => {
|
||||
const {
|
||||
@@ -104,7 +99,8 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
|
||||
setVisible(true);
|
||||
}, []);
|
||||
|
||||
// Action menu handler
|
||||
// Hide "match on Comic Vine" when there are no raw file details — matching
|
||||
// requires file metadata to seed the search query.
|
||||
const Placeholder = components.Placeholder;
|
||||
const filteredActionOptions = filter(actionOptions, (item) => {
|
||||
if (isUndefined(rawFileDetails)) {
|
||||
@@ -180,6 +176,7 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
|
||||
rawFileDetails={rawFileDetails}
|
||||
inferredMetadata={inferredMetadata}
|
||||
comicVineMatches={comicVineMatches}
|
||||
// Prefer the route param; fall back to the data ID when rendered outside a route.
|
||||
comicObjectId={comicObjectId || _id}
|
||||
queryClient={queryClient}
|
||||
onMatchApplied={() => {
|
||||
|
||||
@@ -7,7 +7,7 @@ import TextareaAutosize from "react-textarea-autosize";
|
||||
|
||||
interface EditMetadataPanelProps {
|
||||
data: {
|
||||
name?: string;
|
||||
name?: string | null;
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,27 +2,27 @@ import React from "react";
|
||||
import { ComicVineSearchForm } from "./ComicVineSearchForm";
|
||||
import { ComicVineMatchPanel } from "./ComicVineMatchPanel";
|
||||
import { EditMetadataPanel } from "./EditMetadataPanel";
|
||||
import { RawFileDetails } from "../../graphql/generated";
|
||||
import type { RawFileDetails, InferredMetadata } from "../../graphql/generated";
|
||||
|
||||
type InferredIssue = {
|
||||
name?: string;
|
||||
number?: number;
|
||||
year?: string;
|
||||
subtitle?: string;
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
type CVMatchesPanelProps = {
|
||||
interface CVMatchesPanelProps {
|
||||
rawFileDetails?: RawFileDetails;
|
||||
inferredMetadata: {
|
||||
issue?: InferredIssue;
|
||||
};
|
||||
inferredMetadata: InferredMetadata;
|
||||
comicVineMatches: any[];
|
||||
comicObjectId: string;
|
||||
queryClient: any;
|
||||
onMatchApplied: () => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sliding panel content for ComicVine match search.
|
||||
*
|
||||
* Renders a search form pre-populated from `rawFileDetails`, a preview of the
|
||||
* inferred issue being searched for, and a list of ComicVine match candidates
|
||||
* the user can apply to the comic.
|
||||
*
|
||||
* @param props.onMatchApplied - Called after the user selects and applies a match,
|
||||
* allowing the parent to close the panel and refresh state.
|
||||
*/
|
||||
export const CVMatchesPanel: React.FC<CVMatchesPanelProps> = ({
|
||||
rawFileDetails,
|
||||
inferredMetadata,
|
||||
@@ -62,4 +62,4 @@ type EditMetadataPanelWrapperProps = {
|
||||
|
||||
export const EditMetadataPanelWrapper: React.FC<EditMetadataPanelWrapperProps> = ({
|
||||
rawFileDetails,
|
||||
}) => <EditMetadataPanel data={rawFileDetails} />;
|
||||
}) => <EditMetadataPanel data={rawFileDetails ?? {}} />;
|
||||
|
||||
@@ -30,6 +30,7 @@ interface VolumeInformationProps {
|
||||
onReconcile?: () => void;
|
||||
}
|
||||
|
||||
/** Sources stored under `sourcedMetadata` — excludes `inferredMetadata`, which is checked separately. */
|
||||
const SOURCED_METADATA_KEYS = ["comicvine", "locg", "comicInfo", "metron", "gcd"];
|
||||
|
||||
const SOURCE_LABELS: Record<string, string> = {
|
||||
@@ -72,6 +73,17 @@ const MetadataSourceChips = ({
|
||||
</div>
|
||||
);
|
||||
|
||||
/**
|
||||
* Displays volume metadata for a comic.
|
||||
*
|
||||
* - When multiple sources are present, renders a chip bar listing each source
|
||||
* with a "Reconcile sources" action to merge them.
|
||||
* - When exactly one source is present and it is ComicVine, renders the full
|
||||
* ComicVine detail panel directly.
|
||||
*
|
||||
* @param props.data - Comic data containing sourced and inferred metadata.
|
||||
* @param props.onReconcile - Called when the user triggers source reconciliation.
|
||||
*/
|
||||
export const VolumeInformation = (props: VolumeInformationProps): ReactElement => {
|
||||
const { data, onReconcile } = props;
|
||||
|
||||
@@ -83,7 +95,7 @@ export const VolumeInformation = (props: VolumeInformationProps): ReactElement =
|
||||
if (key === "locg") return Object.values(val as Record<string, unknown>).some((v) => !isNil(v) && v !== "");
|
||||
return true;
|
||||
});
|
||||
if (!isNil(data?.inferredMetadata?.issue) && !isEmpty(data.inferredMetadata.issue)) {
|
||||
if (!isNil(data?.inferredMetadata?.issue) && !isEmpty(data?.inferredMetadata?.issue)) {
|
||||
sources.push("inferredMetadata");
|
||||
}
|
||||
return sources;
|
||||
|
||||
Reference in New Issue
Block a user