From 8dacff7850993e843a354cf1b783c1ceb02fc9db Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Wed, 17 Apr 2024 21:14:07 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7=20Refactoring=20DC++=20search/down?= =?UTF-8?q?load?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ComicDetail/AcquisitionPanel.tsx | 216 +++++++----------- 1 file changed, 87 insertions(+), 129 deletions(-) diff --git a/src/client/components/ComicDetail/AcquisitionPanel.tsx b/src/client/components/ComicDetail/AcquisitionPanel.tsx index d8fd82c..34b772a 100644 --- a/src/client/components/ComicDetail/AcquisitionPanel.tsx +++ b/src/client/components/ComicDetail/AcquisitionPanel.tsx @@ -21,17 +21,10 @@ interface IAcquisitionPanelProps { export const AcquisitionPanel = ( props: IAcquisitionPanelProps, ): ReactElement => { - const { - airDCPPSocketInstance, - airDCPPClientConfiguration, - airDCPPSessionInformation, - airDCPPDownloadTick, - } = useStore( + const { airDCPPSocketInstance, socketIOInstance } = useStore( useShallow((state) => ({ airDCPPSocketInstance: state.airDCPPSocketInstance, - airDCPPClientConfiguration: state.airDCPPClientConfiguration, - airDCPPSessionInformation: state.airDCPPSessionInformation, - airDCPPDownloadTick: state.airDCPPDownloadTick, + socketIOInstance: state.socketIOInstance, })), ); @@ -40,7 +33,18 @@ export const AcquisitionPanel = ( hub_urls: string[] | undefined | null; priority: PriorityEnum; } + interface SearchResult { + result: { + id: number; + }; + search_id: number; + // Add other properties as needed + } + const handleSearch = (searchQuery) => { + // Use the already connected socket instance to emit events + socketIOInstance.emit("initiateSearch", searchQuery); + }; /** * Get the hubs list from an AirDCPP Socket */ @@ -53,7 +57,9 @@ export const AcquisitionPanel = ( const sanitizedIssueName = issueName.replace(/[^a-zA-Z0-9 ]/g, " "); const [dcppQuery, setDcppQuery] = useState({}); - const [airDCPPSearchResults, setAirDCPPSearchResults] = useState([]); + const [airDCPPSearchResults, setAirDCPPSearchResults] = useState< + SearchResult[] + >([]); const [airDCPPSearchStatus, setAirDCPPSearchStatus] = useState(false); const [airDCPPSearchInstance, setAirDCPPSearchInstance] = useState({}); const [airDCPPSearchInfo, setAirDCPPSearchInfo] = useState({}); @@ -61,7 +67,7 @@ export const AcquisitionPanel = ( // Construct a AirDC++ query based on metadata inferred, upon component mount // Pre-populate the search input with the search string, so that - // All the user has to do is hit "Search AirDC++" + // all the user has to do is hit "Search AirDC++" to perform a search useEffect(() => { // AirDC++ search query const dcppSearchQuery = { @@ -80,80 +86,55 @@ export const AcquisitionPanel = ( * @param {SearchData} data - a SearchData query * @param {any} ADCPPSocket - an intialized AirDC++ socket instance */ - const search = async (data: SearchData, ADCPPSocket: any) => { - try { - if (!ADCPPSocket.isConnected()) { - await ADCPPSocket(); - } - const instance: SearchInstance = await ADCPPSocket.post("search"); - setAirDCPPSearchStatus(true); - - // 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]); + const search = async (searchData: any) => { + setAirDCPPSearchResults([]); + socketIOInstance.emit( + "call", + "socket.search", + { + query: searchData, + config: { + protocol: `ws`, + hostname: `localhost:5600`, + username: `user`, + password: `pass`, }, - 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}`, - ); - setAirDCPPSearchInstance(currentInstance); - setAirDCPPSearchInfo(searchInfo); - 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 - setAirDCPPSearchInstance(instance); - setAirDCPPSearchStatus(false); - }, - instance.id, - ); - // Finally, perform the actual search - await ADCPPSocket.post(`search/${instance.id}/hub_search`, data); - } catch (error) { - console.log(error); - throw error; - } + }, + (data: any) => console.log(data), + ); }; + socketIOInstance.on("searchResultAdded", (data: SearchResult) => { + setAirDCPPSearchResults((previousState) => { + const exists = previousState.some( + (item) => data.result.id === item.result.id, + ); + if (!exists) { + return [...previousState, data]; + } + return previousState; + }); + }); + + socketIOInstance.on("searchResultUpdated", (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]); + }); + + socketIOInstance.on("searchInitiated", (data) => { + setAirDCPPSearchInstance(data.instance); + }); + socketIOInstance.on("searchesSent", (data) => { + setAirDCPPSearchInfo(data.searchInfo); + }); + /** * Method to download a bundle associated with a search result from AirDC++ * @param {Number} searchInstanceId - description @@ -162,7 +143,7 @@ export const AcquisitionPanel = ( * @param {String} name - description * @param {Number} size - description * @param {any} type - description - * @param {any} ADCPPSocket - description + * @param {any} config - description * @returns {void} - description */ const download = async ( @@ -172,50 +153,22 @@ export const AcquisitionPanel = ( name: String, size: Number, type: any, - ADCPPSocket: any, + config: any, ): void => { - try { - if (!ADCPPSocket.isConnected()) { - await ADCPPSocket.connect(); - } - let bundleDBImportResult = {}; - const downloadResult = await ADCPPSocket.post( - `search/${searchInstanceId}/results/${resultId}/download`, - ); - - if (!isNil(downloadResult)) { - bundleDBImportResult = await axios({ - method: "POST", - url: `http://localhost:3000/api/library/applyAirDCPPDownloadMetadata`, - headers: { - "Content-Type": "application/json; charset=utf-8", - }, - data: { - bundleId: downloadResult.bundle_info.id, - comicObjectId, - name, - size, - type, - }, - }); - console.log(bundleDBImportResult?.data); - queryClient.invalidateQueries({ queryKey: ["comicBookMetadata"] }); - - // 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; - } + socketIOInstance.emit( + "call", + "socket.download", + { + searchInstanceId, + resultId, + comicObjectId, + name, + size, + type, + config, + }, + (data: any) => console.log(data), + ); }; const getDCPPSearchResults = async (searchQuery) => { const manualQuery = { @@ -227,7 +180,7 @@ export const AcquisitionPanel = ( priority: 5, }; - search(manualQuery, airDCPPSocketInstance); + search(manualQuery); }; return ( @@ -358,7 +311,7 @@ export const AcquisitionPanel = ( - {map(airDCPPSearchResults, ({ result }, idx) => { + {map(airDCPPSearchResults, ({ result, search_id }, idx) => { return (