diff --git a/src/modules/global-search/screens/SearchAll.tsx b/src/modules/global-search/screens/SearchAll.tsx index 647a1f8b..1c8a5ba9 100644 --- a/src/modules/global-search/screens/SearchAll.tsx +++ b/src/modules/global-search/screens/SearchAll.tsx @@ -9,7 +9,7 @@ import Card from '@mui/material/Card'; import CardActionArea from '@mui/material/CardActionArea'; import Typography from '@mui/material/Typography'; -import React, { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useState } from 'react'; +import React, { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { Link, useLocation } from 'react-router-dom'; import { StringParam, useQueryParam } from 'use-query-params'; import { useTranslation } from 'react-i18next'; @@ -110,12 +110,24 @@ const SourceSearchPreview = React.memo( const { t } = useTranslation(); const { id, displayName, lang } = source; + + const currentSearchString = useRef(searchString); + const currentAbortRequest = useRef<(reason: any) => void>(() => {}); + + const didSearchChange = currentSearchString.current !== searchString; + if (didSearchChange) { + currentSearchString.current = searchString; + currentAbortRequest.current(new Error(`SourceSearchPreview(${id}, ${displayName}): search string changed`)); + } + const [refetch, results] = requestManager.useSourceSearch(id, searchString ?? '', undefined, 1, { skipRequest: !searchString, addAbortSignal: true, }); - console.log('SearchAll', source.displayName, results); + const { data: searchResult, isLoading, error, abortRequest } = results[0]!; + currentAbortRequest.current = abortRequest; + const mangas = searchResult?.fetchSourceManga?.mangas ?? []; const noMangasFound = !error && !isLoading && !mangas.length; @@ -130,16 +142,6 @@ const SourceSearchPreview = React.memo( errorMessage = t('manga.error.label.no_mangas_found'); } - useEffect( - () => () => { - // INFO: - // with strict mode + dev mode the first request will be aborted. due to using SWR there won't be an - // immediate second request since it's the same key. instead the "second" request will be the error handling of SWR - abortRequest(new Error(`SourceSearchPreview(${id}, ${displayName}): search string changed`)); - }, - [searchString, abortRequest], - ); - if ((!isLoading && !searchString) || emptyQuery) { return null; } diff --git a/src/modules/source/screens/SourceMangas.tsx b/src/modules/source/screens/SourceMangas.tsx index 842e34f8..6ba73d2a 100644 --- a/src/modules/source/screens/SourceMangas.tsx +++ b/src/modules/source/screens/SourceMangas.tsx @@ -6,7 +6,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useState } from 'react'; +import { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { useParams, useNavigate, useLocation } from 'react-router-dom'; import IconButton from '@mui/material/IconButton'; import SettingsIcon from '@mui/icons-material/Settings'; @@ -212,11 +212,6 @@ export function SourceMangas() { clearCache: boolean; }>().state ?? {}; - const [isFirstRender, setIsFirstRender] = useState(true); - useEffect(() => { - setIsFirstRender(false); - }, []); - const { settings: { hideLibraryEntries }, } = useMetadataServerSettings(); @@ -241,6 +236,21 @@ export function SourceMangas() { query ? SourceContentType.SEARCH : currentContentType!, ); + const scrollToTop = useCallback(() => { + AppStorage.session.setItem(getGridSnapshotKey(location), undefined, false); + window.scrollTo(0, 0); + }, [locationKey]); + + const currentQuery = useRef(query); + const currentAbortRequest = useRef<(reason: any) => void>(() => {}); + + const didSearchChange = currentQuery.current !== query; + if (didSearchChange && contentType === SourceContentType.SEARCH) { + currentQuery.current = query; + currentAbortRequest.current(new Error(`SourceMangas(${sourceId}): search string changed`)); + scrollToTop(); + } + useEffect( () => () => { setCurrentFiltersToApply(undefined); @@ -249,11 +259,6 @@ export function SourceMangas() { [sourceId], ); - const scrollToTop = useCallback(() => { - AppStorage.session.setItem(getGridSnapshotKey(location), undefined, false); - window.scrollTo(0, 0); - }, [locationKey]); - const setFiltersToApply = (filters: IPos[]) => { setCurrentFiltersToApply(filters); setLocationFiltersToApply(filters); @@ -269,6 +274,7 @@ export function SourceMangas() { loadPage, { data, error, isLoading: loading, size: lastPageNum, abortRequest, filteredOutAllItemsOfFetchedPage }, ] = useSourceManga(sourceId, contentType, query, filtersToApply, 1, hideLibraryEntries); + currentAbortRequest.current = abortRequest; const isLoading = loading || filteredOutAllItemsOfFetchedPage; const mangas = data?.fetchSourceManga?.mangas ?? []; const hasNextPage = !!data?.fetchSourceManga?.hasNextPage; @@ -389,20 +395,6 @@ export function SourceMangas() { requestManager.clearBrowseCacheFor(sourceId); }, [clearCache]); - useEffect( - () => () => { - if (contentType !== SourceContentType.SEARCH || isFirstRender) { - return; - } - // INFO: - // with strict mode + dev mode the first request will be aborted. due to using SWR there won't be an - // immediate second request since it's the same key. instead the "second" request will be the error handling of SWR - abortRequest(new Error(`SourceMangas(${sourceId}): search string changed`)); - scrollToTop(); - }, - [query, abortRequest], - ); - useLayoutEffect(() => { setTitle(source?.displayName ?? t('source.title_one')); setAction(