🏗️ Refactoring the Library page

This commit is contained in:
2023-11-15 21:03:49 -06:00
parent 15cba6d56a
commit b1d63b02c4
7 changed files with 173 additions and 153 deletions

View File

@@ -4,9 +4,10 @@ import { useNavigate } from "react-router-dom";
import { isEmpty, isNil, isUndefined } from "lodash"; import { isEmpty, isNil, isUndefined } from "lodash";
import MetadataPanel from "../shared/MetadataPanel"; import MetadataPanel from "../shared/MetadataPanel";
import T2Table from "../shared/T2Table"; import T2Table from "../shared/T2Table";
import { useDispatch, useSelector } from "react-redux";
import { searchIssue } from "../../actions/fileops.actions"; import { searchIssue } from "../../actions/fileops.actions";
import ellipsize from "ellipsize"; import ellipsize from "ellipsize";
import { useQuery } from "@tanstack/react-query";
import axios from "axios";
/** /**
* Component that tabulates the contents of the user's ThreeTwo Library. * Component that tabulates the contents of the user's ThreeTwo Library.
@@ -16,29 +17,31 @@ import ellipsize from "ellipsize";
* <Library /> * <Library />
*/ */
export const Library = (): ReactElement => { export const Library = (): ReactElement => {
const searchResults = useSelector( // const searchResults = useSelector(
(state: RootState) => state.fileOps.libraryComics, // (state: RootState) => state.fileOps.libraryComics,
); // );
const searchError = useSelector((state: RootState) => state.fileOps.librarySearchError); // const searchError = useSelector(
const dispatch = useDispatch(); // (state: RootState) => state.fileOps.librarySearchError,
useEffect(() => { // );
dispatch( // const dispatch = useDispatch();
searchIssue( const { data, isLoading, isError } = useQuery({
{ queryKey: ["comics"],
queryFn: async () =>
axios({
method: "POST",
url: "http://localhost:3000/api/search/searchIssue",
data: {
query: {}, query: {},
},
{
pagination: { pagination: {
size: 15, size: 15,
from: 0, from: 0,
}, },
type: "all", type: "all",
trigger: "libraryPage",
}, },
), }),
); });
}, []); const searchResults = data?.data;
console.log(searchResults);
// programatically navigate to comic detail // programatically navigate to comic detail
const navigate = useNavigate(); const navigate = useNavigate();
const navigateToComicDetail = (row) => { const navigateToComicDetail = (row) => {
@@ -157,21 +160,21 @@ export const Library = (): ReactElement => {
* *
**/ **/
const nextPage = useCallback((pageIndex: number, pageSize: number) => { const nextPage = useCallback((pageIndex: number, pageSize: number) => {
dispatch( // dispatch(
searchIssue( // searchIssue(
{ // {
query: {}, // query: {},
}, // },
{ // {
pagination: { // pagination: {
size: pageSize, // size: pageSize,
from: pageSize * pageIndex + 1, // from: pageSize * pageIndex + 1,
}, // },
type: "all", // type: "all",
trigger: "libraryPage", // trigger: "libraryPage",
}, // },
), // ),
); // );
}, []); }, []);
/** /**
@@ -188,21 +191,21 @@ export const Library = (): ReactElement => {
} else { } else {
from = (pageIndex - 1) * pageSize + 2 - 16; from = (pageIndex - 1) * pageSize + 2 - 16;
} }
dispatch( // dispatch(
searchIssue( // searchIssue(
{ // {
query: {}, // query: {},
}, // },
{ // {
pagination: { // pagination: {
size: pageSize, // size: pageSize,
from, // from,
}, // },
type: "all", // type: "all",
trigger: "libraryPage", // trigger: "libraryPage",
}, // },
), // ),
); // );
}, []); }, []);
// ImportStatus.propTypes = { // ImportStatus.propTypes = {
@@ -214,7 +217,7 @@ export const Library = (): ReactElement => {
<div className="header-area"> <div className="header-area">
<h1 className="title">Library</h1> <h1 className="title">Library</h1>
</div> </div>
{!isEmpty(searchResults) ? ( {!isUndefined(searchResults?.data?.data) ? (
<div> <div>
<div className="library"> <div className="library">
<T2Table <T2Table
@@ -240,9 +243,9 @@ export const Library = (): ReactElement => {
</div> </div>
</article> </article>
<pre> <pre>
{!isUndefined(searchError.data) && {!isUndefined(searchResults?.code === 404 && !isLoading) &&
JSON.stringify( JSON.stringify(
searchError.data.meta.body.error.root_cause, searchResults.data.meta.body.error.root_cause,
null, null,
4, 4,
)} )}

View File

@@ -2,29 +2,27 @@ import React, { ReactElement, useCallback } from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Form, Field } from "react-final-form"; import { Form, Field } from "react-final-form";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { useDispatch } from "react-redux";
import { searchIssue } from "../../actions/fileops.actions"; import { searchIssue } from "../../actions/fileops.actions";
export const SearchBar = (): ReactElement => { export const SearchBar = (): ReactElement => {
const dispatch = useDispatch();
const handleSubmit = useCallback((e) => { const handleSubmit = useCallback((e) => {
dispatch( // dispatch(
searchIssue( // searchIssue(
{ // {
query: { // query: {
volumeName: e.search, // volumeName: e.search,
}, // },
}, // },
{ // {
pagination: { // pagination: {
size: 25, // size: 25,
from: 0, // from: 0,
}, // },
type: "volumeName", // type: "volumeName",
trigger: "libraryPage", // trigger: "libraryPage",
}, // },
), // ),
); // );
}, []); }, []);
return ( return (
<div className="box"> <div className="box">
@@ -56,7 +54,6 @@ export const SearchBar = (): ReactElement => {
</form> </form>
)} )}
/> />
</div> </div>
); );
}; };

View File

@@ -1,25 +1,23 @@
import React, { ReactElement, useEffect, useMemo } from "react"; import React, { ReactElement, useEffect, useMemo } from "react";
import T2Table from "../shared/T2Table"; import T2Table from "../shared/T2Table";
import { getWeeklyPullList } from "../../actions/comicinfo.actions"; import { getWeeklyPullList } from "../../actions/comicinfo.actions";
import { useDispatch, useSelector } from "react-redux";
import Card from "../shared/Carda"; import Card from "../shared/Carda";
import ellipsize from "ellipsize"; import ellipsize from "ellipsize";
import { isNil } from "lodash"; import { isNil } from "lodash";
export const PullList = (): ReactElement => { export const PullList = (): ReactElement => {
const pullListComics = useSelector( // const pullListComics = useSelector(
(state: RootState) => state.comicInfo.pullList, // (state: RootState) => state.comicInfo.pullList,
); // );
const dispatch = useDispatch();
useEffect(() => { useEffect(() => {
dispatch( // dispatch(
getWeeklyPullList({ // getWeeklyPullList({
startDate: "2023-7-28", // startDate: "2023-7-28",
pageSize: "15", // pageSize: "15",
currentPage: "1", // currentPage: "1",
}), // }),
); // );
}, []); }, []);
const nextPageHandler = () => {}; const nextPageHandler = () => {};
const previousPageHandler = () => {}; const previousPageHandler = () => {};

View File

@@ -1,5 +1,4 @@
import React, { ReactElement, useEffect, useMemo } from "react"; import React, { ReactElement, useEffect, useMemo } from "react";
import { useDispatch, useSelector } from "react-redux";
import { searchIssue } from "../../actions/fileops.actions"; import { searchIssue } from "../../actions/fileops.actions";
import Card from "../shared/Carda"; import Card from "../shared/Carda";
import T2Table from "../shared/T2Table"; import T2Table from "../shared/T2Table";
@@ -8,24 +7,23 @@ import { convert } from "html-to-text";
import { isUndefined } from "lodash"; import { isUndefined } from "lodash";
export const Volumes = (props): ReactElement => { export const Volumes = (props): ReactElement => {
const volumes = useSelector((state: RootState) => state.fileOps.volumes); // const volumes = useSelector((state: RootState) => state.fileOps.volumes);
const dispatch = useDispatch();
useEffect(() => { useEffect(() => {
dispatch( // dispatch(
searchIssue( // searchIssue(
{ // {
query: {}, // query: {},
}, // },
{ // {
pagination: { // pagination: {
size: 25, // size: 25,
from: 0, // from: 0,
}, // },
type: "volumes", // type: "volumes",
trigger: "volumesPage", // trigger: "volumesPage",
}, // },
), // ),
); // );
}, []); }, []);
const columnData = useMemo( const columnData = useMemo(
() => [ () => [

View File

@@ -1,5 +1,4 @@
import React, { ReactElement, useCallback, useEffect, useMemo } from "react"; import React, { ReactElement, useCallback, useEffect, useMemo } from "react";
import { useDispatch, useSelector } from "react-redux";
import { searchIssue } from "../../actions/fileops.actions"; import { searchIssue } from "../../actions/fileops.actions";
import SearchBar from "../Library/SearchBar"; import SearchBar from "../Library/SearchBar";
import T2Table from "../shared/T2Table"; import T2Table from "../shared/T2Table";
@@ -7,26 +6,25 @@ import { isEmpty, isUndefined } from "lodash";
import MetadataPanel from "../shared/MetadataPanel"; import MetadataPanel from "../shared/MetadataPanel";
export const WantedComics = (props): ReactElement => { export const WantedComics = (props): ReactElement => {
const wantedComics = useSelector( // const wantedComics = useSelector(
(state: RootState) => state.fileOps.wantedComics, // (state: RootState) => state.fileOps.wantedComics,
); // );
const dispatch = useDispatch();
useEffect(() => { useEffect(() => {
dispatch( // dispatch(
searchIssue( // searchIssue(
{ // {
query: {}, // query: {},
}, // },
{ // {
pagination: { // pagination: {
size: 25, // size: 25,
from: 0, // from: 0,
}, // },
type: "wanted", // type: "wanted",
trigger: "wantedComicsPage" // trigger: "wantedComicsPage"
}, // },
), // ),
); // );
}, []); }, []);
const columnData = [ const columnData = [
@@ -37,7 +35,7 @@ export const WantedComics = (props): ReactElement => {
header: "Details", header: "Details",
id: "comicDetails", id: "comicDetails",
minWidth: 350, minWidth: 350,
accessorFn: data => data, accessorFn: (data) => data,
cell: (value) => <MetadataPanel data={value.getValue()} />, cell: (value) => <MetadataPanel data={value.getValue()} />,
}, },
], ],
@@ -49,8 +47,10 @@ export const WantedComics = (props): ReactElement => {
header: "Files", header: "Files",
accessorKey: "acquisition", accessorKey: "acquisition",
align: "right", align: "right",
cell: props => { cell: (props) => {
const { directconnect: { downloads } } = props.getValue(); const {
directconnect: { downloads },
} = props.getValue();
return ( return (
<div <div
style={{ style={{
@@ -60,9 +60,7 @@ export const WantedComics = (props): ReactElement => {
}} }}
> >
{downloads.length > 0 ? ( {downloads.length > 0 ? (
<span className="tag is-warning"> <span className="tag is-warning">{downloads.length}</span>
{downloads.length}
</span>
) : null} ) : null}
</div> </div>
); );
@@ -72,11 +70,17 @@ export const WantedComics = (props): ReactElement => {
header: "Download Details", header: "Download Details",
id: "downloadDetails", id: "downloadDetails",
accessorKey: "acquisition", accessorKey: "acquisition",
cell: data => <ol> cell: (data) => (
{data.getValue().directconnect.downloads.map((download, idx) => { <ol>
return <li className="is-size-7" key={idx}>{download.name}</li>; {data.getValue().directconnect.downloads.map((download, idx) => {
})} return (
</ol> <li className="is-size-7" key={idx}>
{download.name}
</li>
);
})}
</ol>
),
}, },
{ {
header: "Type", header: "Type",
@@ -92,7 +96,7 @@ export const WantedComics = (props): ReactElement => {
* @param {number} pageIndex * @param {number} pageIndex
* @param {number} pageSize * @param {number} pageSize
* @returns void * @returns void
* *
**/ **/
const nextPage = useCallback((pageIndex: number, pageSize: number) => { const nextPage = useCallback((pageIndex: number, pageSize: number) => {
dispatch( dispatch(
@@ -112,7 +116,6 @@ export const WantedComics = (props): ReactElement => {
); );
}, []); }, []);
/** /**
* Pagination control that fetches the previous x (pageSize) items * Pagination control that fetches the previous x (pageSize) items
* based on the y (pageIndex) offset from the Elasticsearch index * based on the y (pageIndex) offset from the Elasticsearch index
@@ -138,7 +141,7 @@ export const WantedComics = (props): ReactElement => {
from, from,
}, },
type: "wanted", type: "wanted",
trigger: "wantedComicsPage" trigger: "wantedComicsPage",
}, },
), ),
); );
@@ -161,7 +164,7 @@ export const WantedComics = (props): ReactElement => {
nextPage: nextPage, nextPage: nextPage,
previousPage: previousPage, previousPage: previousPage,
}} }}
// rowClickHandler={navigateToComicDetail} // rowClickHandler={navigateToComicDetail}
/> />
{/* pagination controls */} {/* pagination controls */}
</div> </div>

View File

@@ -13,12 +13,14 @@ const Navbar: React.FunctionComponent = (props) => {
airDCPPDisconnectionInfo, airDCPPDisconnectionInfo,
airDCPPSessionInformation, airDCPPSessionInformation,
airDCPPDownloadTick, airDCPPDownloadTick,
importJobQueue,
} = useStore( } = useStore(
useShallow((state) => ({ useShallow((state) => ({
airDCPPSocketConnected: state.airDCPPSocketConnected, airDCPPSocketConnected: state.airDCPPSocketConnected,
airDCPPDisconnectionInfo: state.airDCPPDisconnectionInfo, airDCPPDisconnectionInfo: state.airDCPPDisconnectionInfo,
airDCPPSessionInformation: state.airDCPPSessionInformation, airDCPPSessionInformation: state.airDCPPSessionInformation,
airDCPPDownloadTick: state.airDCPPDownloadTick, airDCPPDownloadTick: state.airDCPPDownloadTick,
importJobQueue: state.importJobQueue,
})), })),
); );
// const downloadProgressTick = useSelector( // const downloadProgressTick = useSelector(
@@ -35,24 +37,6 @@ const Navbar: React.FunctionComponent = (props) => {
// (state: RootState) => state.airdcpp.socketDisconnectionReason, // (state: RootState) => state.airdcpp.socketDisconnectionReason,
// ); // );
// Import-related selector hooks
// const successfulImportJobCount = useSelector(
// (state: RootState) => state.fileOps.successfulJobCount,
// );
// const failedImportJobCount = useSelector(
// (state: RootState) => state.fileOps.failedJobCount,
// );
//
// const lastQueueJob = useSelector(
// (state: RootState) => state.fileOps.lastQueueJob,
// );
// const libraryQueueImportStatus = useSelector(
// (state: RootState) => state.fileOps.LSQueueImportStatus,
// );
//
// const allImportJobResults = useSelector(
// (state: RootState) => state.fileOps.importJobStatistics,
// );
return ( return (
<nav className="navbar is-fixed-top"> <nav className="navbar is-fixed-top">
<div className="navbar-brand"> <div className="navbar-brand">
@@ -128,6 +112,38 @@ const Navbar: React.FunctionComponent = (props) => {
) : null} ) : null}
</div> </div>
{!isUndefined(importJobQueue.status) &&
location.hash !== "#/import" ? (
<div className="navbar-item has-dropdown is-hoverable">
<a className="navbar-link is-arrowless">
<i className="fa-solid fa-file-import has-text-warning-dark"></i>
</a>
<div className="navbar-dropdown is-right is-boxed">
<a className="navbar-item">
<ul>
{importJobQueue.successfulJobCount > 0 ? (
<li className="mb-2">
<span className="tag is-success mr-2">
{importJobQueue.successfulJobCount}
</span>
imported.
</li>
) : null}
{importJobQueue.failedJobCount > 0 ? (
<li>
<span className="tag is-danger mr-2">
{importJobQueue.failedJobCount}
</span>
failed to import.
</li>
) : null}
</ul>
</a>
</div>
</div>
) : null}
{/* AirDC++ socket connection status */} {/* AirDC++ socket connection status */}
<div className="navbar-item has-dropdown is-hoverable"> <div className="navbar-item has-dropdown is-hoverable">
{airDCPPSocketConnected ? ( {airDCPPSocketConnected ? (

View File

@@ -11,6 +11,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; 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";
const queryClient = new QueryClient(); const queryClient = new QueryClient();
@@ -22,6 +23,10 @@ const router = createBrowserRouter([
children: [ children: [
{ path: "dashboard", element: <Dashboard /> }, { path: "dashboard", element: <Dashboard /> },
{ path: "settings", element: <Settings /> }, { path: "settings", element: <Settings /> },
{
path: "library",
element: <TabulatedContentContainer category="library" />,
},
{ path: "import", element: <Import path={"./comics"} /> }, { path: "import", element: <Import path={"./comics"} /> },
], ],
}, },