diff --git a/src/client/actions/airdcpp.actions.tsx b/src/client/actions/airdcpp.actions.tsx index 74550fb..658aa82 100644 --- a/src/client/actions/airdcpp.actions.tsx +++ b/src/client/actions/airdcpp.actions.tsx @@ -33,9 +33,9 @@ interface SearchData { priority: PriorityEnum; } -function sleep(ms: number): Promise { +export const sleep = (ms: number): Promise => { return new Promise((resolve) => setTimeout(resolve, ms)); -} +}; export const toggleAirDCPPSocketConnectionStatus = (status: String, payload?: any) => async (dispatch) => { @@ -59,78 +59,6 @@ export const toggleAirDCPPSocketConnectionStatus = break; } }; -export const search = async (data: SearchData, ADCPPSocket: any) => { - 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, diff --git a/src/client/assets/scss/App.scss b/src/client/assets/scss/App.scss index 97e0634..0833103 100644 --- a/src/client/assets/scss/App.scss +++ b/src/client/assets/scss/App.scss @@ -13,7 +13,9 @@ $size-9: 0.7rem; $flexSize: 4em; $boxSpacing: 1em; $colorText: #404646; - +body { + background: #fffcc; +} .is-size-8 { font-size: $size-8; } @@ -422,7 +424,7 @@ pre { width: 100%; padding: 25px 0 15px 0; position: sticky; - z-index:9999; + z-index: 9999; background: #fffffc; top: 50px; } diff --git a/src/client/components/ComicDetail/AcquisitionPanel.tsx b/src/client/components/ComicDetail/AcquisitionPanel.tsx index 9913318..0e3752e 100644 --- a/src/client/components/ComicDetail/AcquisitionPanel.tsx +++ b/src/client/components/ComicDetail/AcquisitionPanel.tsx @@ -1,18 +1,14 @@ -import React, { - useCallback, - useContext, - ReactElement, - useEffect, - useState, -} from "react"; +import React, { useCallback, ReactElement, useEffect, useState } from "react"; import { - search, downloadAirDCPPItem, getBundlesForComic, + sleep, } from "../../actions/airdcpp.actions"; +import { SearchQuery, PriorityEnum, SearchResponse } from "threetwo-ui-typings"; import { RootState, SearchInstance } from "threetwo-ui-typings"; import ellipsize from "ellipsize"; import { Form, Field } from "react-final-form"; +import { difference } from "../../shared/utils/object.utils"; import { isEmpty, isNil, map } from "lodash"; import { useStore } from "../../store"; import { useShallow } from "zustand/react/shallow"; @@ -48,7 +44,6 @@ export const AcquisitionPanel = ( queryFn: async () => await airDCPPSocketInstance.get(`hubs`), }); - console.log("narangi umlaut", hubs); const issueName = props.query.issue.name || ""; // const { settings } = props; const sanitizedIssueName = issueName.replace(/[^a-zA-Z0-9 ]/g, " "); @@ -69,8 +64,13 @@ export const AcquisitionPanel = ( // const settings = useSelector((state: RootState) => state.settings.data); // const airDCPPConfiguration = useContext(AirDCPPSocketContext); - + interface SearchData { + query: Pick & Partial>; + hub_urls: string[] | undefined | null; + priority: PriorityEnum; + } const [dcppQuery, setDcppQuery] = useState({}); + const [airDCPPSearchResults, setAirDCPPSearchResults] = useState([]); // Construct a AirDC++ query based on metadata inferred, upon component mount // Pre-populate the search input with the search string, so that @@ -88,6 +88,82 @@ export const AcquisitionPanel = ( setDcppQuery(dcppSearchQuery); }, []); + const search = async (data: SearchData, ADCPPSocket: any) => { + 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) + setAirDCPPSearchResults((state) => [...state, 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 + const bundleToUpdateIndex = airDCPPSearchResults?.findIndex( + (bundle) => bundle.result.id === groupedResult.result.id, + ); + const updatedState = [...airDCPPSearchResults]; + if ( + !isNil(difference(updatedState[bundleToUpdateIndex], groupedResult)) + ) { + updatedState[bundleToUpdateIndex] = groupedResult; + } + setAirDCPPSearchResults((state) => [...state, ...updatedState]); + }, + 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; + } + }; const getDCPPSearchResults = async (searchQuery) => { const manualQuery = { query: { @@ -133,6 +209,7 @@ export const AcquisitionPanel = ( }, [], ); + console.log("yaman", airDCPPSearchResults); return ( <>
@@ -199,6 +276,106 @@ export const AcquisitionPanel = (
)} + + {/* AirDC++ results */} +
+ {!isNil(airDCPPSearchResults) && !isEmpty(airDCPPSearchResults) ? ( +
+ + + + + + + + + + + {map(airDCPPSearchResults, ({ result }, idx) => { + return ( + + + + + + + ); + })} + +
NameTypeSlotsActions
+

+ {result.type.id === "directory" ? ( + + ) : null}{" "} + {ellipsize(result.name, 70)} +

+ +
+
+
+ {!isNil(result.dupe) ? ( + Dupe + ) : null} + + {result.users.user.nicks} + + {result.users.user.flags.map((flag, idx) => ( + + {flag} + + ))} +
+
+
+
+ + {result.type.id === "directory" + ? "directory" + : result.type.str} + + +
+ + {result.slots.free} free + + + {result.slots.total} + +
+
+ + downloadDCPPResult( + searchInstance.id, + result.id, + result.name, + result.size, + result.type, + ) + } + > + + +
+
+ ) : ( +
+
+
+ Searching via AirDC++ is still in{" "} + alpha. Some searches may take arbitrarily long, + or may not work at all. Searches from ADCS hubs are + more reliable than NMDCS ones. +
+
+
+ )} +
); }; diff --git a/src/client/components/Settings/AirDCPPSettings/AirDCPPHubsForm.tsx b/src/client/components/Settings/AirDCPPSettings/AirDCPPHubsForm.tsx index 6233832..c6efbb5 100644 --- a/src/client/components/Settings/AirDCPPSettings/AirDCPPHubsForm.tsx +++ b/src/client/components/Settings/AirDCPPSettings/AirDCPPHubsForm.tsx @@ -67,7 +67,6 @@ export const AirDCPPHubsForm = (): ReactElement => { return