🏗 Refactoring AirDCPPSocket init and download handling
This commit is contained in:
@@ -265,7 +265,6 @@ export const getTransfers =
|
||||
true,
|
||||
);
|
||||
}
|
||||
console.log(ADCPPSocket);
|
||||
const foo = await ADCPPSocket.get("queue/bundles/1/50", {});
|
||||
console.log(foo);
|
||||
} catch (err) {
|
||||
|
||||
@@ -58,6 +58,11 @@ pre {
|
||||
|
||||
.navbar {
|
||||
border-bottom: 1px solid #f2f1f9;
|
||||
.download-progress-meter {
|
||||
margin-left: -300px;
|
||||
min-width: 500px;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-item.is-mega {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { ReactElement, useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import React, { ReactElement, useContext, useEffect, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import Dashboard from "./Dashboard/Dashboard";
|
||||
|
||||
import Import from "./Import";
|
||||
@@ -18,7 +18,11 @@ import { Routes, Route } from "react-router-dom";
|
||||
import Navbar from "./Navbar";
|
||||
import "../assets/scss/App.scss";
|
||||
import Notifications from "react-notification-system-redux";
|
||||
import { AirDCPPSocketContext } from "../context/AirDCPPSocket";
|
||||
import {
|
||||
AirDCPPSocketContextProvider,
|
||||
AirDCPPSocketContext,
|
||||
} from "../context/AirDCPPSocket";
|
||||
import { isNil } from "lodash";
|
||||
|
||||
//Optional styling
|
||||
const style = {
|
||||
@@ -66,10 +70,26 @@ const style = {
|
||||
|
||||
export const App = (): ReactElement => {
|
||||
const notifications = useSelector((state: RootState) => state.notifications);
|
||||
const [ADCPPSocket, setADCPPSocket] = useState({});
|
||||
|
||||
const airDCPPConfiguration = useContext(AirDCPPSocketContext);
|
||||
const { AirDCPPSocket } = airDCPPConfiguration;
|
||||
useEffect(() => {
|
||||
const addQueueListener = async () => {
|
||||
if (!isNil(AirDCPPSocket)) {
|
||||
await AirDCPPSocket.addListener(
|
||||
"queue",
|
||||
"queue_bundle_added",
|
||||
async (data) => console.log("JEMEN:", data),
|
||||
);
|
||||
console.log(
|
||||
"[AirDCPP]: Listener registered - listening to queue bundle changes",
|
||||
);
|
||||
}
|
||||
};
|
||||
addQueueListener();
|
||||
}, [AirDCPPSocket]);
|
||||
return (
|
||||
<AirDCPPSocketContext.Provider value={{ ADCPPSocket, setADCPPSocket }}>
|
||||
<AirDCPPSocketContextProvider>
|
||||
<div>
|
||||
<Navbar />
|
||||
<Notifications
|
||||
@@ -83,7 +103,7 @@ export const App = (): ReactElement => {
|
||||
<Route path="/import" element={<Import path={"./comics"} />} />
|
||||
<Route path="/library" element={<LibraryContainer />} />
|
||||
<Route path="/library-grid" element={<LibraryGrid />} />
|
||||
<Route path="/downloads" element={<Downloads />} />
|
||||
<Route path="/downloads" element={<Downloads data={{}} />} />
|
||||
<Route path="/search" element={<Search />} />
|
||||
<Route
|
||||
path={"/comic/details/:comicObjectId"}
|
||||
@@ -99,7 +119,7 @@ export const App = (): ReactElement => {
|
||||
<Route path="/volumes/all" element={<Volumes />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</AirDCPPSocketContext.Provider>
|
||||
</AirDCPPSocketContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -20,14 +20,14 @@ interface IAcquisitionPanelProps {
|
||||
query: any;
|
||||
comicObjectid: any;
|
||||
comicObject: any;
|
||||
userSettings: any;
|
||||
settings: any;
|
||||
}
|
||||
|
||||
export const AcquisitionPanel = (
|
||||
props: IAcquisitionPanelProps,
|
||||
): ReactElement => {
|
||||
const issueName = props.query.issue.name || "";
|
||||
const { userSettings } = props;
|
||||
// const { settings } = props;
|
||||
const sanitizedIssueName = issueName.replace(/[^a-zA-Z0-9 ]/g, " ");
|
||||
|
||||
// Selectors for picking state
|
||||
@@ -44,23 +44,21 @@ export const AcquisitionPanel = (
|
||||
(state: RootState) => state.airdcpp.searchInstance,
|
||||
);
|
||||
|
||||
// const userSettings = useSelector((state: RootState) => state.settings.data);
|
||||
const { ADCPPSocket } = useContext(AirDCPPSocketContext);
|
||||
// const settings = useSelector((state: RootState) => state.settings.data);
|
||||
const airDCPPConfiguration = useContext(AirDCPPSocketContext);
|
||||
const { AirDCPPSocket, settings } = airDCPPConfiguration;
|
||||
const dispatch = useDispatch();
|
||||
const [dcppQuery, setDcppQuery] = useState({});
|
||||
console.log(ADCPPSocket);
|
||||
console.log(airDCPPConfiguration)
|
||||
useEffect(() => {
|
||||
if (!isNil(userSettings.directConnect)) {
|
||||
if (!isNil(settings)) {
|
||||
// AirDC++ search query
|
||||
const dcppSearchQuery = {
|
||||
query: {
|
||||
pattern: `${sanitizedIssueName.replace(/#/g, "")}`,
|
||||
extensions: ["cbz", "cbr", "cb7"],
|
||||
},
|
||||
hub_urls: map(
|
||||
userSettings.directConnect.client.hubs,
|
||||
(item) => item.value,
|
||||
),
|
||||
hub_urls: map(settings.directConnect.client.hubs, (item) => item.value),
|
||||
priority: 5,
|
||||
};
|
||||
setDcppQuery(dcppSearchQuery);
|
||||
@@ -74,20 +72,17 @@ export const AcquisitionPanel = (
|
||||
pattern: `${searchQuery.issueName}`,
|
||||
extensions: ["cbz", "cbr", "cb7"],
|
||||
},
|
||||
hub_urls: map(
|
||||
userSettings.directConnect.client.hubs,
|
||||
(item) => item.value,
|
||||
),
|
||||
hub_urls: map(settings.directConnect.client.hubs, (item) => item.value),
|
||||
priority: 5,
|
||||
};
|
||||
dispatch(
|
||||
search(manualQuery, ADCPPSocket, {
|
||||
username: `${userSettings.directConnect.client.host.username}`,
|
||||
password: `${userSettings.directConnect.client.host.password}`,
|
||||
search(manualQuery, AirDCPPSocket, {
|
||||
username: `${settings.directConnect.client.host.username}`,
|
||||
password: `${settings.directConnect.client.host.password}`,
|
||||
}),
|
||||
);
|
||||
},
|
||||
[dispatch, ADCPPSocket],
|
||||
[dispatch, AirDCPPSocket],
|
||||
);
|
||||
|
||||
// download via AirDC++
|
||||
@@ -99,18 +94,18 @@ export const AcquisitionPanel = (
|
||||
resultId,
|
||||
comicBookObjectId,
|
||||
comicObject,
|
||||
ADCPPSocket,
|
||||
AirDCPPSocket,
|
||||
{
|
||||
username: `${userSettings.directConnect.client.host.username}`,
|
||||
password: `${userSettings.directConnect.client.host.password}`,
|
||||
username: `${settings.directConnect.client.host.username}`,
|
||||
password: `${settings.directConnect.client.host.password}`,
|
||||
},
|
||||
),
|
||||
);
|
||||
// this is to update the download count badge on the downloads tab
|
||||
dispatch(
|
||||
getBundlesForComic(comicBookObjectId, ADCPPSocket, {
|
||||
username: `${userSettings.directConnect.client.host.username}`,
|
||||
password: `${userSettings.directConnect.client.host.password}`,
|
||||
getBundlesForComic(comicBookObjectId, AirDCPPSocket, {
|
||||
username: `${settings.directConnect.client.host.username}`,
|
||||
password: `${settings.directConnect.client.host.password}`,
|
||||
}),
|
||||
);
|
||||
},
|
||||
@@ -119,7 +114,7 @@ export const AcquisitionPanel = (
|
||||
return (
|
||||
<>
|
||||
<div className="comic-detail columns">
|
||||
{!isEmpty(ADCPPSocket) ? (
|
||||
{!isEmpty(AirDCPPSocket) ? (
|
||||
<Form
|
||||
onSubmit={getDCPPSearchResults}
|
||||
initialValues={{
|
||||
@@ -190,13 +185,11 @@ export const AcquisitionPanel = (
|
||||
<dl>
|
||||
<dt>
|
||||
<div className="tags mb-1">
|
||||
{userSettings.directConnect.client.hubs.map(
|
||||
({ value }) => (
|
||||
<span className="tag is-warning" key={value}>
|
||||
{value}
|
||||
</span>
|
||||
),
|
||||
)}
|
||||
{settings.directConnect.client.hubs.map(({ value }) => (
|
||||
<span className="tag is-warning" key={value}>
|
||||
{value}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</dt>
|
||||
<dt>
|
||||
|
||||
@@ -5,38 +5,18 @@ import { useParams } from "react-router-dom";
|
||||
import { getComicBookDetailById } from "../../actions/comicinfo.actions";
|
||||
import { ComicDetail } from "../ComicDetail/ComicDetail";
|
||||
|
||||
import { getSettings } from "../../actions/settings.actions";
|
||||
import { AirDCPPSocketContext } from "../../context/AirDCPPSocket";
|
||||
import AirDCPPSocket from "../../services/DcppSearchService";
|
||||
|
||||
|
||||
export const ComicDetailContainer = (): ReactElement | null => {
|
||||
const comicBookDetailData = useSelector(
|
||||
(state: RootState) => state.comicInfo.comicBookDetail,
|
||||
);
|
||||
const dispatch = useDispatch();
|
||||
useEffect(() => {
|
||||
dispatch(getSettings());
|
||||
}, [dispatch]);
|
||||
const userSettings = useSelector((state: RootState) => state.settings.data);
|
||||
const { ADCPPSocket, setADCPPSocket } = useContext(AirDCPPSocketContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (isEmpty(ADCPPSocket) && !isNil(userSettings.directConnect)) {
|
||||
setADCPPSocket(
|
||||
new AirDCPPSocket({
|
||||
protocol: `${userSettings.directConnect.client.host.protocol}`,
|
||||
hostname: `${userSettings.directConnect.client.host.hostname}`,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}, [userSettings]);
|
||||
const { comicObjectId } = useParams<{ comicObjectId: string }>();
|
||||
useEffect(() => {
|
||||
dispatch(getComicBookDetailById(comicObjectId));
|
||||
// dispatch(getSettings());
|
||||
}, [dispatch]);
|
||||
return !isEmpty(comicBookDetailData) ? (
|
||||
<ComicDetail data={comicBookDetailData} userSettings={userSettings} />
|
||||
<ComicDetail data={comicBookDetailData} />
|
||||
) : null;
|
||||
};
|
||||
|
||||
35
src/client/components/ComicDetail/DownloadProgressTick.tsx
Normal file
35
src/client/components/ComicDetail/DownloadProgressTick.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import prettyBytes from "pretty-bytes";
|
||||
import React, { ReactElement } from "react";
|
||||
|
||||
export const DownloadProgressTick = (props): ReactElement => {
|
||||
return (
|
||||
<div className="box">
|
||||
<h4 className="is-size-6">{props.data.name}</h4>
|
||||
<div>
|
||||
<span className="is-size-3 has-text-weight-semibold">
|
||||
{prettyBytes(props.data.downloaded_bytes)} of{" "}
|
||||
{prettyBytes(props.data.size)}{" "}
|
||||
</span>
|
||||
<progress
|
||||
className="progress is-small is-success"
|
||||
value={props.data.downloaded_bytes}
|
||||
max={props.data.size}
|
||||
>
|
||||
{(parseInt(props.data.downloaded_bytes) / parseInt(props.data.size)) *
|
||||
100}
|
||||
%
|
||||
</progress>
|
||||
</div>
|
||||
<div className="is-size-5">
|
||||
{prettyBytes(props.data.speed)} per second.
|
||||
</div>
|
||||
<div className="is-size-5">
|
||||
Time left:
|
||||
{Math.round(parseInt(props.data.seconds_left) / 60)}
|
||||
</div>
|
||||
<div>{props.data.target}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DownloadProgressTick;
|
||||
@@ -19,9 +19,6 @@ interface IDownloadsPanelProps {
|
||||
export const DownloadsPanel = (
|
||||
props: IDownloadsPanelProps,
|
||||
): ReactElement | null => {
|
||||
const downloadProgressTick = useSelector(
|
||||
(state: RootState) => state.airdcpp.downloadProgressData,
|
||||
);
|
||||
const bundles = useSelector((state: RootState) => {
|
||||
return state.airdcpp.bundles;
|
||||
});
|
||||
@@ -41,58 +38,12 @@ export const DownloadsPanel = (
|
||||
password: `${userSettings.directConnect.client.host.password}`,
|
||||
}),
|
||||
);
|
||||
dispatch(
|
||||
getDownloadProgress(props.comicObjectId, ADCPPSocket, {
|
||||
username: `${userSettings.directConnect.client.host.username}`,
|
||||
password: `${userSettings.directConnect.client.host.password}`,
|
||||
}),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}, [dispatch]);
|
||||
|
||||
const ProgressTick = (props) => {
|
||||
return (
|
||||
<div className="column is-half">
|
||||
{JSON.stringify(props.data.downloadProgressTick)}
|
||||
|
||||
<div className="card">
|
||||
<div className="card-content is-size-7">
|
||||
<dl>
|
||||
<dt className="is-size-6">{props.data.name}</dt>
|
||||
<dd>
|
||||
<span className="is-size-3 has-text-weight-semibold">
|
||||
{prettyBytes(props.data.downloaded_bytes)} of{" "}
|
||||
{prettyBytes(props.data.size)}{" "}
|
||||
</span>
|
||||
<progress
|
||||
className="progress is-small is-success"
|
||||
value={props.data.downloaded_bytes}
|
||||
max={props.data.size}
|
||||
>
|
||||
{(parseInt(props.data.downloaded_bytes) /
|
||||
parseInt(props.data.size)) *
|
||||
100}
|
||||
%
|
||||
</progress>
|
||||
</dd>
|
||||
<dd className="is-size-5">
|
||||
{prettyBytes(props.data.speed)} per second.
|
||||
</dd>
|
||||
<dd className="is-size-5">
|
||||
Time left:
|
||||
{Math.round(parseInt(props.data.seconds_left) / 60)}
|
||||
</dd>
|
||||
<dd>{props.data.target}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Bundles = (props) => {
|
||||
return !isEmpty(props.data) ? (
|
||||
<div className="column is-full">
|
||||
@@ -130,9 +81,6 @@ export const DownloadsPanel = (
|
||||
return !isNil(props.data) ? (
|
||||
<>
|
||||
<div className="columns is-multiline">
|
||||
{!isNil(downloadProgressTick) ? (
|
||||
<ProgressTick data={downloadProgressTick} />
|
||||
) : null}
|
||||
{!isEmpty(ADCPPSocket) ? (
|
||||
<Bundles data={bundles} />
|
||||
) : (
|
||||
|
||||
@@ -9,7 +9,7 @@ export const LibraryStatistics = (
|
||||
return (
|
||||
<div className="mt-5">
|
||||
<h4 className="title is-4">
|
||||
<i className="fa-solid fa-chart-simple"></i> Statistics
|
||||
<i className="fa-solid fa-chart-simple"></i> Your Library In Numbers
|
||||
</h4>
|
||||
<p className="subtitle is-7">A brief snapshot of your library.</p>
|
||||
<div className="columns is-multiline">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from "react";
|
||||
import { SearchBar } from "./GlobalSearchBar/SearchBar";
|
||||
import { DownloadProgressTick } from "./ComicDetail/DownloadProgressTick";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
const Navbar: React.FunctionComponent = (props) => {
|
||||
@@ -62,15 +63,23 @@ const Navbar: React.FunctionComponent = (props) => {
|
||||
<div className="navbar-end">
|
||||
<a className="navbar-item is-hidden-desktop-only"></a>
|
||||
|
||||
<div className="navbar-item has-dropdown is-hoverable">
|
||||
<a
|
||||
className="navbar-link is-active"
|
||||
href="/documentation/overview/start/"
|
||||
>
|
||||
{/* <div className="navbar-item has-dropdown is-hoverable">
|
||||
<a className="navbar-link is-arrowless">
|
||||
<i className="fa-solid fa-download"></i>
|
||||
{!isUndefined(downloadProgressTick) ? (
|
||||
<>
|
||||
<i className="fa-solid fa-circle-dashed"></i>
|
||||
</>
|
||||
) : null}
|
||||
</a>
|
||||
<div className="navbar-dropdown">asdas</div>
|
||||
</div>
|
||||
{!isUndefined(downloadProgressTick) ? (
|
||||
<div className="navbar-dropdown download-progress-meter">
|
||||
<a className="navbar-item">
|
||||
<DownloadProgressTick data={downloadProgressTick} />
|
||||
</a>
|
||||
</div>
|
||||
) : null}
|
||||
</div> */}
|
||||
<div className="navbar-item has-dropdown is-hoverable is-mega">
|
||||
<div className="navbar-link flex">Blog</div>
|
||||
<div id="blogDropdown" className="navbar-dropdown">
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
const AirDCPPSocketContext = React.createContext(null);
|
||||
|
||||
export { AirDCPPSocketContext };
|
||||
45
src/client/context/AirDCPPSocket.tsx
Normal file
45
src/client/context/AirDCPPSocket.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import axios from "axios";
|
||||
import React, { createContext, useEffect, useState } from "react";
|
||||
import { SETTINGS_SERVICE_BASE_URI } from "../constants/endpoints";
|
||||
import AirDCPPSocket from "../services/DcppSearchService";
|
||||
|
||||
const AirDCPPSocketContext = createContext({});
|
||||
const AirDCPPSocketContextProvider = ({ children }) => {
|
||||
const [airDCPPConfiguration, setValue] = useState({});
|
||||
useEffect(() => {
|
||||
const initializeAirDCPPSocket = () => {
|
||||
axios({
|
||||
url: `${SETTINGS_SERVICE_BASE_URI}/getSettings`,
|
||||
method: "POST",
|
||||
data: "",
|
||||
}).then(async (data) => {
|
||||
const { directConnect } = data.data;
|
||||
const initializedAirDCPPSocket = new AirDCPPSocket({
|
||||
protocol: `${directConnect.client.host.protocol}`,
|
||||
hostname: `${directConnect.client.host.hostname}`,
|
||||
});
|
||||
await initializedAirDCPPSocket.connect(
|
||||
`${directConnect.client.host.username}`,
|
||||
`${directConnect.client.host.password}`,
|
||||
true,
|
||||
);
|
||||
setValue({
|
||||
AirDCPPSocket: initializedAirDCPPSocket,
|
||||
settings: data.data,
|
||||
});
|
||||
});
|
||||
};
|
||||
initializeAirDCPPSocket();
|
||||
}, []);
|
||||
|
||||
// the Provider gives access to the context to its children
|
||||
|
||||
console.log(airDCPPConfiguration);
|
||||
return (
|
||||
<AirDCPPSocketContext.Provider value={airDCPPConfiguration}>
|
||||
{children}
|
||||
</AirDCPPSocketContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export { AirDCPPSocketContext, AirDCPPSocketContextProvider };
|
||||
0
src/client/context/SettingsContext.tsx
Normal file
0
src/client/context/SettingsContext.tsx
Normal file
Reference in New Issue
Block a user