From 71bad167ab30d045c6587e5742fafe11ef2d0a26 Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Thu, 29 Dec 2022 15:55:33 -0800 Subject: [PATCH 1/5] =?UTF-8?q?=F0=9F=94=A7=20Library=20zero=20state=20(#5?= =?UTF-8?q?2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🔧 Refactoring uncompression methods on client-side * ✏️ Refactoring * 👁️ Updates to the comic viewer * 🖼️ Added screenshots from December 2022 * ✏️ Fixed typo in README * 🏗️ Massive refactor around archive uncompression for reading/analysis * 🔧 Tweaked state vars for reading and analysis * 🏗️ Refactor to support DC++ and socket.io integration This refactor covers the following workflows: 1. Adding a comic from LOCG or ComicVine adds it to the wanted list 2. Downloading that comic from DC++ correctly adds download metadata to the corresponding comic object in mongo 3. Successful download triggers automatic import to library and cover extraction, metadata application From ddef87ea293cd0a702f2863cb866dc96a4c215c9 Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Fri, 30 Dec 2022 22:39:38 -0800 Subject: [PATCH 2/5] AirDC++ Connection Status (#53) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🔧 Fixed empty library state with explanation * ⚡️ Added a status indicator for the AirDC++ socket connection --- .../components/Dashboard/RecentlyImported.tsx | 2 -- src/client/components/Library/Library.tsx | 23 ++++++++++++++++--- src/client/components/Navbar.tsx | 8 +++++++ src/client/context/AirDCPPSocket.tsx | 5 ++-- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/client/components/Dashboard/RecentlyImported.tsx b/src/client/components/Dashboard/RecentlyImported.tsx index 8b64b50..8516b14 100644 --- a/src/client/components/Dashboard/RecentlyImported.tsx +++ b/src/client/components/Dashboard/RecentlyImported.tsx @@ -51,7 +51,6 @@ export const RecentlyImported = ({ }, idx, ) => { - console.log(comicvine); const { issueName, url } = determineCoverFile({ rawFileDetails, comicvine, @@ -63,7 +62,6 @@ export const RecentlyImported = ({ comicInfo, locg, }); - console.log(name); const isComicBookMetadataAvailable = !isUndefined(comicvine) && !isUndefined(comicvine.volumeInformation); diff --git a/src/client/components/Library/Library.tsx b/src/client/components/Library/Library.tsx index 107023f..0366e80 100644 --- a/src/client/components/Library/Library.tsx +++ b/src/client/components/Library/Library.tsx @@ -208,14 +208,13 @@ export const Library = (): ReactElement => { // ImportStatus.propTypes = { // value: PropTypes.bool.isRequired, // }; - return (

Library

- {!isUndefined(searchResults.hits) && ( + {!isEmpty(searchResults) ? (
{ />
- )} + ):
+
+
+
+ No comics were found in the library, Elasticsearch reports no + indices. Try importing a few comics into the library and come + back. +
+
+
+              {!isUndefined(searchError.data) &&
+                JSON.stringify(
+                  searchError.data.meta.body.error.root_cause,
+                  null,
+                  4,
+                )}
+            
+
+
}
); diff --git a/src/client/components/Navbar.tsx b/src/client/components/Navbar.tsx index c7c6999..db66d98 100644 --- a/src/client/components/Navbar.tsx +++ b/src/client/components/Navbar.tsx @@ -84,6 +84,14 @@ const Navbar: React.FunctionComponent = (props) => { ) : null} + {/* AirDC++ socket connection status */} +
+ + + +
+ +
Blog
diff --git a/src/client/context/AirDCPPSocket.tsx b/src/client/context/AirDCPPSocket.tsx index d7fbe9f..a0f30e4 100644 --- a/src/client/context/AirDCPPSocket.tsx +++ b/src/client/context/AirDCPPSocket.tsx @@ -12,7 +12,7 @@ const AirDCPPSocketContextProvider = ({ children }) => { airDCPPState: { settings: settingsObject, socket: {}, - socketConnectionInformation: {}, + socketConectionInformation: {}, }, }); }; @@ -55,7 +55,6 @@ const AirDCPPSocketContextProvider = ({ children }) => { protocol: `${host.protocol}`, hostname: `${host.hostname}:${host.port}`, }); - const socketConnectionInformation = await initializedAirDCPPSocket.connect( `${host.username}`, `${host.password}`, @@ -80,7 +79,7 @@ const AirDCPPSocketContextProvider = ({ children }) => { }; const AirDCPPSocketContext = createContext({ airDCPPState: {}, - saveSettings: () => {}, + saveSettings: () => { }, }); export { AirDCPPSocketContext, AirDCPPSocketContextProvider }; From 719ebe7c6e8a7e651239a3a0973d3817247f8740 Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Sun, 1 Jan 2023 17:14:27 -0800 Subject: [PATCH 3/5] ThreeTwo favicon (#54) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🔧 Fixed empty library state with explanation * ⚡️ Added a status indicator for the AirDC++ socket connection * 🖼️ Fixed the ThreeTwo favico --- public/favicon.ico | Bin 1150 -> 15406 bytes src/client/components/Navbar.tsx | 23 +++++++++++++++-------- src/client/context/AirDCPPSocket.tsx | 4 +++- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/public/favicon.ico b/public/favicon.ico index b34c9c79ddb5606abc55b74f9941b5d1b47397ef..a6de6b4e37f2ace8d2ca99f10404111ac890b785 100644 GIT binary patch literal 15406 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 Vfh`GaNnlF?TN2okz^6&z{{Zy}K@R`` literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x%r0{?*$Ll#>YJ^hc2FQqX2N5)L+ zzy3$Y;yc>@BV%N}AU3jI5F1%9h>ffl#2(d8S@;ivw1=KAJ_7}K&;@~f2_Oc^5d!Og z3UaV0$_I*p diff --git a/src/client/components/Navbar.tsx b/src/client/components/Navbar.tsx index db66d98..f7fb4b7 100644 --- a/src/client/components/Navbar.tsx +++ b/src/client/components/Navbar.tsx @@ -1,15 +1,17 @@ -import React from "react"; +import React, { useContext } from "react"; import { SearchBar } from "./GlobalSearchBar/SearchBar"; import { DownloadProgressTick } from "./ComicDetail/DownloadProgressTick"; import { Link } from "react-router-dom"; import { useSelector } from "react-redux"; -import { isUndefined } from "lodash"; +import { isUndefined, isEmpty } from "lodash"; +import { AirDCPPSocketContext } from "../context/AirDCPPSocket"; const Navbar: React.FunctionComponent = (props) => { const downloadProgressTick = useSelector( (state: RootState) => state.airdcpp.downloadProgressData, ); - + const airDCPPConfiguration = useContext(AirDCPPSocketContext); +console.log(airDCPPConfiguration) return (
{/* AirDC++ socket connection status */}
- - - + + {!isEmpty(airDCPPConfiguration.airDCPPState.socketConnectionInformation) ? ( + ) : null} + +
+ +
{JSON.stringify(airDCPPConfiguration.airDCPPState.socketConnectionInformation, null, 2)}
+
+
diff --git a/src/client/context/AirDCPPSocket.tsx b/src/client/context/AirDCPPSocket.tsx index a0f30e4..f1302d0 100644 --- a/src/client/context/AirDCPPSocket.tsx +++ b/src/client/context/AirDCPPSocket.tsx @@ -55,11 +55,13 @@ const AirDCPPSocketContextProvider = ({ children }) => { protocol: `${host.protocol}`, hostname: `${host.hostname}:${host.port}`, }); - const socketConnectionInformation = await initializedAirDCPPSocket.connect( + + let socketConnectionInformation = await initializedAirDCPPSocket.connect( `${host.username}`, `${host.password}`, true, ); + persistSettings({ ...airDCPPState, airDCPPState: { From a8b52d0ac61fcbc1be913c759b1c2d6146f5ad3c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 8 Jan 2023 08:49:09 -0800 Subject: [PATCH 4/5] Bump json5 from 1.0.1 to 1.0.2 (#56) Bumps [json5](https://github.com/json5/json5) from 1.0.1 to 1.0.2. - [Release notes](https://github.com/json5/json5/releases) - [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md) - [Commits](https://github.com/json5/json5/compare/v1.0.1...v1.0.2) --- updated-dependencies: - dependency-name: json5 dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index a11303c..c00d7cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12007,9 +12007,9 @@ json2mq@^0.2.0: string-convert "^0.2.0" json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== dependencies: minimist "^1.2.0" @@ -12973,9 +12973,9 @@ minimist-options@4.1.0: kind-of "^6.0.3" minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + version "1.2.7" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" + integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== minipass-collect@^1.0.2: version "1.0.2" From 8dd68e9d54e0cdef1a0bca5f6e897c341ea99ac1 Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Sat, 21 Jan 2023 02:29:32 -0800 Subject: [PATCH 5/5] AirDC++ Socket Status (#58) --- package.json | 7 +- src/client/actions/airdcpp.actions.tsx | 327 +++++++++++----------- src/client/assets/scss/App.scss | 4 + src/client/components/App.tsx | 2 +- src/client/components/Library/Library.tsx | 167 +++++------ src/client/components/Navbar.tsx | 81 ++++-- src/client/constants/action-types.ts | 2 + src/client/context/AirDCPPSocket.tsx | 30 +- src/client/reducers/airdcpp.reducer.ts | 21 +- src/client/services/DcppSearchService.ts | 4 +- src/client/services/api/SearchApi.ts | 2 +- yarn.lock | 21 +- 12 files changed, 376 insertions(+), 292 deletions(-) diff --git a/package.json b/package.json index ff95b58..07a3ce7 100644 --- a/package.json +++ b/package.json @@ -44,10 +44,6 @@ "babel-preset-minify": "^0.5.2", "better-docs": "^2.7.2", "comlink-loader": "^2.0.0", - "compromise": "^13.11.3", - "compromise-dates": "^2.2.1", - "compromise-numbers": "^1.4.0", - "compromise-sentences": "^0.3.0", "date-fns": "^2.28.0", "dayjs": "^1.10.6", "ellipsize": "^0.1.0", @@ -138,7 +134,6 @@ "bulma": "^0.9.3", "clean-webpack-plugin": "^1.0.0", "comlink": "^4.3.0", - "compromise-strict": "^0.0.2", "concurrently": "^4.0.0", "copy-webpack-plugin": "^9.0.1", "css-loader": "^5.1.2", @@ -182,4 +177,4 @@ "resolutions": { "@storybook/react/webpack": "^5" } -} \ No newline at end of file +} diff --git a/src/client/actions/airdcpp.actions.tsx b/src/client/actions/airdcpp.actions.tsx index 663bdb2..b63268b 100644 --- a/src/client/actions/airdcpp.actions.tsx +++ b/src/client/actions/airdcpp.actions.tsx @@ -4,7 +4,10 @@ import { PriorityEnum, SearchResponse, } from "threetwo-ui-typings"; -import { LIBRARY_SERVICE_BASE_URI, SEARCH_SERVICE_BASE_URI } from "../constants/endpoints"; +import { + LIBRARY_SERVICE_BASE_URI, + SEARCH_SERVICE_BASE_URI, +} from "../constants/endpoints"; import { AIRDCPP_SEARCH_RESULTS_ADDED, AIRDCPP_SEARCH_RESULTS_UPDATED, @@ -18,6 +21,8 @@ import { IMS_COMIC_BOOK_DB_OBJECT_FETCHED, AIRDCPP_TRANSFERS_FETCHED, LIBRARY_ISSUE_BUNDLES, + AIRDCPP_SOCKET_CONNECTED, + AIRDCPP_SOCKET_DISCONNECTED, } from "../constants/action-types"; import { isNil } from "lodash"; import axios from "axios"; @@ -32,190 +37,199 @@ function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } +export const toggleAirDCPPSocketConnectionStatus = + (status: String, payload?: any) => async (dispatch) => { + switch (status) { + case "connected": + dispatch({ + type: AIRDCPP_SOCKET_CONNECTED, + data: payload, + }); + break; + + case "disconnected": + dispatch({ + type: AIRDCPP_SOCKET_DISCONNECTED, + data: payload, + }); + break; + + default: + console.log("Can't set AirDC++ socket status."); + break; + } + }; export const search = (data: SearchData, ADCPPSocket: any, credentials: any) => - async (dispatch) => { - try { - if (!ADCPPSocket.isConnected()) { - await ADCPPSocket.connect( - credentials.username, - credentials.password, - true, - ); - } - const instance: SearchInstance = await ADCPPSocket.post("search"); - dispatch({ - type: AIRDCPP_SEARCH_IN_PROGRESS, - }); - - // We want to get notified about every new result in order to make the user experience better - await ADCPPSocket.addListener( - `search`, - "search_result_added", - async (groupedResult) => { - // ...add the received result in the UI - // (it's probably a good idea to have some kind of throttling for the UI updates as there can be thousands of results) - - dispatch({ - type: AIRDCPP_SEARCH_RESULTS_ADDED, - groupedResult, - }); - }, - instance.id, - ); - - // We also want to update the existing items in our list when new hits arrive for the previously listed files/directories - await ADCPPSocket.addListener( - `search`, - "search_result_updated", - async (groupedResult) => { - // ...update properties of the existing result in the UI - dispatch({ - type: AIRDCPP_SEARCH_RESULTS_UPDATED, - groupedResult, - }); - }, - instance.id, - ); - - // We need to show something to the user in case the search won't yield any results so that he won't be waiting forever) - // Wait for 5 seconds for any results to arrive after the searches were sent to the hubs - await ADCPPSocket.addListener( - `search`, - "search_hub_searches_sent", - async (searchInfo) => { - await sleep(5000); - - // Check the number of received results (in real use cases we should know that even without calling the API) - const currentInstance = await ADCPPSocket.get( - `search/${instance.id}`, - ); - if (currentInstance.result_count === 0) { - // ...nothing was received, show an informative message to the user - console.log("No more search results."); - } - - // The search can now be considered to be "complete" - // If there's an "in progress" indicator in the UI, that could also be disabled here - dispatch({ - type: AIRDCPP_HUB_SEARCHES_SENT, - searchInfo, - instance, - }); - }, - instance.id, - ); - // Finally, perform the actual search - await ADCPPSocket.post(`search/${instance.id}/hub_search`, data); - } catch (error) { - console.log(error); - throw error; + async (dispatch) => { + try { + if (!ADCPPSocket.isConnected()) { + await ADCPPSocket(); } - }; + const instance: SearchInstance = await ADCPPSocket.post("search"); + dispatch({ + type: AIRDCPP_SEARCH_IN_PROGRESS, + }); + + // We want to get notified about every new result in order to make the user experience better + await ADCPPSocket.addListener( + `search`, + "search_result_added", + async (groupedResult) => { + // ...add the received result in the UI + // (it's probably a good idea to have some kind of throttling for the UI updates as there can be thousands of results) + + dispatch({ + type: AIRDCPP_SEARCH_RESULTS_ADDED, + groupedResult, + }); + }, + instance.id, + ); + + // We also want to update the existing items in our list when new hits arrive for the previously listed files/directories + await ADCPPSocket.addListener( + `search`, + "search_result_updated", + async (groupedResult) => { + // ...update properties of the existing result in the UI + dispatch({ + type: AIRDCPP_SEARCH_RESULTS_UPDATED, + groupedResult, + }); + }, + instance.id, + ); + + // We need to show something to the user in case the search won't yield any results so that he won't be waiting forever) + // Wait for 5 seconds for any results to arrive after the searches were sent to the hubs + await ADCPPSocket.addListener( + `search`, + "search_hub_searches_sent", + async (searchInfo) => { + await sleep(5000); + + // Check the number of received results (in real use cases we should know that even without calling the API) + const currentInstance = await ADCPPSocket.get( + `search/${instance.id}`, + ); + if (currentInstance.result_count === 0) { + // ...nothing was received, show an informative message to the user + console.log("No more search results."); + } + + // The search can now be considered to be "complete" + // If there's an "in progress" indicator in the UI, that could also be disabled here + dispatch({ + type: AIRDCPP_HUB_SEARCHES_SENT, + searchInfo, + instance, + }); + }, + instance.id, + ); + // Finally, perform the actual search + await ADCPPSocket.post(`search/${instance.id}/hub_search`, data); + } catch (error) { + console.log(error); + throw error; + } + }; export const downloadAirDCPPItem = - (searchInstanceId: Number, + ( + searchInstanceId: Number, resultId: String, comicObjectId: String, - name: String, size: Number, type: any, + name: String, + size: Number, + type: any, ADCPPSocket: any, credentials: any, ): void => - async (dispatch) => { - try { - if (!ADCPPSocket.isConnected()) { - await ADCPPSocket.connect( - `${credentials.username}`, - `${credentials.password}`, - true, - ); - } - let bundleDBImportResult = {}; - const downloadResult = await ADCPPSocket.post( - `search/${searchInstanceId}/results/${resultId}/download`, - ); - - if (!isNil(downloadResult)) { - bundleDBImportResult = await axios({ - method: "POST", - url: `${LIBRARY_SERVICE_BASE_URI}/applyAirDCPPDownloadMetadata`, - headers: { - "Content-Type": "application/json; charset=utf-8", - }, - data: { - bundleId: downloadResult.bundle_info.id, - comicObjectId, - name, - size, - type, - }, - }); - - dispatch({ - type: AIRDCPP_RESULT_DOWNLOAD_INITIATED, - downloadResult, - bundleDBImportResult, - }); - - dispatch({ - type: IMS_COMIC_BOOK_DB_OBJECT_FETCHED, - comicBookDetail: bundleDBImportResult.data, - IMS_inProgress: false, - }); - - } - } catch (error) { - throw error; + async (dispatch) => { + try { + if (!ADCPPSocket.isConnected()) { + await ADCPPSocket.connect(); } - }; + let bundleDBImportResult = {}; + const downloadResult = await ADCPPSocket.post( + `search/${searchInstanceId}/results/${resultId}/download`, + ); -export const getBundlesForComic = - (comicObjectId: string, ADCPPSocket: any, credentials: any) => - async (dispatch) => { - try { - if (!ADCPPSocket.isConnected()) { - await ADCPPSocket.connect( - `${credentials.username}`, - `${credentials.password}`, - true, - ); - } - const comicObject = await axios({ + if (!isNil(downloadResult)) { + bundleDBImportResult = await axios({ method: "POST", - url: `${LIBRARY_SERVICE_BASE_URI}/getComicBookById`, + url: `${LIBRARY_SERVICE_BASE_URI}/applyAirDCPPDownloadMetadata`, headers: { "Content-Type": "application/json; charset=utf-8", }, data: { - id: `${comicObjectId}`, + bundleId: downloadResult.bundle_info.id, + comicObjectId, + name, + size, + type, }, }); - // get only the bundles applicable for the comic - if (comicObject.data.acquisition.directconnect) { - const filteredBundles = comicObject.data.acquisition.directconnect.downloads.map( + + dispatch({ + type: AIRDCPP_RESULT_DOWNLOAD_INITIATED, + downloadResult, + bundleDBImportResult, + }); + + dispatch({ + type: IMS_COMIC_BOOK_DB_OBJECT_FETCHED, + comicBookDetail: bundleDBImportResult.data, + IMS_inProgress: false, + }); + } + } catch (error) { + throw error; + } + }; + +export const getBundlesForComic = + (comicObjectId: string, ADCPPSocket: any, credentials: any) => + async (dispatch) => { + try { + if (!ADCPPSocket.isConnected()) { + await ADCPPSocket.connect(); + } + const comicObject = await axios({ + method: "POST", + url: `${LIBRARY_SERVICE_BASE_URI}/getComicBookById`, + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + data: { + id: `${comicObjectId}`, + }, + }); + // get only the bundles applicable for the comic + if (comicObject.data.acquisition.directconnect) { + const filteredBundles = + comicObject.data.acquisition.directconnect.downloads.map( async ({ bundleId }) => { return await ADCPPSocket.get(`queue/bundles/${bundleId}`); }, ); - dispatch({ - type: AIRDCPP_BUNDLES_FETCHED, - bundles: await Promise.all(filteredBundles), - }); - } - } catch (error) { - throw error; + dispatch({ + type: AIRDCPP_BUNDLES_FETCHED, + bundles: await Promise.all(filteredBundles), + }); } - }; + } catch (error) { + throw error; + } + }; export const getTransfers = (ADCPPSocket: any, credentials: any) => async (dispatch) => { try { if (!ADCPPSocket.isConnected()) { - await ADCPPSocket.connect( - `${credentials.username}`, - `${credentials.password}`, - true, - ); + await ADCPPSocket.connect(); } const bundles = await ADCPPSocket.get("queue/bundles/1/85", {}); if (!isNil(bundles)) { @@ -234,7 +248,6 @@ export const getTransfers = type: LIBRARY_ISSUE_BUNDLES, issue_bundles, }); - } } catch (err) { throw err; diff --git a/src/client/assets/scss/App.scss b/src/client/assets/scss/App.scss index 1bc5cf6..efb9b6b 100644 --- a/src/client/assets/scss/App.scss +++ b/src/client/assets/scss/App.scss @@ -62,6 +62,10 @@ pre { margin-left: -300px; min-width: 500px; } + .airdcpp-status { + min-width: 300px; + line-height: 1.7rem; + } body { background: #454a59; } diff --git a/src/client/components/App.tsx b/src/client/components/App.tsx index 1df28b4..6446767 100644 --- a/src/client/components/App.tsx +++ b/src/client/components/App.tsx @@ -137,4 +137,4 @@ export const App = (): ReactElement => { ); }; -export default App; \ No newline at end of file +export default App; diff --git a/src/client/components/Library/Library.tsx b/src/client/components/Library/Library.tsx index 0366e80..c808ec3 100644 --- a/src/client/components/Library/Library.tsx +++ b/src/client/components/Library/Library.tsx @@ -8,10 +8,9 @@ import { useDispatch, useSelector } from "react-redux"; import { searchIssue } from "../../actions/fileops.actions"; import ellipsize from "ellipsize"; - /** * Component that tabulates the contents of the user's ThreeTwo Library. - * + * * @component * @example * @@ -20,9 +19,10 @@ export const Library = (): ReactElement => { const searchResults = useSelector( (state: RootState) => state.fileOps.libraryComics, ); - const searchError = useSelector( - (state: RootState) => state.fileOps.librarySearchError, - ); + const searchError = useSelector((state: RootState) => { + console.log(state); + return state.fileOps.librarySearchError; + }); const dispatch = useDispatch(); useEffect(() => { dispatch( @@ -36,7 +36,7 @@ export const Library = (): ReactElement => { from: 0, }, type: "all", - trigger: "libraryPage" + trigger: "libraryPage", }, ), ); @@ -89,63 +89,67 @@ export const Library = (): ReactElement => { const WantedStatus = ({ value }) => { return !value ? Wanted : null; }; - const columns = useMemo(() => [ - { - header: "Comic Metadata", - footer: 1, - columns: [ - { - header: "File Details", - id: "fileDetails", - minWidth: 400, - accessorKey: "_source", - cell: info => { - return ; + const columns = useMemo( + () => [ + { + header: "Comic Metadata", + footer: 1, + columns: [ + { + header: "File Details", + id: "fileDetails", + minWidth: 400, + accessorKey: "_source", + cell: (info) => { + return ; + }, }, - }, - { - header: "ComicInfo.xml", - accessorKey: "_source.sourcedMetadata.comicInfo", - align: "center", - minWidth: 250, - cell: info => - !isEmpty(info.getValue()) ? ( - - ) : ( - No ComicInfo.xml - ), - }, - ], - }, - { - header: "Additional Metadata", - columns: [ - { - header: "Publisher", - accessorKey: - "_source.sourcedMetadata.comicvine.volumeInformation", - cell: info => { - return ( - !isNil(info.getValue()) && ( -
- {info.getValue().publisher.name} -
- ) - ); + { + header: "ComicInfo.xml", + accessorKey: "_source.sourcedMetadata.comicInfo", + align: "center", + minWidth: 250, + cell: (info) => + !isEmpty(info.getValue()) ? ( + + ) : ( + No ComicInfo.xml + ), }, - }, - { - header: "Something", - accessorKey: "_source.acquisition.source.wanted", - cell: info => { - !isUndefined(info.getValue()) ? - : "Nothing"; + ], + }, + { + header: "Additional Metadata", + columns: [ + { + header: "Publisher", + accessorKey: "_source.sourcedMetadata.comicvine.volumeInformation", + cell: (info) => { + return ( + !isNil(info.getValue()) && ( +
+ {info.getValue().publisher.name} +
+ ) + ); + }, }, - }, - ], - } - ], []); - + { + header: "Something", + accessorKey: "_source.acquisition.source.wanted", + cell: (info) => { + !isUndefined(info.getValue()) ? ( + + ) : ( + "Nothing" + ); + }, + }, + ], + }, + ], + [], + ); /** * Pagination control that fetches the next x (pageSize) items @@ -153,7 +157,7 @@ export const Library = (): ReactElement => { * @param {number} pageIndex * @param {number} pageSize * @returns void - * + * **/ const nextPage = useCallback((pageIndex: number, pageSize: number) => { dispatch( @@ -173,7 +177,6 @@ export const Library = (): ReactElement => { ); }, []); - /** * Pagination control that fetches the previous x (pageSize) items * based on the y (pageIndex) offset from the ThreeTwo Elasticsearch index @@ -199,7 +202,7 @@ export const Library = (): ReactElement => { from, }, type: "all", - trigger: "libraryPage" + trigger: "libraryPage", }, ), ); @@ -229,25 +232,27 @@ export const Library = (): ReactElement => { />
- ):
-
-
-
- No comics were found in the library, Elasticsearch reports no - indices. Try importing a few comics into the library and come - back. -
-
-
-              {!isUndefined(searchError.data) &&
-                JSON.stringify(
-                  searchError.data.meta.body.error.root_cause,
-                  null,
-                  4,
-                )}
-            
+ ) : ( +
+
+
+
+ No comics were found in the library, Elasticsearch reports no + indices. Try importing a few comics into the library and come + back. +
+
+
+                {!isUndefined(searchError.data) &&
+                  JSON.stringify(
+                    searchError.data.meta.body.error.root_cause,
+                    null,
+                    4,
+                  )}
+              
+
-
} + )}
); diff --git a/src/client/components/Navbar.tsx b/src/client/components/Navbar.tsx index f7fb4b7..6de6dde 100644 --- a/src/client/components/Navbar.tsx +++ b/src/client/components/Navbar.tsx @@ -3,15 +3,23 @@ import { SearchBar } from "./GlobalSearchBar/SearchBar"; import { DownloadProgressTick } from "./ComicDetail/DownloadProgressTick"; import { Link } from "react-router-dom"; import { useSelector } from "react-redux"; -import { isUndefined, isEmpty } from "lodash"; -import { AirDCPPSocketContext } from "../context/AirDCPPSocket"; +import { isUndefined } from "lodash"; +import { format, fromUnixTime } from "date-fns"; const Navbar: React.FunctionComponent = (props) => { const downloadProgressTick = useSelector( (state: RootState) => state.airdcpp.downloadProgressData, ); - const airDCPPConfiguration = useContext(AirDCPPSocketContext); -console.log(airDCPPConfiguration) + + const airDCPPSocketConnectionStatus = useSelector( + (state: RootState) => state.airdcpp.isAirDCPPSocketConnected, + ); + const airDCPPSessionInfo = useSelector( + (state: RootState) => state.airdcpp.airDCPPSessionInfo, + ); + const socketDisconnectionReason = useSelector( + (state: RootState) => state.airdcpp.socketDisconnectionReason, + ); return (