diff --git a/package.json b/package.json index c297c15..abac689 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "react-final-form": "^6.5.3", "react-lazylog": "^4.5.3", "react-loader-spinner": "^4.0.0", + "react-masonry-css": "^1.0.16", "react-notification-system": "^0.4.0", "react-notification-system-redux": "^2.0.1", "react-select": "^4.3.1", diff --git a/src/client/assets/scss/App.scss b/src/client/assets/scss/App.scss index 52486e1..28fb4a1 100644 --- a/src/client/assets/scss/App.scss +++ b/src/client/assets/scss/App.scss @@ -278,6 +278,24 @@ $border-color: red; } } +// Library grid +.my-masonry-grid { + display: -webkit-box; /* Not needed if autoprefixing */ + display: -ms-flexbox; /* Not needed if autoprefixing */ + display: flex; + margin-left: -30px; /* gutter size offset */ + width: auto; +} +.my-masonry-grid_column { + padding-left: 30px; /* gutter size */ + background-clip: padding-box; +} + +/* Style your items */ +.my-masonry-grid_column > div { /* change div to reference your elements you put in */ + margin-bottom: 20px; +} + // progress .progress-indicator-container { height: 100%; diff --git a/src/client/components/App.tsx b/src/client/components/App.tsx index b4e240f..4b65bea 100644 --- a/src/client/components/App.tsx +++ b/src/client/components/App.tsx @@ -6,6 +6,7 @@ import Dashboard from "./Dashboard"; import Import from "./Import"; import { ComicDetail } from "./ComicDetail"; import Library from "./Library"; +import LibraryGrid from "./LibraryGrid"; import Search from "./Search"; import Settings from "./Settings"; @@ -78,6 +79,9 @@ export const App = (): ReactElement => { + + + diff --git a/src/client/components/LibraryGrid.tsx b/src/client/components/LibraryGrid.tsx new file mode 100644 index 0000000..b7a4723 --- /dev/null +++ b/src/client/components/LibraryGrid.tsx @@ -0,0 +1,101 @@ +import React, { useState, useEffect, useMemo, ReactElement } from "react"; +import PropTypes from "prop-types"; +import { useHistory } from "react-router"; +import { + removeLeadingPeriod, + escapePoundSymbol, +} from "../shared/utils/formatting.utils"; +import { useTable, usePagination } from "react-table"; +import prettyBytes from "pretty-bytes"; +import ellipsize from "ellipsize"; +import { useDispatch, useSelector } from "react-redux"; +import { getComicBooks } from "../actions/fileops.actions"; +import { isNil, isEmpty } from "lodash"; +import Masonry from "react-masonry-css"; +import Card from "./Carda"; +import { detectTradePaperbacks } from "../shared/utils/tradepaperback.utils"; +import { Link } from "react-router-dom"; + +interface ILibraryGridProps {} +export const LibraryGrid = (libraryGridProps: ILibraryGridProps) => { + const data = useSelector( + (state: RootState) => state.fileOps.recentComics.docs, + ); + const pageTotal = useSelector( + (state: RootState) => state.fileOps.recentComics.totalDocs, + ); + const breakpointColumnsObj = { + default: 5, + 1100: 4, + 700: 3, + 500: 1, + }; + + return ( +
+
+

Library

+ + + {data.map(({ _id, rawFileDetails, sourcedMetadata }) => { + let imagePath = ""; + let comicName = ""; + if (!isNil(rawFileDetails)) { + const encodedFilePath = encodeURI( + "http://localhost:3000" + + removeLeadingPeriod(rawFileDetails.path), + ); + imagePath = escapePoundSymbol(encodedFilePath); + comicName = rawFileDetails.name; + } else if (!isNil(sourcedMetadata)) { + imagePath = sourcedMetadata.comicvine.image.small_url; + comicName = sourcedMetadata.comicvine.name; + } + const titleElement = ( + + {ellipsize(comicName, 18)} + + ); + console.log(name); + return ( + +
+ {!isNil(sourcedMetadata.comicvine) && ( + + + + )} + {isNil(rawFileDetails) && ( + + + + )} + {!isNil(sourcedMetadata.comicvine) && + !isEmpty( + detectTradePaperbacks( + sourcedMetadata.comicvine.volumeInformation.description, + ), + ) ? ( + TPB + ) : null} +
+
+ ); + })} +
+
+
+ ); +}; + +export default LibraryGrid; diff --git a/src/client/components/Navbar.tsx b/src/client/components/Navbar.tsx index a5dd7ce..d42bcab 100644 --- a/src/client/components/Navbar.tsx +++ b/src/client/components/Navbar.tsx @@ -47,6 +47,9 @@ const Navbar: React.FunctionComponent = (props) => { Library + + Library Grid + Search diff --git a/src/client/index.tsx b/src/client/index.tsx index 9a3fd9c..4513a49 100644 --- a/src/client/index.tsx +++ b/src/client/index.tsx @@ -7,7 +7,6 @@ import WebSocketProvider, { } from "./context/socket/socket.context"; import configureStore, { history } from "./store/index"; import App from "./components/App"; - const store = configureStore({}); const rootEl = document.getElementById("root"); diff --git a/src/client/shared/utils/filenameparser.utils.ts b/src/client/shared/utils/filenameparser.utils.ts index 135c4c3..29b0f31 100644 --- a/src/client/shared/utils/filenameparser.utils.ts +++ b/src/client/shared/utils/filenameparser.utils.ts @@ -17,12 +17,12 @@ interface M { value: string; } -function replaceRecursive( +const replaceRecursive = ( text: string, left: string, right: string, replacer: (match: string) => string, -): string { +): string => { const r: M[] = xregexp.matchRecursive(text, left, right, "g", { valueNames: [null, null, "match", null], }); @@ -33,7 +33,7 @@ function replaceRecursive( offset += replacement.length - m.value.length; } return text; -} +}; function replaceAt( string: string, @@ -48,7 +48,7 @@ export const preprocess = (inputString: string) => { // see if the comic matches the following format, and if so, remove everything // after the first number: // "nnn series name #xx (etc) (etc)" -> "series name #xx (etc) (etc)" - const format1 = inputString.match(/^\s*(\d+)[\s._-]+?([^#]+)(\W+.*)/); + const format1 = inputString.match(/^\s*(\d+)[\s._-]+?([^#]+)(\W+.*)/gim); // see if the comic matches the following format, and if so, remove everything // after the first number that isn't in brackets: @@ -56,6 +56,12 @@ export const preprocess = (inputString: string) => { const format2 = inputString.match( /^((?:[a-zA-Z,.-]+\s)+)(\#?(?:\d+[.0-9*])\s*(?:-))(.*((\(.*)?))$/gis, ); + return { + matches: { + format1, + format2, + }, + }; }; /** @@ -67,6 +73,9 @@ export const tokenize = (inputString: string) => { const doc = nlp(inputString); const sentence = doc.sentences().json(); + // filter out anything at the end of the title in parantheses + inputString = inputString.replace(/\((.*?)\)$/gi, ""); + // regexes to match constituent parts of the search string // and isolate the search terms @@ -154,7 +163,9 @@ export const extractNumerals = (inputString: string): MatchArray[string] => { }; export const refineQuery = (inputString: string) => { + const queryObj = tokenize(inputString); + console.log(queryObj) const removedYears = xor( queryObj.sentence_tokens.normalized, queryObj.years.yearMatches, diff --git a/src/client/shared/utils/tradepaperback.utils.ts b/src/client/shared/utils/tradepaperback.utils.ts index dcd1a11..e569748 100644 --- a/src/client/shared/utils/tradepaperback.utils.ts +++ b/src/client/shared/utils/tradepaperback.utils.ts @@ -5,9 +5,8 @@ export const detectTradePaperbacks = (deck): any => { /((trade)?\s?(paperback)|(tpb))/gim, // https://regex101.com/r/FhuowT/1 /(hard\s?cover)\s?(collect((ion)|(ed)|(ing)))/gim, //https://regex101.com/r/eFJVRM/1 ]; - const miniSeries = [ + const miniSeries = [/mini\Wseries/gim]; - ] const matches = paperback .map((regex) => { return deck.match(regex); diff --git a/src/server/route/routes/importComics.routes.ts b/src/server/route/routes/importComics.routes.ts index d3d0f30..06aa91b 100644 --- a/src/server/route/routes/importComics.routes.ts +++ b/src/server/route/routes/importComics.routes.ts @@ -5,14 +5,14 @@ import axios from "axios"; router.route("/getComicCovers").post(async (req: Request, res: Response) => { typeof req.body === "object" ? req.body : {}; await axios.request({ - url: "http://localhost:3000/api/import/importComicsToDB", + url: "http://localhost:3000/api/import/processAndImportToDB", method: "POST", data: { extractionOptions: req.body.extractionOptions, walkedFolders: req.body.walkedFolders, }, }); - res.send({ po: "jo" }); + res.send({ message: "Scan and import initiated." }); }); export default router; diff --git a/yarn.lock b/yarn.lock index b5c210d..64ec690 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10986,6 +10986,11 @@ react-loader-spinner@^4.0.0: dependencies: prop-types "^15.7.2" +react-masonry-css@^1.0.16: + version "1.0.16" + resolved "https://registry.yarnpkg.com/react-masonry-css/-/react-masonry-css-1.0.16.tgz#72b28b4ae3484e250534700860597553a10f1a2c" + integrity sha512-KSW0hR2VQmltt/qAa3eXOctQDyOu7+ZBevtKgpNDSzT7k5LA/0XntNa9z9HKCdz3QlxmJHglTZ18e4sX4V8zZQ== + react-modal@^3.12.1: version "3.14.3" resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.14.3.tgz#7eb7c5ec85523e5843e2d4737cc17fc3f6aeb1c0"