🦟 Fixed 404s upon page refresh

This commit is contained in:
2022-03-01 23:01:57 -08:00
parent 769e2e3edc
commit 9ec5040bd7
10 changed files with 238 additions and 189 deletions

View File

@@ -5,6 +5,7 @@ import {
IMAGETRANSFORMATION_SERVICE_BASE_URI, IMAGETRANSFORMATION_SERVICE_BASE_URI,
LIBRARY_SERVICE_BASE_URI, LIBRARY_SERVICE_BASE_URI,
LIBRARY_SERVICE_HOST, LIBRARY_SERVICE_HOST,
SEARCH_SERVICE_BASE_URI,
} from "../constants/endpoints"; } from "../constants/endpoints";
import { import {
IMS_COMIC_BOOK_GROUPS_FETCHED, IMS_COMIC_BOOK_GROUPS_FETCHED,
@@ -257,7 +258,14 @@ export const extractComicArchive =
}); });
}; };
export const searchIssue = (options) => async (dispatch) => {}; export const searchIssue = (query) => async (dispatch) => {
const foo = await axios({
url: `${SEARCH_SERVICE_BASE_URI}/searchIssue`,
method: "POST",
data: query,
});
console.log(foo);
};
export const analyzeImage = export const analyzeImage =
(imageFilePath: string | Buffer) => async (dispatch) => { (imageFilePath: string | Buffer) => async (dispatch) => {
dispatch({ dispatch({

View File

@@ -12,7 +12,7 @@ import DownloadsPanel from "./ComicDetail/DownloadsPanel";
import { EditMetadataPanel } from "./ComicDetail/EditMetadataPanel"; import { EditMetadataPanel } from "./ComicDetail/EditMetadataPanel";
import { Menu } from "./ComicDetail/ActionMenu/Menu"; import { Menu } from "./ComicDetail/ActionMenu/Menu";
import { isEmpty, isUndefined, isNil, findIndex } from "lodash"; import { isEmpty, isUndefined, isNil } from "lodash";
import { RootState } from "threetwo-ui-typings"; import { RootState } from "threetwo-ui-typings";
import { getComicBookDetailById } from "../actions/comicinfo.actions"; import { getComicBookDetailById } from "../actions/comicinfo.actions";
@@ -117,6 +117,7 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
}, },
}; };
// check for the availability of CV metadata
const isComicBookMetadataAvailable = const isComicBookMetadataAvailable =
comicBookDetailData.sourcedMetadata && comicBookDetailData.sourcedMetadata &&
!isUndefined(comicBookDetailData.sourcedMetadata.comicvine) && !isUndefined(comicBookDetailData.sourcedMetadata.comicvine) &&
@@ -125,6 +126,29 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
) && ) &&
!isEmpty(comicBookDetailData.sourcedMetadata); !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 // Tab content and header details
const tabGroup = [ const tabGroup = [
{ {
@@ -134,7 +158,7 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
content: isComicBookMetadataAvailable ? ( content: isComicBookMetadataAvailable ? (
<VolumeInformation data={comicBookDetailData} key={1} /> <VolumeInformation data={comicBookDetailData} key={1} />
) : null, ) : null,
include: isComicBookMetadataAvailable, shouldShow: isComicBookMetadataAvailable,
}, },
{ {
id: 2, id: 2,
@@ -156,8 +180,8 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
</div> </div>
</div> </div>
), ),
include: shouldShow:
!isNil(comicBookDetailData.sourcedMetadata) && !isUndefined(comicBookDetailData.sourcedMetadata) &&
!isEmpty(comicBookDetailData.sourcedMetadata.comicInfo), !isEmpty(comicBookDetailData.sourcedMetadata.comicInfo),
}, },
{ {
@@ -165,18 +189,14 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
icon: <i className="fa-regular fa-file-archive"></i>, icon: <i className="fa-regular fa-file-archive"></i>,
name: "Archive Operations", name: "Archive Operations",
content: <ArchiveOperations data={comicBookDetailData} key={3} />, content: <ArchiveOperations data={comicBookDetailData} key={3} />,
include: shouldShow: areRawFileDetailsAvailable,
!isUndefined(comicBookDetailData.rawFileDetails) &&
!isEmpty(comicBookDetailData.rawFileDetails.cover),
}, },
{ {
id: 4, id: 4,
icon: <i className="fa-solid fa-floppy-disk"></i>, icon: <i className="fa-solid fa-floppy-disk"></i>,
name: "Acquisition", name: "Acquisition",
content: ( content: <AcquisitionPanel query={airDCPPQuery} key={4} />,
<AcquisitionPanel comicBookMetadata={comicBookDetailData} key={4} /> shouldShow: true,
),
include: !isNil(comicBookDetailData.rawFileDetails),
}, },
{ {
id: 5, id: 5,
@@ -194,12 +214,11 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
key={5} key={5}
/> />
), ),
include: !isNil(comicBookDetailData.rawFileDetails), shouldShow: true,
}, },
]; ];
// filtered Tabs // filtered Tabs
const filteredTabs = tabGroup.filter((tab) => tab.include); const filteredTabs = tabGroup.filter((tab) => tab.shouldShow);
// Tabs // Tabs
const MetadataTabGroup = () => { const MetadataTabGroup = () => {
@@ -245,10 +264,7 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
// 2. from the CV-scraped version // 2. from the CV-scraped version
let imagePath = ""; let imagePath = "";
let comicBookTitle = ""; let comicBookTitle = "";
if ( if (areRawFileDetailsAvailable) {
!isUndefined(comicBookDetailData.rawFileDetails) &&
!isEmpty(comicBookDetailData.rawFileDetails.cover)
) {
const encodedFilePath = encodeURI( const encodedFilePath = encodeURI(
`${LIBRARY_SERVICE_HOST}/${comicBookDetailData.rawFileDetails.cover.filePath}`, `${LIBRARY_SERVICE_HOST}/${comicBookDetailData.rawFileDetails.cover.filePath}`,
); );

View File

@@ -16,16 +16,15 @@ import ellipsize from "ellipsize";
import { isEmpty, isNil, map } from "lodash"; import { isEmpty, isNil, map } from "lodash";
import { AirDCPPSocketContext } from "../../context/AirDCPPSocket"; import { AirDCPPSocketContext } from "../../context/AirDCPPSocket";
interface IAcquisitionPanelProps { interface IAcquisitionPanelProps {
comicBookMetadata: any; query: any;
} }
export const AcquisitionPanel = ( export const AcquisitionPanel = (
props: IAcquisitionPanelProps, props: IAcquisitionPanelProps,
): ReactElement => { ): ReactElement => {
const volumeName = console.log(props);
props.comicBookMetadata.sourcedMetadata.comicvine.volumeInformation.name; const issueName = props.query.issue.name;
const sanitizedVolumeName = volumeName.replace(/[^a-zA-Z0-9 ]/g, " "); const sanitizedIssueName = issueName.replace(/[^a-zA-Z0-9 ]/g, " ");
const issueName = props.comicBookMetadata.sourcedMetadata.comicvine.name;
// Selectors for picking state // Selectors for picking state
const airDCPPSearchResults = useSelector((state: RootState) => { const airDCPPSearchResults = useSelector((state: RootState) => {
@@ -51,7 +50,7 @@ export const AcquisitionPanel = (
// AirDC++ search query // AirDC++ search query
const dcppSearchQuery = { const dcppSearchQuery = {
query: { query: {
pattern: `${sanitizedVolumeName.replace(/#/g, "")}`, pattern: `${sanitizedIssueName.replace(/#/g, "")}`,
extensions: ["cbz", "cbr"], extensions: ["cbz", "cbr"],
}, },
hub_urls: map( hub_urls: map(

View File

@@ -191,6 +191,7 @@ export const Library = ({}: IComicBookLibraryProps): ReactElement => {
<h1 className="title">Library</h1> <h1 className="title">Library</h1>
{/* Search bar */} {/* Search bar */}
<SearchBar /> <SearchBar />
{!isUndefined(data) ? (
<div> <div>
<div className="library"> <div className="library">
<table {...getTableProps()} className="table is-hoverable"> <table {...getTableProps()} className="table is-hoverable">
@@ -306,7 +307,8 @@ export const Library = ({}: IComicBookLibraryProps): ReactElement => {
{/* page size selector */} {/* page size selector */}
<div <div
className={ className={
"dropdown " + (isPageSizeDropdownCollapsed ? "is-active" : "") "dropdown " +
(isPageSizeDropdownCollapsed ? "is-active" : "")
} }
onBlur={() => togglePageSizeDropdown()} onBlur={() => togglePageSizeDropdown()}
> >
@@ -336,6 +338,7 @@ export const Library = ({}: IComicBookLibraryProps): ReactElement => {
</nav> </nav>
</div> </div>
</div> </div>
) : null}
</div> </div>
</section> </section>
); );

View File

@@ -1,16 +1,29 @@
import React, { ReactElement } from "react"; 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";
export const SearchBar = (): ReactElement => { export const SearchBar = (): ReactElement => {
const foo = () => {}; const dispatch = useDispatch();
const handleSubmit = useCallback((e) => {
console.log(e);
dispatch(
searchIssue({
queryObject: {
volumeName: e.search,
},
}),
);
}, []);
return ( return (
<div className="box sticky"> <div className="box sticky">
<Form <Form
onSubmit={foo} onSubmit={handleSubmit}
initialValues={{}} initialValues={{}}
render={({ handleSubmit, form, submitting, pristine, values }) => ( render={({ handleSubmit, form, submitting, pristine, values }) => (
<form onSubmit={handleSubmit}>
<div className="column is-three-quarters search"> <div className="column is-three-quarters search">
<label>Search</label> <label>Search</label>
<Field name="search"> <Field name="search">
@@ -24,7 +37,11 @@ export const SearchBar = (): ReactElement => {
); );
}} }}
</Field> </Field>
<button className="button" type="submit">
Search
</button>
</div> </div>
</form>
)} )}
/> />
<div className="column one-fifth"> <div className="column one-fifth">

View File

@@ -1,6 +1,6 @@
import React, { useState, useEffect, useMemo, ReactElement } from "react"; import React, { useState, useEffect, useMemo, ReactElement } from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { useHistory } from "react-router"; import { useNavigate } from "react-router";
import { import {
removeLeadingPeriod, removeLeadingPeriod,
escapePoundSymbol, escapePoundSymbol,
@@ -10,7 +10,7 @@ import prettyBytes from "pretty-bytes";
import ellipsize from "ellipsize"; import ellipsize from "ellipsize";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { getComicBooks } from "../actions/fileops.actions"; import { getComicBooks } from "../actions/fileops.actions";
import { isNil, isEmpty } from "lodash"; import { isNil, isEmpty, isUndefined } from "lodash";
import Masonry from "react-masonry-css"; import Masonry from "react-masonry-css";
import Card from "./Carda"; import Card from "./Carda";
import { detectIssueTypes } from "../shared/utils/tradepaperback.utils"; import { detectIssueTypes } from "../shared/utils/tradepaperback.utils";
@@ -45,7 +45,7 @@ export const LibraryGrid = (libraryGridProps: ILibraryGridProps) => {
{data.map(({ _id, rawFileDetails, sourcedMetadata }) => { {data.map(({ _id, rawFileDetails, sourcedMetadata }) => {
let imagePath = ""; let imagePath = "";
let comicName = ""; let comicName = "";
if (!isNil(rawFileDetails)) { if (!isEmpty(rawFileDetails.cover)) {
const encodedFilePath = encodeURI( const encodedFilePath = encodeURI(
`${LIBRARY_SERVICE_HOST}/${removeLeadingPeriod( `${LIBRARY_SERVICE_HOST}/${removeLeadingPeriod(
rawFileDetails.cover.filePath, rawFileDetails.cover.filePath,
@@ -71,7 +71,7 @@ export const LibraryGrid = (libraryGridProps: ILibraryGridProps) => {
title={comicName ? titleElement : null} title={comicName ? titleElement : null}
> >
<div className="content is-flex is-flex-direction-row"> <div className="content is-flex is-flex-direction-row">
{!isNil(sourcedMetadata.comicvine) && ( {!isEmpty(sourcedMetadata.comicvine) && (
<span className="icon cv-icon is-small"> <span className="icon cv-icon is-small">
<img src="/dist/img/cvlogo.svg" /> <img src="/dist/img/cvlogo.svg" />
</span> </span>
@@ -81,13 +81,13 @@ export const LibraryGrid = (libraryGridProps: ILibraryGridProps) => {
<i className="fas fa-adjust" /> <i className="fas fa-adjust" />
</span> </span>
)} )}
{!isNil(sourcedMetadata.comicvine) && {!isUndefined(sourcedMetadata.comicvine.volumeInformation) &&
!isEmpty( !isEmpty(
detectIssueTypes( detectIssueTypes(
sourcedMetadata.comicvine.volumeInformation.description, sourcedMetadata.comicvine.volumeInformation.description,
), ),
) ? ( ) ? (
<span className="tag is-warning"> <span className="tag is-warning ml-1">
{ {
detectIssueTypes( detectIssueTypes(
sourcedMetadata.comicvine.volumeInformation sourcedMetadata.comicvine.volumeInformation

View File

@@ -161,7 +161,7 @@ const VolumeDetails = (props): ReactElement => {
if ( if (
!isUndefined(comicBookDetails.sourcedMetadata) && !isUndefined(comicBookDetails.sourcedMetadata) &&
!isUndefined(comicBookDetails.sourcedMetadata.comicvine) !isUndefined(comicBookDetails.sourcedMetadata.comicvine.volumeInformation)
) { ) {
return ( return (
<div className="container volume-details"> <div className="container volume-details">

View File

@@ -49,6 +49,12 @@ export const LIBRARY_SERVICE_BASE_URI = hostURIBuilder({
port: "3000", port: "3000",
apiPath: "/api/library", apiPath: "/api/library",
}); });
export const SEARCH_SERVICE_BASE_URI = hostURIBuilder({
protocol: "http",
host: process.env.UNDERLYING_HOSTNAME || "localhost",
port: "3000",
apiPath: "/api/search",
});
export const SETTINGS_SERVICE_BASE_URI = hostURIBuilder({ export const SETTINGS_SERVICE_BASE_URI = hostURIBuilder({
protocol: "http", protocol: "http",

View File

@@ -1,5 +1,5 @@
import { createStore, combineReducers, applyMiddleware } from "redux"; import { createStore, combineReducers, applyMiddleware } from "redux";
import { createBrowserHistory } from "history"; import { createHashHistory } from "history";
import { composeWithDevTools } from "redux-devtools-extension"; import { composeWithDevTools } from "redux-devtools-extension";
import thunk from "redux-thunk"; import thunk from "redux-thunk";
import { createReduxHistoryContext } from "redux-first-history"; import { createReduxHistoryContext } from "redux-first-history";
@@ -12,7 +12,7 @@ const socketConnection = io(SOCKET_BASE_URI, { transports: ["websocket"] });
const { createReduxHistory, routerMiddleware, routerReducer } = const { createReduxHistory, routerMiddleware, routerReducer } =
createReduxHistoryContext({ createReduxHistoryContext({
history: createBrowserHistory(), history: createHashHistory(),
}); });
export const store = createStore( export const store = createStore(

View File

@@ -73,9 +73,9 @@ module.exports = () => {
aliasFields: ["browser", "browser.esm"], aliasFields: ["browser", "browser.esm"],
}, },
devServer: { devServer: {
hot: true,
port: 3050, port: 3050,
open: true, open: true,
hot: true,
proxy: { proxy: {
"/api/**": { "/api/**": {
target: "http://localhost:8050", target: "http://localhost:8050",