Compare commits

...

12 Commits

Author SHA1 Message Date
bbe6207a25 Merge branch 'master' into dcpp-socket-status 2023-01-01 17:14:18 -08:00
a47b67c6c9 🖼️ Fixed the ThreeTwo favico 2023-01-01 17:08:12 -08:00
ddef87ea29 AirDC++ Connection Status (#53)
* 🔧 Fixed empty library state with explanation

* ️ Added a status indicator for the AirDC++ socket connection
2022-12-30 22:39:38 -08:00
c09ec6bcfe ️ Added a status indicator for the AirDC++ socket connection 2022-12-31 06:21:21 +00:00
71bad167ab 🔧 Library zero state (#52)
* 🔧 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
2022-12-29 15:55:33 -08:00
df1fbc7dd3 🔧 Fixed empty library state with explanation 2022-12-29 23:33:23 +00:00
789e5b9518 Merge branch 'master' of https://github.com/rishighan/threetwo 2022-12-21 21:49:42 -08:00
ce6653b5d7 🏗️ Applying the refactor patc 2022-12-21 21:49:12 -08:00
d065225d8e 🏗️ Refactoring archive uncompression for "Read Comic" and "Analysis" user flows (#46)
* 🔧 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
2022-12-21 21:17:38 -08:00
dependabot[bot]
f854ff9cc6 Bump decode-uri-component from 0.2.0 to 0.2.2 (#45)
Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2.
- [Release notes](https://github.com/SamVerschueren/decode-uri-component/releases)
- [Commits](https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.2)

---
updated-dependencies:
- dependency-name: decode-uri-component
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-06 20:35:38 -08:00
815444a973 🔧 Refactoring the uncompression methods for "Read Comic" and "Uncompress Archive" operations (#43)
* 🔧 Refactoring uncompression methods on client-side

* ✏️ Refactoring

* 👁️ Updates to the comic viewer

* 🖼️ Added screenshots from December 2022

* ✏️ Fixed typo in README
2022-12-06 14:08:53 -08:00
1cbf53be98 🖼️ Adding screenshots as of December 2022 (#44) 2022-12-06 14:07:44 -08:00
25 changed files with 320 additions and 160 deletions

BIN
ComicVine Matching.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

BIN
DC++ integration.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1013 KiB

BIN
Dashboard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

BIN
Library.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@@ -4,6 +4,16 @@ ThreeTwo! _aims to be_ a comic book curation app.
[![Docker Image CI](https://github.com/rishighan/threetwo/actions/workflows/docker-image.yml/badge.svg)](https://github.com/rishighan/threetwo/actions/workflows/docker-image.yml)
### Screenshots
![](https://raw.githubusercontent.com/rishighan/threetwo/rishighan-screenshots-dec-2022/Dashboard.png)
![](https://raw.githubusercontent.com/rishighan/threetwo/rishighan-screenshots-dec-2022/Library.png)
![](https://raw.githubusercontent.com/rishighan/threetwo/rishighan-screenshots-dec-2022/DC%2B%2B%20integration.png)
![](https://raw.githubusercontent.com/rishighan/threetwo/rishighan-screenshots-dec-2022/ComicVine%20Matching.png)
### 🦄 Early Development Support Channel

View File

@@ -63,9 +63,9 @@
"pretty-bytes": "^5.6.0",
"prop-types": "^15.8.1",
"qs": "^6.10.5",
"react": "^18.1.0",
"react": "^18.2.0",
"react-collapsible": "^2.9.0",
"react-comic-viewer": "^0.3.5",
"react-comic-viewer": "^0.4.0",
"react-day-picker": "^8.0.6",
"react-dom": "^18.1.0",
"react-fast-compare": "^3.2.0",
@@ -182,4 +182,4 @@
"resolutions": {
"@storybook/react/webpack": "^5"
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -36,7 +36,7 @@ import {
CV_WEEKLY_PULLLIST_FETCHED,
} from "../constants/action-types";
import { success } from "react-notification-system-redux";
import { removeLeadingPeriod } from "../shared/utils/formatting.utils";
import { isNil, map } from "lodash";
export async function walkFolder(path: string): Promise<Array<IFolderData>> {
@@ -60,15 +60,6 @@ export async function walkFolder(path: string): Promise<Array<IFolderData>> {
* @return the comic book metadata
*/
export const fetchComicBookMetadata = () => async (dispatch) => {
const extractionOptions = {
extractTarget: "cover",
targetExtractionFolder: "./userdata/covers",
extractionMode: "bulk",
paginationOptions: {
pageLimit: 25,
page: 1,
},
};
dispatch({
type: LS_IMPORT_CALL_IN_PROGRESS,
});
@@ -86,7 +77,7 @@ export const fetchComicBookMetadata = () => async (dispatch) => {
dispatch({
type: LS_IMPORT,
meta: { remote: true },
data: { extractionOptions },
data: {},
});
};
export const toggleImportQueueStatus = (options) => async (dispatch) => {
@@ -136,21 +127,24 @@ export const getComicBooks = (options) => async (dispatch) => {
* @returns Nothing.
* @param payload
*/
export const importToDB = (sourceName: string, payload?: any) => (dispatch) => {
export const importToDB = (sourceName: string, metadata?: any) => (dispatch) => {
try {
const comicBookMetadata = {
rawFileDetails: {
name: "",
},
importStatus: {
isImported: true,
tagged: false,
matchedResult: {
score: "0",
importType: "new",
payload: {
rawFileDetails: {
name: "",
},
},
sourcedMetadata: payload || null,
acquisition: { source: { wanted: true, name: sourceName } },
importStatus: {
isImported: true,
tagged: false,
matchedResult: {
score: "0",
},
},
sourcedMetadata: metadata || null,
acquisition: { source: { wanted: true, name: sourceName } },
}
};
dispatch({
type: IMS_CV_METADATA_IMPORT_CALL_IN_PROGRESS,
@@ -256,36 +250,35 @@ export const fetchComicVineMatches =
/**
* This method is a proxy to `uncompressFullArchive` which uncompresses complete `rar` or `zip` archives
* @param {string} path The path to the compressed archive
* @param {any} options Options object
* @param {any} options Options object
* @returns {any}
*/
export const extractComicArchive = (path: string, options: any) => async (dispatch) => {
const comicBookPages: string[] = [];
dispatch({
type: IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_IN_PROGRESS,
});
const extractedComicBookArchive = await axios({
method: "POST",
url: `${LIBRARY_SERVICE_BASE_URI}/uncompressFullArchive`,
headers: {
"Content-Type": "application/json; charset=utf-8",
},
data: {
filePath: path,
},
});
map(extractedComicBookArchive.data, (page) => {
console.log(page);
const pageFilePath = removeLeadingPeriod(page);
const imagePath = encodeURI(`${LIBRARY_SERVICE_HOST}${pageFilePath}`);
comicBookPages.push(imagePath);
});
dispatch({
type: IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_SUCCESS,
extractedComicBookArchive: comicBookPages,
});
};
export const extractComicArchive =
(path: string, options: any): any =>
async (dispatch) => {
dispatch({
type: IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_IN_PROGRESS,
});
await axios({
method: "POST",
url: `${LIBRARY_SERVICE_BASE_URI}/uncompressFullArchive`,
headers: {
"Content-Type": "application/json; charset=utf-8",
},
data: {
filePath: path,
options,
},
});
};
/**
* Description
* @param {any} query
* @param {any} options
* @returns {any}
*/
export const searchIssue = (query, options) => async (dispatch) => {
dispatch({
type: SS_SEARCH_IN_PROGRESS,
@@ -356,4 +349,4 @@ export const analyzeImage =
type: IMG_ANALYSIS_DATA_FETCH_SUCCESS,
result: foo.data,
});
};
};

View File

@@ -356,6 +356,10 @@ pre {
border-radius: 0.5rem;
}
.comic-viewer {
border: 1px solid red;
}
// comicvine metadata
.comicvine-metadata {
background-color: #f2f1f9;

View File

@@ -18,18 +18,22 @@ import {
AirDCPPSocketContext,
} from "../context/AirDCPPSocket";
import { isEmpty, isUndefined } from "lodash";
import { AIRDCPP_DOWNLOAD_PROGRESS_TICK } from "../constants/action-types";
import { useDispatch } from "react-redux";
import {
AIRDCPP_DOWNLOAD_PROGRESS_TICK,
LS_SINGLE_IMPORT,
} from "../constants/action-types";
import { useDispatch, useSelector } from "react-redux";
/**
* Method that initializes an AirDC++ socket connection
* 1. Initializes event listeners for download init, tick and complete events
* 2. Handles errors in case the connection to AirDC++ is not established or terminated
* @returns void
* 2. Handles errors in case the connection to AirDC++ is not established or terminated
* @returns void
*/
const AirDCPPSocketComponent = (): ReactElement => {
const airDCPPConfiguration = useContext(AirDCPPSocketContext);
const dispatch = useDispatch();
useEffect(() => {
const initializeAirDCPPEventListeners = async () => {
if (
@@ -42,9 +46,7 @@ const AirDCPPSocketComponent = (): ReactElement => {
"queue_bundle_added",
async (data) => {
console.log("JEMEN:", data);
}
},
);
// download tick listener
await airDCPPConfiguration.airDCPPState.socket.addListener(
@@ -62,9 +64,18 @@ const AirDCPPSocketComponent = (): ReactElement => {
`queue`,
"queue_bundle_status",
async (bundleData) => {
let count = 0;
if (bundleData.status.completed && bundleData.status.downloaded) {
// dispatch the action for raw import, with the metadata
console.log("IM THE MAN UP IN THIS")
if (count < 1) {
console.log(`[AirDCPP]: Download complete.`);
dispatch({
type: LS_SINGLE_IMPORT,
meta: { remote: true },
data: bundleData,
});
count += 1;
}
}
},
);
@@ -92,7 +103,10 @@ export const App = (): ReactElement => {
<Routes>
<Route path="/" element={<Dashboard />} />
<Route path="/import" element={<Import path={"./comics"} />} />
<Route path="/library" element={<TabulatedContentContainer category="library" />} />
<Route
path="/library"
element={<TabulatedContentContainer category="library" />}
/>
<Route path="/library-grid" element={<LibraryGrid />} />
<Route path="/downloads" element={<Downloads data={{}} />} />
<Route path="/search" element={<Search />} />
@@ -105,13 +119,22 @@ export const App = (): ReactElement => {
element={<VolumeDetail />}
/>
<Route path="/settings" element={<Settings />} />
<Route path="/pull-list/all" element={<TabulatedContentContainer category="pullList" />} />
<Route path="/wanted/all" element={<TabulatedContentContainer category="wanted" />} />
<Route path="/volumes/all" element={<TabulatedContentContainer category="volumes" />} />
<Route
path="/pull-list/all"
element={<TabulatedContentContainer category="pullList" />}
/>
<Route
path="/wanted/all"
element={<TabulatedContentContainer category="wanted" />}
/>
<Route
path="/volumes/all"
element={<TabulatedContentContainer category="volumes" />}
/>
</Routes>
</div>
</AirDCPPSocketContextProvider>
);
};
export default App;
export default App;

View File

@@ -16,6 +16,7 @@ import ellipsize from "ellipsize";
import { Form, Field } from "react-final-form";
import { isEmpty, isNil, map } from "lodash";
import { AirDCPPSocketContext } from "../../context/AirDCPPSocket";
interface IAcquisitionPanelProps {
query: any;
comicObjectId: any;
@@ -96,9 +97,12 @@ export const AcquisitionPanel = (
(searchInstanceId, resultId, name, size, type) => {
dispatch(
downloadAirDCPPItem(
searchInstanceId, resultId,
searchInstanceId,
resultId,
props.comicObjectId,
name, size, type,
name,
size,
type,
airDCPPConfiguration.airDCPPState.socket,
{
username: `${airDCPPConfiguration.airDCPPState.settings.directConnect.client.host.username}`,
@@ -332,4 +336,4 @@ export const AcquisitionPanel = (
);
};
export default AcquisitionPanel;
export default AcquisitionPanel;

View File

@@ -66,7 +66,7 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
);
const extractedComicBook = useSelector(
(state: RootState) => state.fileOps.extractedComicBookArchive,
(state: RootState) => state.fileOps.extractedComicBookArchive.reading,
);
const { comicObjectId } = useParams<{ comicObjectId: string }>();
@@ -74,13 +74,21 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
const openModal = useCallback((filePath) => {
setIsOpen(true);
dispatch(extractComicArchive(filePath));
dispatch(
extractComicArchive(filePath, {
type: "full",
purpose: "reading",
imageResizeOptions: {
baseWidth: 1024,
},
}),
);
}, []);
const afterOpenModal = useCallback((things) => {
// references are now sync'd and can be accessed.
// subtitle.style.color = "#f00";
console.log(things);
console.log("kolaveri", things);
}, []);
const closeModal = useCallback(() => {
@@ -94,7 +102,7 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
<>
<div className="card search-criteria-card">
<div className="card-content">
<ComicVineSearchForm data={rawFileDetails}/>
<ComicVineSearchForm data={rawFileDetails} />
</div>
</div>
<p className="is-size-5 mt-3 mb-2 ml-3">Searching for:</p>
@@ -112,7 +120,9 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
comicVineSearchResults,
comicObjectId,
}}
/>) : (<div className="progress-indicator-container" >
/>
) : (
<div className="progress-indicator-container">
<div className="indicator">
<Loader
type="MutatingDots"
@@ -123,16 +133,16 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
visible={comicVineAPICallProgress}
/>
</div>
</div >)}
</>),
</div>
)}
</>
),
},
editComicBookMetadata: {
content: () => <EditMetadataPanel />,
},
};
// check for the availability of CV metadata
const isComicBookMetadataAvailable =
@@ -249,7 +259,7 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
</div>
</div>
{/* raw file details */}
<div className="column is-three-fifths">
<div className="column">
{!isUndefined(rawFileDetails) &&
!isEmpty(rawFileDetails.cover) && (
<>
@@ -280,6 +290,7 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
<ComicViewer
pages={extractedComicBook}
direction="ltr"
className={{closeButton: "border: 1px solid red;"}}
/>
)}
</Modal>
@@ -306,4 +317,4 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
);
};
export default ComicDetail;
export default ComicDetail;

View File

@@ -7,7 +7,7 @@ import Card from "../Carda";
export const ComicVineDetails = (props): ReactElement => {
const { data, updatedAt } = props;
return (
<div className="column is-two-thirds">
<div className="column is-half">
<div className="comic-detail comicvine-metadata">
<dl>
<dt>ComicVine Metadata</dt>

View File

@@ -7,7 +7,7 @@ export const RawFileDetails = (props): ReactElement => {
const { rawFileDetails, inferredMetadata } = props.data;
return (
<>
<div className="comic-detail raw-file-details">
<div className="comic-detail raw-file-details column is-three-fifths">
<dl>
<dt>Raw File Details</dt>
<dd className="is-size-7">
@@ -38,7 +38,8 @@ export const RawFileDetails = (props): ReactElement => {
</dd>
</dl>
</div>
<div className="content comic-detail raw-file-details mt-3">
<div className="content comic-detail raw-file-details mt-3 column is-three-fifths">
<dl>
{/* inferred metadata */}
<dt>Inferred Issue Metadata</dt>
@@ -93,4 +94,4 @@ RawFileDetails.propTypes = {
}),
}),
}),
};
};

View File

@@ -14,7 +14,7 @@ export const ArchiveOperations = (props): ReactElement => {
(state: RootState) => state.fileOps.comicBookExtractionInProgress,
);
const extractedComicBookArchive = useSelector(
(state: RootState) => state.fileOps.extractedComicBookArchive,
(state: RootState) => state.fileOps.extractedComicBookArchive.analysis,
);
const imageAnalysisResult = useSelector((state: RootState) => {
@@ -23,7 +23,15 @@ export const ArchiveOperations = (props): ReactElement => {
const dispatch = useDispatch();
const unpackComicArchive = useCallback(() => {
dispatch(extractComicArchive(data.rawFileDetails.filePath));
dispatch(
extractComicArchive(data.rawFileDetails.filePath, {
type: "full",
purpose: "analysis",
imageResizeOptions: {
baseWidth: 275,
},
}),
);
}, []);
// sliding panel config
@@ -118,4 +126,4 @@ export const ArchiveOperations = (props): ReactElement => {
);
};
export default ArchiveOperations;
export default ArchiveOperations;

View File

@@ -11,7 +11,7 @@ import {
getComicBooks,
} from "../../actions/fileops.actions";
import { getLibraryStatistics } from "../../actions/comicinfo.actions";
import { isEmpty } from "lodash";
import { isEmpty, isNil } from "lodash";
export const Dashboard = (): ReactElement => {
const dispatch = useDispatch();
@@ -43,7 +43,7 @@ export const Dashboard = (): ReactElement => {
}, []);
const recentComics = useSelector(
(state: RootState) => state.fileOps.recentComics,
(state: RootState) => state.fileOps.recentComics
);
const wantedComics = useSelector(
(state: RootState) => state.fileOps.wantedComics,
@@ -60,7 +60,7 @@ export const Dashboard = (): ReactElement => {
<section className="section">
<h1 className="title">Dashboard</h1>
{!isEmpty(recentComics) && !isEmpty(recentComics.docs) ? (
{!isEmpty(recentComics) ? (
<>
{/* Pull List */}
<PullList issues={recentComics} />
@@ -74,9 +74,8 @@ export const Dashboard = (): ReactElement => {
<WantedComicsList comics={wantedComics} />
)}
{/* Recent imports */}
{!isEmpty(recentComics) && (
<RecentlyImported comicBookCovers={recentComics} />
)}
{/* Volumes */}
{!isEmpty(volumeGroups) && (
<VolumeGroups volumeGroups={volumeGroups} />
@@ -95,4 +94,4 @@ export const Dashboard = (): ReactElement => {
);
};
export default Dashboard;
export default Dashboard;

View File

@@ -20,7 +20,7 @@ export const PullList = ({ issues }: PullListProps): ReactElement => {
useEffect(() => {
dispatch(
getWeeklyPullList({
startDate: "2022-11-15",
startDate: "2022-12-25",
pageSize: "15",
currentPage: "1",
}),
@@ -161,4 +161,4 @@ export const PullList = ({ issues }: PullListProps): ReactElement => {
);
};
export default PullList;
export default PullList;

View File

@@ -2,7 +2,6 @@ import React, { ReactElement } from "react";
import Card from "../Carda";
import { Link } from "react-router-dom";
import ellipsize from "ellipsize";
import { escapePoundSymbol } from "../../shared/utils/formatting.utils";
import { isEmpty, isNil, isUndefined, map } from "lodash";
import { detectIssueTypes } from "../../shared/utils/tradepaperback.utils";
import Masonry from "react-masonry-css";
@@ -24,7 +23,6 @@ export const RecentlyImported = ({
700: 2,
600: 2,
};
return (
<>
<div className="content mt-5">
@@ -41,7 +39,7 @@ export const RecentlyImported = ({
columnClassName="recent-comics-column"
>
{map(
comicBookCovers.docs,
comicBookCovers,
(
{
_id,
@@ -64,7 +62,6 @@ export const RecentlyImported = ({
comicInfo,
locg,
});
const isComicBookMetadataAvailable =
!isUndefined(comicvine) &&
!isUndefined(comicvine.volumeInformation);
@@ -123,7 +120,7 @@ export const RecentlyImported = ({
</div>
</Card>
{/* metadata card */}
{!isNil(name) ? (
{!isNil(name) && (
<Card orientation="horizontal" hasDetails imageUrl={coverURL}>
<dd className="is-size-9">
<dl>
@@ -138,7 +135,7 @@ export const RecentlyImported = ({
</dl>
</dd>
</Card>
) : null}
)}
</React.Fragment>
);
},
@@ -146,4 +143,4 @@ export const RecentlyImported = ({
</Masonry>
</>
);
};
};

View File

@@ -208,14 +208,13 @@ export const Library = (): ReactElement => {
// ImportStatus.propTypes = {
// value: PropTypes.bool.isRequired,
// };
return (
<section className="container">
<div className="section">
<div className="header-area">
<h1 className="title">Library</h1>
</div>
{!isUndefined(searchResults.hits) && (
{!isEmpty(searchResults) ? (
<div>
<div className="library">
<T2Table
@@ -230,7 +229,25 @@ export const Library = (): ReactElement => {
/>
</div>
</div>
)}
): <div className="columns">
<div className="column is-two-thirds">
<article className="message is-link">
<div className="message-body">
No comics were found in the library, Elasticsearch reports no
indices. Try importing a few comics into the library and come
back.
</div>
</article>
<pre>
{!isUndefined(searchError.data) &&
JSON.stringify(
searchError.data.meta.body.error.root_cause,
null,
4,
)}
</pre>
</div>
</div> }
</div>
</section>
);

View File

@@ -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 (
<nav className="navbar is-fixed-top">
<div className="navbar-brand">
@@ -80,10 +82,23 @@ const Navbar: React.FunctionComponent = (props) => {
<div className="navbar-dropdown download-progress-meter">
<a className="navbar-item">
<DownloadProgressTick data={downloadProgressTick} />
</a>
</div>
</a> </div>
) : null}
</div>
{/* AirDC++ socket connection status */}
<div className="navbar-item has-dropdown is-hoverable">
<a className="navbar-link is-arrowless has-text-success">
{!isEmpty(airDCPPConfiguration.airDCPPState.socketConnectionInformation) ? (
<i className="fa-solid fa-bolt"></i>) : null}
</a>
<div className="navbar-dropdown download-progress-meter">
<a className="navbar-item">
<pre>{JSON.stringify(airDCPPConfiguration.airDCPPState.socketConnectionInformation, null, 2)}</pre>
</a>
</div>
</div>
<div className="navbar-item has-dropdown is-hoverable is-mega">
<div className="navbar-link flex">Blog</div>
<div id="blogDropdown" className="navbar-dropdown">

View File

@@ -81,6 +81,8 @@ export const IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_IN_PROGRESS =
export const IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_FAILED =
"IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_FAILED";
export const COMICBOOK_EXTRACTION_SUCCESS = "COMICBOOK_EXTRACTION_SUCCESS";
// Image file stats
export const IMG_ANALYSIS_CALL_IN_PROGRESS = "IMG_ANALYSIS_CALL_IN_PROGRESS";
export const IMG_ANALYSIS_DATA_FETCH_SUCCESS =
@@ -135,4 +137,4 @@ export const SETTINGS_DB_FLUSH_SUCCESS = "SETTINGS_DB_FLUSH_SUCCESS";
// Metron Metadata
export const METRON_DATA_FETCH_SUCCESS = "METRON_DATA_FETCH_SUCCESS";
export const METRON_DATA_FETCH_IN_PROGRESS = "METRON_DATA_FETCH_IN_PROGRESS";
export const METRON_DATA_FETCH_ERROR = "METRON_DATA_FETCH_ERROR";
export const METRON_DATA_FETCH_ERROR = "METRON_DATA_FETCH_ERROR";

View File

@@ -12,7 +12,7 @@ const AirDCPPSocketContextProvider = ({ children }) => {
airDCPPState: {
settings: settingsObject,
socket: {},
socketConnectionInformation: {},
socketConectionInformation: {},
},
});
};
@@ -56,11 +56,12 @@ const AirDCPPSocketContextProvider = ({ children }) => {
hostname: `${host.hostname}:${host.port}`,
});
const socketConnectionInformation = await initializedAirDCPPSocket.connect(
let socketConnectionInformation = await initializedAirDCPPSocket.connect(
`${host.username}`,
`${host.password}`,
true,
);
persistSettings({
...airDCPPState,
airDCPPState: {
@@ -80,7 +81,7 @@ const AirDCPPSocketContextProvider = ({ children }) => {
};
const AirDCPPSocketContext = createContext({
airDCPPState: {},
saveSettings: () => {},
saveSettings: () => { },
});
export { AirDCPPSocketContext, AirDCPPSocketContextProvider };

View File

@@ -29,7 +29,11 @@ import {
SS_SEARCH_FAILED,
SS_SEARCH_RESULTS_FETCHED_SPECIAL,
VOLUMES_FETCHED,
COMICBOOK_EXTRACTION_SUCCESS,
} from "../constants/action-types";
import { removeLeadingPeriod } from "../shared/utils/formatting.utils";
import { LIBRARY_SERVICE_HOST } from "../constants/endpoints";
const initialState = {
IMSCallInProgress: false,
IMGCallInProgress: false,
@@ -42,7 +46,10 @@ const initialState = {
isComicVineMetadataImportInProgress: false,
comicVineMetadataImportError: {},
rawImportError: {},
extractedComicBookArchive: [],
extractedComicBookArchive: {
reading: [],
analysis: [],
},
recentComics: [],
wantedComics: [],
libraryComics: [],
@@ -81,7 +88,7 @@ function fileOpsReducer(state = initialState, action) {
case IMS_RECENT_COMICS_FETCHED:
return {
...state,
recentComics: action.data,
recentComics: action.data.docs,
};
case IMS_WANTED_COMICS_FETCHED:
return {
@@ -131,13 +138,7 @@ function fileOpsReducer(state = initialState, action) {
comicBookExtractionInProgress: true,
};
}
case IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_SUCCESS: {
return {
...state,
extractedComicBookArchive: action.extractedComicBookArchive,
comicBookExtractionInProgress: false,
};
}
case LOCATION_CHANGE: {
return {
...state,
@@ -152,11 +153,44 @@ function fileOpsReducer(state = initialState, action) {
}
case LS_COVER_EXTRACTED: {
console.log("BASH", action);
if(state.recentComics.length === 5) {
state.recentComics.pop();
}
return {
...state,
librarySearchResultCount: state.librarySearchResultCount + 1,
recentComics: [...state.recentComics, action.result.data.importResult]
};
}
case COMICBOOK_EXTRACTION_SUCCESS: {
const comicBookPages: string[] = [];
map(action.result.files, (page) => {
const pageFilePath = removeLeadingPeriod(page);
const imagePath = encodeURI(`${LIBRARY_SERVICE_HOST}${pageFilePath}`);
comicBookPages.push(imagePath);
});
switch (action.result.purpose) {
case "reading":
return {
...state,
extractedComicBookArchive: {
reading: comicBookPages,
},
comicBookExtractionInProgress: false,
};
case "analysis":
return {
...state,
extractedComicBookArchive: {
analysis: comicBookPages,
},
comicBookExtractionInProgress: false,
};
}
}
case LS_QUEUE_DRAINED: {
console.log("drained", action);
return {
@@ -229,7 +263,7 @@ function fileOpsReducer(state = initialState, action) {
volumes: action.data,
SSCallInProgress: false,
};
case SS_SEARCH_FAILED: {
return {
...state,
@@ -249,4 +283,4 @@ function fileOpsReducer(state = initialState, action) {
}
}
export default fileOpsReducer;
export default fileOpsReducer;

View File

@@ -67,7 +67,10 @@ export const determineCoverFile = (data) => {
}
};
export const determineExternalMetadata = (metadataSource, source) => {
export const determineExternalMetadata = (
metadataSource: string,
source: any
) => {
switch (metadataSource) {
case "comicvine":
return {
@@ -87,4 +90,4 @@ export const determineExternalMetadata = (metadataSource, source) => {
default:
break;
}
};
};

102
yarn.lock
View File

@@ -1283,7 +1283,7 @@
dependencies:
tslib "^2.0.0"
"@emotion/babel-plugin@^11.3.0", "@emotion/babel-plugin@^11.7.1":
"@emotion/babel-plugin@^11.7.1":
version "11.9.2"
resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.9.2.tgz#723b6d394c89fb2ef782229d92ba95a740576e95"
integrity sha512-Pr/7HGH6H6yKgnVFNEj2MVlreu3ADqftqjqwUvDy/OJzKFgxKeTQ+eeUf20FOTuHVkDON2iNa25rAXVYtWJCjw==
@@ -1317,28 +1317,45 @@
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413"
integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==
"@emotion/is-prop-valid@^1.1.0", "@emotion/is-prop-valid@^1.1.1":
"@emotion/hash@^0.9.0":
version "0.9.0"
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.0.tgz#c5153d50401ee3c027a57a177bc269b16d889cb7"
integrity sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==
"@emotion/is-prop-valid@^1.1.0":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.1.3.tgz#f0907a416368cf8df9e410117068e20fe87c0a3a"
integrity sha512-RFg04p6C+1uO19uG8N+vqanzKqiM9eeV1LDOG3bmkYmuOj7NbKNlFC/4EZq5gnwAIlcC/jOT24f8Td0iax2SXA==
dependencies:
"@emotion/memoize" "^0.7.4"
"@emotion/is-prop-valid@^1.1.2":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz#7f2d35c97891669f7e276eb71c83376a5dc44c83"
integrity sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==
dependencies:
"@emotion/memoize" "^0.8.0"
"@emotion/memoize@^0.7.4", "@emotion/memoize@^0.7.5":
version "0.7.5"
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.5.tgz#2c40f81449a4e554e9fc6396910ed4843ec2be50"
integrity sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==
"@emotion/react@11.7.1":
version "11.7.1"
resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.7.1.tgz#3f800ce9b20317c13e77b8489ac4a0b922b2fe07"
integrity sha512-DV2Xe3yhkF1yT4uAUoJcYL1AmrnO5SVsdfvu+fBuS7IbByDeTVx9+wFmvx9Idzv7/78+9Mgx2Hcmr7Fex3tIyw==
"@emotion/memoize@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.0.tgz#f580f9beb67176fa57aae70b08ed510e1b18980f"
integrity sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==
"@emotion/react@11.9.0":
version "11.9.0"
resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.9.0.tgz#b6d42b1db3bd7511e7a7c4151dc8bc82e14593b8"
integrity sha512-lBVSF5d0ceKtfKCDQJveNAtkC7ayxpVlgOohLgXqRwqWr9bOf4TZAFFyIcNngnV6xK6X4x2ZeXq7vliHkoVkxQ==
dependencies:
"@babel/runtime" "^7.13.10"
"@emotion/babel-plugin" "^11.7.1"
"@emotion/cache" "^11.7.1"
"@emotion/serialize" "^1.0.2"
"@emotion/sheet" "^1.1.0"
"@emotion/utils" "^1.0.0"
"@emotion/serialize" "^1.0.3"
"@emotion/utils" "^1.1.0"
"@emotion/weak-memoize" "^0.2.5"
hoist-non-react-statics "^3.3.1"
@@ -1366,21 +1383,32 @@
"@emotion/utils" "^1.0.0"
csstype "^3.0.2"
"@emotion/sheet@^1.1.0", "@emotion/sheet@^1.1.1":
"@emotion/serialize@^1.0.3":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.1.tgz#0595701b1902feded8a96d293b26be3f5c1a5cf0"
integrity sha512-Zl/0LFggN7+L1liljxXdsVSVlg6E/Z/olVWpfxUTxOAmi8NU7YoeWeLfi1RmnB2TATHoaWwIBRoL+FvAJiTUQA==
dependencies:
"@emotion/hash" "^0.9.0"
"@emotion/memoize" "^0.8.0"
"@emotion/unitless" "^0.8.0"
"@emotion/utils" "^1.2.0"
csstype "^3.0.2"
"@emotion/sheet@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.1.1.tgz#015756e2a9a3c7c5f11d8ec22966a8dbfbfac787"
integrity sha512-J3YPccVRMiTZxYAY0IOq3kd+hUP8idY8Kz6B/Cyo+JuXq52Ek+zbPbSQUrVQp95aJ+lsAW7DPL1P2Z+U1jGkKA==
"@emotion/styled@11.6.0":
version "11.6.0"
resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.6.0.tgz#9230d1a7bcb2ebf83c6a579f4c80e0664132d81d"
integrity sha512-mxVtVyIOTmCAkFbwIp+nCjTXJNgcz4VWkOYQro87jE2QBTydnkiYusMrRGFtzuruiGK4dDaNORk4gH049iiQuw==
"@emotion/styled@11.8.1":
version "11.8.1"
resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.8.1.tgz#856f6f63aceef0eb783985fa2322e2bf66d04e17"
integrity sha512-OghEVAYBZMpEquHZwuelXcRjRJQOVayvbmNR0zr174NHdmMgrNkLC6TljKC5h9lZLkN5WGrdUcrKlOJ4phhoTQ==
dependencies:
"@babel/runtime" "^7.13.10"
"@emotion/babel-plugin" "^11.3.0"
"@emotion/is-prop-valid" "^1.1.1"
"@emotion/babel-plugin" "^11.7.1"
"@emotion/is-prop-valid" "^1.1.2"
"@emotion/serialize" "^1.0.2"
"@emotion/utils" "^1.0.0"
"@emotion/utils" "^1.1.0"
"@emotion/stylis@^0.8.4":
version "0.8.5"
@@ -1392,11 +1420,21 @@
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
"@emotion/unitless@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.0.tgz#a4a36e9cbdc6903737cd20d38033241e1b8833db"
integrity sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==
"@emotion/utils@^1.0.0", "@emotion/utils@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.1.0.tgz#86b0b297f3f1a0f2bdb08eeac9a2f49afd40d0cf"
integrity sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ==
"@emotion/utils@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.0.tgz#9716eaccbc6b5ded2ea5a90d65562609aab0f561"
integrity sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==
"@emotion/weak-memoize@^0.2.5":
version "0.2.5"
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46"
@@ -7642,9 +7680,9 @@ decimal.js@^10.2.1:
integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==
decode-uri-component@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
integrity sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==
version "0.2.2"
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9"
integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==
decompress-response@^3.2.0, decompress-response@^3.3.0:
version "3.3.0"
@@ -15281,20 +15319,20 @@ react-collapsible@^2.9.0:
resolved "https://registry.yarnpkg.com/react-collapsible/-/react-collapsible-2.10.0.tgz#57330f9f4f968a41ece49c651b56cf30f9a06d19"
integrity sha512-kEVsmlFfXBMTCnU5gwIv19MdmPAhbIPzz5Er37TiJSzRKS0IHrqAKQyQeHEmtoGIQMTcVI46FzE4z3NlVTx77A==
react-comic-viewer@^0.3.5:
version "0.3.5"
resolved "https://registry.yarnpkg.com/react-comic-viewer/-/react-comic-viewer-0.3.5.tgz#ab5f9ba74a4aa2e748c29d11b43e40d2a042d9ba"
integrity sha512-xMjvu/JVJ821dYWOuOiZlEEMpgdmUeUtznoRjBlMaFi6WJI52V55/6E+ICg8VqROwiMwmWjp65rGhF1P8hlRLg==
react-comic-viewer@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/react-comic-viewer/-/react-comic-viewer-0.4.0.tgz#81e170c4d3924f3312171e3fc7d5d4485466c301"
integrity sha512-tdse0Vn6cJzEbFUZMlEBcY1LrH+LfCKodJInl5Y5lyIZyKARoejLrQNlJPnarvNcPW0qm6yQkYioaI56pq0U1w==
dependencies:
"@emotion/react" "11.7.1"
"@emotion/styled" "11.6.0"
"@emotion/react" "11.9.0"
"@emotion/styled" "11.8.1"
"@react-hook/window-size" "^3.0.7"
"@rooks/use-did-update" "^4.10.0"
"@rooks/use-outside-click-ref" "4.11.2"
nanoid "^3.1.25"
react-full-screen "1.1.0"
react-icons "4.3.1"
react-swipeable "6.2.0"
react-swipeable "6.2.2"
screenfull "^5.1.0"
react-day-picker@^8.0.6:
@@ -15534,10 +15572,10 @@ react-stickynode@^4.0.0:
shallowequal "^1.0.0"
subscribe-ui-event "^2.0.6"
react-swipeable@6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/react-swipeable/-/react-swipeable-6.2.0.tgz#057271cb7a6fb4af9d2a3f6d80ccdf33e2f64d47"
integrity sha512-nWQ8dEM8e/uswZLSIkXUsAnQmnX4MTcryOHBQIQYRMJFDpgDBSiVbKsz/BZVCIScF4NtJh16oyxwaNOepR6xSw==
react-swipeable@6.2.2:
version "6.2.2"
resolved "https://registry.yarnpkg.com/react-swipeable/-/react-swipeable-6.2.2.tgz#52ba570f3a7a90db7093094ec476f3d151f727d1"
integrity sha512-Oz7nSFrssvq2yvy05aNL3F+yBUqSvLsK6x1mu+rQFOpMdQVnt4izKt1vyjvvTb70q6GQOaSpaB6qniROW2MAzQ==
react-syntax-highlighter@^15.4.5:
version "15.5.0"
@@ -15569,7 +15607,7 @@ react-transition-group@4.4.2, react-transition-group@^4.3.0:
loose-envify "^1.4.0"
prop-types "^15.6.2"
react@^18.1.0:
react@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==