🏗️ Refactoring ComicDetail page and its children

This commit is contained in:
2023-11-16 09:30:41 -06:00
parent 199ddc2859
commit 4d2d7da5b4
12 changed files with 139 additions and 150 deletions

View File

@@ -10,12 +10,10 @@ import {
downloadAirDCPPItem, downloadAirDCPPItem,
getBundlesForComic, getBundlesForComic,
} from "../../actions/airdcpp.actions"; } from "../../actions/airdcpp.actions";
import { useDispatch, useSelector } from "react-redux";
import { RootState, SearchInstance } from "threetwo-ui-typings"; import { RootState, SearchInstance } from "threetwo-ui-typings";
import ellipsize from "ellipsize"; import ellipsize from "ellipsize";
import { Form, Field } from "react-final-form"; import { Form, Field } from "react-final-form";
import { isEmpty, isNil, map } from "lodash"; import { isEmpty, isNil, map } from "lodash";
import { AirDCPPSocketContext } from "../../context/AirDCPPSocket";
interface IAcquisitionPanelProps { interface IAcquisitionPanelProps {
query: any; query: any;
@@ -48,25 +46,27 @@ export const AcquisitionPanel = (
// const settings = useSelector((state: RootState) => state.settings.data); // const settings = useSelector((state: RootState) => state.settings.data);
const airDCPPConfiguration = useContext(AirDCPPSocketContext); const airDCPPConfiguration = useContext(AirDCPPSocketContext);
const dispatch = useDispatch();
const [dcppQuery, setDcppQuery] = useState({}); const [dcppQuery, setDcppQuery] = useState({});
// Construct a AirDC++ query based on metadata inferred, upon component mount
// Pre-populate the search input with the search string, so that
// All the user has to do is hit "Search AirDC++"
useEffect(() => { useEffect(() => {
if (!isEmpty(airDCPPConfiguration.airDCPPState.settings)) { // if (!isEmpty(airDCPPConfiguration.airDCPPState.settings)) {
// AirDC++ search query // // AirDC++ search query
const dcppSearchQuery = { // const dcppSearchQuery = {
query: { // query: {
pattern: `${sanitizedIssueName.replace(/#/g, "")}`, // pattern: `${sanitizedIssueName.replace(/#/g, "")}`,
extensions: ["cbz", "cbr", "cb7"], // extensions: ["cbz", "cbr", "cb7"],
}, // },
hub_urls: map( // hub_urls: map(
airDCPPConfiguration.airDCPPState.settings.directConnect.client.hubs, // airDCPPConfiguration.airDCPPState.settings.directConnect.client.hubs,
(item) => item.value, // (item) => item.value,
), // ),
priority: 5, // priority: 5,
}; // };
setDcppQuery(dcppSearchQuery); // setDcppQuery(dcppSearchQuery);
} // }
}, [airDCPPConfiguration]); }, [airDCPPConfiguration]);
const getDCPPSearchResults = useCallback( const getDCPPSearchResults = useCallback(
@@ -82,45 +82,45 @@ export const AcquisitionPanel = (
), ),
priority: 5, priority: 5,
}; };
dispatch( // dispatch(
search(manualQuery, airDCPPConfiguration.airDCPPState.socket, { // search(manualQuery, airDCPPConfiguration.airDCPPState.socket, {
username: `${airDCPPConfiguration.airDCPPState.settings.directConnect.client.host.username}`, // username: `${airDCPPConfiguration.airDCPPState.settings.directConnect.client.host.username}`,
password: `${airDCPPConfiguration.airDCPPState.settings.directConnect.client.host.password}`, // password: `${airDCPPConfiguration.airDCPPState.settings.directConnect.client.host.password}`,
}), // }),
); // );
}, },
[dispatch, airDCPPConfiguration], [airDCPPConfiguration],
); );
// download via AirDC++ // download via AirDC++
const downloadDCPPResult = useCallback( const downloadDCPPResult = useCallback(
(searchInstanceId, resultId, name, size, type) => { (searchInstanceId, resultId, name, size, type) => {
dispatch( // dispatch(
downloadAirDCPPItem( // downloadAirDCPPItem(
searchInstanceId, // searchInstanceId,
resultId, // resultId,
props.comicObjectId, // props.comicObjectId,
name, // name,
size, // size,
type, // type,
airDCPPConfiguration.airDCPPState.socket, // airDCPPConfiguration.airDCPPState.socket,
{ // {
username: `${airDCPPConfiguration.airDCPPState.settings.directConnect.client.host.username}`, // username: `${airDCPPConfiguration.airDCPPState.settings.directConnect.client.host.username}`,
password: `${airDCPPConfiguration.airDCPPState.settings.directConnect.client.host.password}`, // password: `${airDCPPConfiguration.airDCPPState.settings.directConnect.client.host.password}`,
}, // },
), // ),
); // );
// this is to update the download count badge on the downloads tab // this is to update the download count badge on the downloads tab
dispatch( // dispatch(
getBundlesForComic( // getBundlesForComic(
props.comicObjectId, // props.comicObjectId,
airDCPPConfiguration.airDCPPState.socket, // airDCPPConfiguration.airDCPPState.socket,
{ // {
username: `${airDCPPConfiguration.airDCPPState.settings.directConnect.client.host.username}`, // username: `${airDCPPConfiguration.airDCPPState.settings.directConnect.client.host.username}`,
password: `${airDCPPConfiguration.airDCPPState.settings.directConnect.client.host.password}`, // password: `${airDCPPConfiguration.airDCPPState.settings.directConnect.client.host.password}`,
}, // },
), // ),
); // );
}, },
[airDCPPConfiguration], [airDCPPConfiguration],
); );

View File

@@ -1,6 +1,5 @@
import { filter, isEmpty, isNil, isUndefined } from "lodash"; import { filter, isEmpty, isNil, isUndefined } from "lodash";
import React, { ReactElement, useCallback } from "react"; import React, { ReactElement, useCallback } from "react";
import { useSelector, useDispatch } from "react-redux";
import Select, { components } from "react-select"; import Select, { components } from "react-select";
import { fetchComicVineMatches } from "../../../actions/fileops.actions"; import { fetchComicVineMatches } from "../../../actions/fileops.actions";
import { refineQuery } from "filename-parser"; import { refineQuery } from "filename-parser";
@@ -8,7 +7,6 @@ import { refineQuery } from "filename-parser";
export const Menu = (props): ReactElement => { export const Menu = (props): ReactElement => {
const { data } = props; const { data } = props;
const { setSlidingPanelContentId, setVisible } = props.handlers; const { setSlidingPanelContentId, setVisible } = props.handlers;
const dispatch = useDispatch();
const openDrawerWithCVMatches = useCallback(() => { const openDrawerWithCVMatches = useCallback(() => {
let seriesSearchQuery: IComicVineSearchQuery = {} as IComicVineSearchQuery; let seriesSearchQuery: IComicVineSearchQuery = {} as IComicVineSearchQuery;
let issueSearchQuery: IComicVineSearchQuery = {} as IComicVineSearchQuery; let issueSearchQuery: IComicVineSearchQuery = {} as IComicVineSearchQuery;
@@ -18,10 +16,10 @@ export const Menu = (props): ReactElement => {
} else if (!isEmpty(data.sourcedMetadata)) { } else if (!isEmpty(data.sourcedMetadata)) {
issueSearchQuery = refineQuery(data.sourcedMetadata.comicvine.name); issueSearchQuery = refineQuery(data.sourcedMetadata.comicvine.name);
} }
dispatch(fetchComicVineMatches(data, issueSearchQuery, seriesSearchQuery)); // dispatch(fetchComicVineMatches(data, issueSearchQuery, seriesSearchQuery));
setSlidingPanelContentId("CVMatches"); setSlidingPanelContentId("CVMatches");
setVisible(true); setVisible(true);
}, [dispatch, data]); }, [data]);
const openEditMetadataPanel = useCallback(() => { const openEditMetadataPanel = useCallback(() => {
setSlidingPanelContentId("editComicBookMetadata"); setSlidingPanelContentId("editComicBookMetadata");

View File

@@ -1,5 +1,4 @@
import React, { useState, ReactElement, useCallback } from "react"; import React, { useState, ReactElement, useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import Card from "../shared/Carda"; import Card from "../shared/Carda";
import { ComicVineMatchPanel } from "./ComicVineMatchPanel"; import { ComicVineMatchPanel } from "./ComicVineMatchPanel";
@@ -55,34 +54,34 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
const [slidingPanelContentId, setSlidingPanelContentId] = useState(""); const [slidingPanelContentId, setSlidingPanelContentId] = useState("");
const [modalIsOpen, setIsOpen] = useState(false); const [modalIsOpen, setIsOpen] = useState(false);
const comicVineSearchResults = useSelector( // const comicVineSearchResults = useSelector(
(state: RootState) => state.comicInfo.searchResults, // (state: RootState) => state.comicInfo.searchResults,
); // );
const comicVineSearchQueryObject = useSelector( // const comicVineSearchQueryObject = useSelector(
(state: RootState) => state.comicInfo.searchQuery, // (state: RootState) => state.comicInfo.searchQuery,
); // );
const comicVineAPICallProgress = useSelector( // const comicVineAPICallProgress = useSelector(
(state: RootState) => state.comicInfo.inProgress, // (state: RootState) => state.comicInfo.inProgress,
); // );
//
const extractedComicBook = useSelector( // const extractedComicBook = useSelector(
(state: RootState) => state.fileOps.extractedComicBookArchive.reading, // (state: RootState) => state.fileOps.extractedComicBookArchive.reading,
); // );
const { comicObjectId } = useParams<{ comicObjectId: string }>(); const { comicObjectId } = useParams<{ comicObjectId: string }>();
const dispatch = useDispatch(); // const dispatch = useDispatch();
const openModal = useCallback((filePath) => { const openModal = useCallback((filePath) => {
setIsOpen(true); setIsOpen(true);
dispatch( // dispatch(
extractComicArchive(filePath, { // extractComicArchive(filePath, {
type: "full", // type: "full",
purpose: "reading", // purpose: "reading",
imageResizeOptions: { // imageResizeOptions: {
baseWidth: 1024, // baseWidth: 1024,
}, // },
}), // }),
); // );
}, []); }, []);
const afterOpenModal = useCallback((things) => { const afterOpenModal = useCallback((things) => {

View File

@@ -1,22 +1,19 @@
import { isEmpty, isNil, isUndefined } from "lodash"; import { isEmpty, isNil, isUndefined } from "lodash";
import React, { ReactElement, useContext, useEffect, useState } from "react"; import React, { ReactElement, useContext, useEffect, useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { getComicBookDetailById } from "../../actions/comicinfo.actions"; import { getComicBookDetailById } from "../../actions/comicinfo.actions";
import { ComicDetail } from "../ComicDetail/ComicDetail"; import { ComicDetail } from "../ComicDetail/ComicDetail";
export const ComicDetailContainer = (): ReactElement | null => { export const ComicDetailContainer = (): ReactElement | null => {
const comicBookDetailData = useSelector( // const comicBookDetailData = useSelector(
(state: RootState) => state.comicInfo.comicBookDetail, // (state: RootState) => state.comicInfo.comicBookDetail,
); // );
const dispatch = useDispatch();
const { comicObjectId } = useParams<{ comicObjectId: string }>(); const { comicObjectId } = useParams<{ comicObjectId: string }>();
console.log(comicObjectId);
useEffect(() => { useEffect(() => {
dispatch(getComicBookDetailById(comicObjectId)); // dispatch(getComicBookDetailById(comicObjectId));
// dispatch(getSettings()); // dispatch(getSettings());
}, [dispatch]); }, []);
return !isEmpty(comicBookDetailData) ? ( return <ComicDetail data={comicBookDetailData} />;
<ComicDetail data={comicBookDetailData} />
) : null;
}; };

View File

@@ -2,7 +2,6 @@ import React, { useCallback } from "react";
import { Form, Field } from "react-final-form"; import { Form, Field } from "react-final-form";
import Collapsible from "react-collapsible"; import Collapsible from "react-collapsible";
import { fetchComicVineMatches } from "../../actions/fileops.actions"; import { fetchComicVineMatches } from "../../actions/fileops.actions";
import { useDispatch } from "react-redux";
/** /**
* Component for performing search against ComicVine * Component for performing search against ComicVine
@@ -14,7 +13,6 @@ import { useDispatch } from "react-redux";
* ) * )
*/ */
export const ComicVineSearchForm = (data) => { export const ComicVineSearchForm = (data) => {
const dispatch = useDispatch();
const onSubmit = useCallback((value) => { const onSubmit = useCallback((value) => {
const userInititatedQuery = { const userInititatedQuery = {
inferredIssueDetails: { inferredIssueDetails: {
@@ -24,7 +22,7 @@ export const ComicVineSearchForm = (data) => {
year: value.issueYear, year: value.issueYear,
}, },
}; };
dispatch(fetchComicVineMatches(data, userInititatedQuery)); // dispatch(fetchComicVineMatches(data, userInititatedQuery));
}, []); }, []);
const validate = () => { const validate = () => {
return true; return true;

View File

@@ -1,12 +1,10 @@
import React, { useEffect, useContext, ReactElement } from "react"; import React, { useEffect, useContext, ReactElement } from "react";
import { getBundlesForComic } from "../../actions/airdcpp.actions"; import { getBundlesForComic } from "../../actions/airdcpp.actions";
import { useDispatch, useSelector } from "react-redux";
import { RootState } from "threetwo-ui-typings"; import { RootState } from "threetwo-ui-typings";
import { isEmpty, isNil, map } from "lodash"; import { isEmpty, isNil, map } from "lodash";
import prettyBytes from "pretty-bytes"; import prettyBytes from "pretty-bytes";
import dayjs from "dayjs"; import dayjs from "dayjs";
import ellipsize from "ellipsize"; import ellipsize from "ellipsize";
import { AirDCPPSocketContext } from "../../context/AirDCPPSocket";
interface IDownloadsPanelProps { interface IDownloadsPanelProps {
data: any; data: any;
@@ -16,34 +14,33 @@ interface IDownloadsPanelProps {
export const DownloadsPanel = ( export const DownloadsPanel = (
props: IDownloadsPanelProps, props: IDownloadsPanelProps,
): ReactElement | null => { ): ReactElement | null => {
const bundles = useSelector((state: RootState) => { // const bundles = useSelector((state: RootState) => {
return state.airdcpp.bundles; // return state.airdcpp.bundles;
}); // });
//
// AirDCPP Socket initialization // // AirDCPP Socket initialization
const userSettings = useSelector((state: RootState) => state.settings.data); // const userSettings = useSelector((state: RootState) => state.settings.data);
const airDCPPConfiguration = useContext(AirDCPPSocketContext); // const airDCPPConfiguration = useContext(AirDCPPSocketContext);
const { const {
airDCPPState: { socket, settings }, airDCPPState: { socket, settings },
} = airDCPPConfiguration; } = airDCPPConfiguration;
const dispatch = useDispatch();
// Fetch the downloaded files and currently-downloading file(s) from AirDC++ // Fetch the downloaded files and currently-downloading file(s) from AirDC++
useEffect(() => { useEffect(() => {
try { try {
if (!isEmpty(userSettings)) { if (!isEmpty(userSettings)) {
dispatch( // dispatch(
getBundlesForComic(props.comicObjectId, socket, { // getBundlesForComic(props.comicObjectId, socket, {
username: `${settings.directConnect.client.host.username}`, // username: `${settings.directConnect.client.host.username}`,
password: `${settings.directConnect.client.host.password}`, // password: `${settings.directConnect.client.host.password}`,
}), // }),
); // );
} }
} catch (error) { } catch (error) {
throw new Error(error); throw new Error(error);
} }
}, [dispatch, airDCPPConfiguration]); }, [airDCPPConfiguration]);
const Bundles = (props) => { const Bundles = (props) => {
return !isEmpty(props.data) ? ( return !isEmpty(props.data) ? (

View File

@@ -1,5 +1,4 @@
import React, { ReactElement, useCallback, useEffect, useState } from "react"; import React, { ReactElement, useCallback, useEffect, useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { Form, Field } from "react-final-form"; import { Form, Field } from "react-final-form";
import arrayMutators from "final-form-arrays"; import arrayMutators from "final-form-arrays";
import { FieldArray } from "react-final-form-arrays"; import { FieldArray } from "react-final-form-arrays";
@@ -28,10 +27,9 @@ export const EditMetadataPanel = (props): ReactElement => {
/> />
); );
}; };
const rawFileDetails = useSelector( // const rawFileDetails = useSelector(
(state: RootState) => state.comicInfo.comicBookDetail.rawFileDetails.name, // (state: RootState) => state.comicInfo.comicBookDetail.rawFileDetails.name,
); // );
const dispatch = useDispatch();
return ( return (
<> <>

View File

@@ -1,5 +1,4 @@
import React, { useCallback } from "react"; import React, { useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
import { isNil, map } from "lodash"; import { isNil, map } from "lodash";
import { applyComicVineMatch } from "../../actions/comicinfo.actions"; import { applyComicVineMatch } from "../../actions/comicinfo.actions";
import { convert } from "html-to-text"; import { convert } from "html-to-text";
@@ -15,12 +14,11 @@ const handleBrokenImage = (e) => {
}; };
export const MatchResult = (props: MatchResultProps) => { export const MatchResult = (props: MatchResultProps) => {
const dispatch = useDispatch();
const applyCVMatch = useCallback( const applyCVMatch = useCallback(
(match, comicObjectId) => { // (match, comicObjectId) => {
dispatch(applyComicVineMatch(match, comicObjectId)); // dispatch(applyComicVineMatch(match, comicObjectId));
}, // },
[dispatch], [],
); );
return ( return (
<> <>

View File

@@ -1,11 +1,10 @@
import React, { ReactElement, useEffect, useState } from "react"; import React, { ReactElement, useEffect, useState } from "react";
import { isEmpty, isNil } from "lodash"; import { isEmpty, isNil } from "lodash";
import { useSelector } from "react-redux";
export const TabControls = (props): ReactElement => { export const TabControls = (props): ReactElement => {
const comicBookDetailData = useSelector( // const comicBookDetailData = useSelector(
(state: RootState) => state.comicInfo.comicBookDetail, // (state: RootState) => state.comicInfo.comicBookDetail,
); // );
const { filteredTabs } = props; const { filteredTabs } = props;
const [active, setActive] = useState(filteredTabs[0].id); const [active, setActive] = useState(filteredTabs[0].id);

View File

@@ -1,5 +1,4 @@
import React, { ReactElement, useCallback, useState } from "react"; import React, { ReactElement, useCallback, useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { DnD } from "../../shared/Draggable/DnD"; import { DnD } from "../../shared/Draggable/DnD";
import { isEmpty } from "lodash"; import { isEmpty } from "lodash";
import Sticky from "react-stickynode"; import Sticky from "react-stickynode";
@@ -10,28 +9,27 @@ import { Canvas } from "../../shared/Canvas";
export const ArchiveOperations = (props): ReactElement => { export const ArchiveOperations = (props): ReactElement => {
const { data } = props; const { data } = props;
const isComicBookExtractionInProgress = useSelector( // const isComicBookExtractionInProgress = useSelector(
(state: RootState) => state.fileOps.comicBookExtractionInProgress, // (state: RootState) => state.fileOps.comicBookExtractionInProgress,
); // );
const extractedComicBookArchive = useSelector( // const extractedComicBookArchive = useSelector(
(state: RootState) => state.fileOps.extractedComicBookArchive.analysis, // (state: RootState) => state.fileOps.extractedComicBookArchive.analysis,
); // );
//
// const imageAnalysisResult = useSelector((state: RootState) => {
// return state.fileOps.imageAnalysisResults;
// });
const imageAnalysisResult = useSelector((state: RootState) => {
return state.fileOps.imageAnalysisResults;
});
const dispatch = useDispatch();
const unpackComicArchive = useCallback(() => { const unpackComicArchive = useCallback(() => {
dispatch( // dispatch(
extractComicArchive(data.rawFileDetails.filePath, { // extractComicArchive(data.rawFileDetails.filePath, {
type: "full", // type: "full",
purpose: "analysis", // purpose: "analysis",
imageResizeOptions: { // imageResizeOptions: {
baseWidth: 275, // baseWidth: 275,
}, // },
}), // }),
); // );
}, []); }, []);
// sliding panel config // sliding panel config
@@ -64,7 +62,7 @@ export const ArchiveOperations = (props): ReactElement => {
// sliding panel handlers // sliding panel handlers
const openImageAnalysisPanel = useCallback((imageFilePath) => { const openImageAnalysisPanel = useCallback((imageFilePath) => {
setSlidingPanelContentId("imageAnalysis"); setSlidingPanelContentId("imageAnalysis");
dispatch(analyzeImage(imageFilePath)); // dispatch(analyzeImage(imageFilePath));
setCurrentImage(imageFilePath); setCurrentImage(imageFilePath);
setVisible(true); setVisible(true);
}, []); }, []);

View File

@@ -19,6 +19,7 @@ export const Library = (): ReactElement => {
// Default page state // Default page state
// offset: 0 // offset: 0
const [offset, setOffset] = useState(0); const [offset, setOffset] = useState(0);
// Method to fetch paginated issues // Method to fetch paginated issues
const fetchIssues = async (searchQuery, offset, type) => { const fetchIssues = async (searchQuery, offset, type) => {
let pagination = { let pagination = {
@@ -35,6 +36,7 @@ export const Library = (): ReactElement => {
}, },
}); });
}; };
const { data, isLoading, isError, isPlaceholderData } = useQuery({ const { data, isLoading, isError, isPlaceholderData } = useQuery({
queryKey: ["comics", offset], queryKey: ["comics", offset],
queryFn: () => fetchIssues({}, offset, "all"), queryFn: () => fetchIssues({}, offset, "all"),
@@ -42,8 +44,8 @@ export const Library = (): ReactElement => {
}); });
const searchResults = data?.data; const searchResults = data?.data;
console.log(searchResults);
// programatically navigate to comic detail // Programmatically navigate to comic detail
const navigate = useNavigate(); const navigate = useNavigate();
const navigateToComicDetail = (row) => { const navigateToComicDetail = (row) => {
navigate(`/comic/details/${row.original._id}`); navigate(`/comic/details/${row.original._id}`);

View File

@@ -12,6 +12,7 @@ import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import Import from "./components/Import/Import"; import Import from "./components/Import/Import";
import Dashboard from "./components/Dashboard/Dashboard"; import Dashboard from "./components/Dashboard/Dashboard";
import TabulatedContentContainer from "./components/Library/TabulatedContentContainer"; import TabulatedContentContainer from "./components/Library/TabulatedContentContainer";
import { ComicDetailContainer } from "./components/ComicDetail/ComicDetailContainer";
const queryClient = new QueryClient(); const queryClient = new QueryClient();
@@ -27,6 +28,10 @@ const router = createBrowserRouter([
path: "library", path: "library",
element: <TabulatedContentContainer category="library" />, element: <TabulatedContentContainer category="library" />,
}, },
{
path: "comic/details/:comicObjectId",
element: <ComicDetailContainer />,
},
{ path: "import", element: <Import path={"./comics"} /> }, { path: "import", element: <Import path={"./comics"} /> },
], ],
}, },