From 1f227ce88620bca6f1263544be980e4e6a4b06d9 Mon Sep 17 00:00:00 2001 From: schroda <50052685+schroda@users.noreply.github.com> Date: Tue, 16 Apr 2024 02:49:08 +0200 Subject: [PATCH] Scroll to top when changing shown data in source browse The current scroll position was preserved when changing the shown content type (browse, latest, filters) or when changing the filters. Regression introduced with aab3e25031babe13c5df4e64aa57e61a8d8db80c --- src/components/MangaGrid.tsx | 5 ++++- src/screens/SourceMangas.tsx | 43 +++++++++++++----------------------- 2 files changed, 19 insertions(+), 29 deletions(-) diff --git a/src/components/MangaGrid.tsx b/src/components/MangaGrid.tsx index a2b3c056..d43acb8d 100644 --- a/src/components/MangaGrid.tsx +++ b/src/components/MangaGrid.tsx @@ -126,6 +126,9 @@ const HorizontalGrid = forwardRef( ), ); +export const getGridSnapshotKey = (location: ReturnType) => + `MangaGrid-snapshot-location-${location.key}`; + const VerticalGrid = forwardRef( ( { @@ -148,7 +151,7 @@ const VerticalGrid = forwardRef( ) => { const location = useLocation<{ snapshot?: GridStateSnapshot }>(); - const snapshotSessionKey = `MangaGrid-snapshot-location-${location.key}`; + const snapshotSessionKey = getGridSnapshotKey(location); const [snapshot] = useSessionStorage(snapshotSessionKey, undefined); const persistGridStateTimeout = useRef(); diff --git a/src/screens/SourceMangas.tsx b/src/screens/SourceMangas.tsx index 734de31a..e83c8c13 100644 --- a/src/screens/SourceMangas.tsx +++ b/src/screens/SourceMangas.tsx @@ -38,6 +38,8 @@ import { import { NavBarContext, useSetDefaultBackTo } from '@/components/context/NavbarContext.tsx'; import { useMetadataServerSettings } from '@/lib/metadata/metadataServerSettings.ts'; import { useSessionStorage } from '@/util/useStorage.tsx'; +import { AppStorage } from '@/util/AppStorage.ts'; +import { getGridSnapshotKey } from '@/components/MangaGrid.tsx'; const ContentTypeMenu = styled('div')(({ theme }) => ({ display: 'flex', @@ -218,7 +220,8 @@ export function SourceMangas() { const { sourceId } = useParams<{ sourceId: string }>(); const navigate = useNavigate(); - const { key: locationKey, state: locationState } = useLocation(); + const location = useLocation(); + const { key: locationKey, state: locationState } = location; const { contentType: initialContentType = SourceContentType.POPULAR, clearCache = false } = useLocation<{ contentType: SourceContentType; @@ -248,7 +251,6 @@ export function SourceMangas() { currentFiltersToApply ?? [], ); const [dialogFiltersToApply, setDialogFiltersToApply] = useState(filtersToApply); - const [resetScrollPosition, setResetScrollPosition] = useState(false); const [currentContentType, setCurrentContentType] = useSessionStorage( `source-mangas-${sourceId}-content-type`, initialContentType, @@ -266,9 +268,15 @@ export function SourceMangas() { [sourceId], ); + const scrollToTop = useCallback(() => { + AppStorage.session.setItem(getGridSnapshotKey(location), undefined); + window.scrollTo(0, 0); + }, [locationKey]); + const setFiltersToApply = (filters: IPos[]) => { setCurrentFiltersToApply(filters); setLocationFiltersToApply(filters); + scrollToTop(); }; const setContentType = (newContentType: SourceContentType) => { @@ -300,7 +308,7 @@ export function SourceMangas() { const updateContentType = useCallback( (newContentType: SourceContentType, newSearch?: string | null) => { setContentType(newContentType); - setResetScrollPosition(true); + scrollToTop(); if (query && !newSearch) { navigate( @@ -313,16 +321,7 @@ export function SourceMangas() { ); } }, - [setContentType, query], - ); - - const updateLocationFilters = useCallback( - (updatedFilters: IPos[]) => { - if (contentType === SourceContentType.SEARCH) { - setFiltersToApply(updatedFilters); - } - }, - [contentType, query], + [setContentType, query, scrollToTop], ); const setSearchContentType = !!query && contentType !== SourceContentType.SEARCH; @@ -338,12 +337,10 @@ export function SourceMangas() { loadPage(lastPageNum + 1); }, [lastPageNum, hasNextPage, contentType]); - const resetFilters = useCallback(async () => { + const resetFilters = useCallback(() => { setDialogFiltersToApply([]); setFiltersToApply([]); - updateLocationFilters([]); - setResetScrollPosition(true); - }, [sourceId, contentType, updateLocationFilters]); + }, [sourceId, contentType]); useEffect(() => { if (filteredOutAllItemsOfFetchedPage && hasNextPage && !loading) { @@ -373,7 +370,7 @@ export function SourceMangas() { // 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`)); - setResetScrollPosition(true); + scrollToTop(); }, [query], ); @@ -406,15 +403,6 @@ export function SourceMangas() { }; }, [t, source]); - useEffect(() => { - if (!resetScrollPosition) { - return; - } - - window.scrollTo(0, 0); - setResetScrollPosition(false); - }, [resetScrollPosition]); - return ( @@ -459,7 +447,6 @@ export function SourceMangas() { updateFilterValue={setDialogFiltersToApply} setTriggerUpdate={() => { setFiltersToApply(dialogFiltersToApply); - updateLocationFilters(dialogFiltersToApply); }} resetFilterValue={resetFilters} update={dialogFiltersToApply}