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 aab3e25031
This commit is contained in:
@@ -126,6 +126,9 @@ const HorizontalGrid = forwardRef(
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const getGridSnapshotKey = (location: ReturnType<typeof useLocation>) =>
|
||||||
|
`MangaGrid-snapshot-location-${location.key}`;
|
||||||
|
|
||||||
const VerticalGrid = forwardRef(
|
const VerticalGrid = forwardRef(
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
@@ -148,7 +151,7 @@ const VerticalGrid = forwardRef(
|
|||||||
) => {
|
) => {
|
||||||
const location = useLocation<{ snapshot?: GridStateSnapshot }>();
|
const location = useLocation<{ snapshot?: GridStateSnapshot }>();
|
||||||
|
|
||||||
const snapshotSessionKey = `MangaGrid-snapshot-location-${location.key}`;
|
const snapshotSessionKey = getGridSnapshotKey(location);
|
||||||
const [snapshot] = useSessionStorage<GridStateSnapshot | undefined>(snapshotSessionKey, undefined);
|
const [snapshot] = useSessionStorage<GridStateSnapshot | undefined>(snapshotSessionKey, undefined);
|
||||||
|
|
||||||
const persistGridStateTimeout = useRef<NodeJS.Timeout | undefined>();
|
const persistGridStateTimeout = useRef<NodeJS.Timeout | undefined>();
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ import {
|
|||||||
import { NavBarContext, useSetDefaultBackTo } from '@/components/context/NavbarContext.tsx';
|
import { NavBarContext, useSetDefaultBackTo } from '@/components/context/NavbarContext.tsx';
|
||||||
import { useMetadataServerSettings } from '@/lib/metadata/metadataServerSettings.ts';
|
import { useMetadataServerSettings } from '@/lib/metadata/metadataServerSettings.ts';
|
||||||
import { useSessionStorage } from '@/util/useStorage.tsx';
|
import { useSessionStorage } from '@/util/useStorage.tsx';
|
||||||
|
import { AppStorage } from '@/util/AppStorage.ts';
|
||||||
|
import { getGridSnapshotKey } from '@/components/MangaGrid.tsx';
|
||||||
|
|
||||||
const ContentTypeMenu = styled('div')(({ theme }) => ({
|
const ContentTypeMenu = styled('div')(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -218,7 +220,8 @@ export function SourceMangas() {
|
|||||||
const { sourceId } = useParams<{ sourceId: string }>();
|
const { sourceId } = useParams<{ sourceId: string }>();
|
||||||
|
|
||||||
const navigate = useNavigate();
|
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 } =
|
const { contentType: initialContentType = SourceContentType.POPULAR, clearCache = false } =
|
||||||
useLocation<{
|
useLocation<{
|
||||||
contentType: SourceContentType;
|
contentType: SourceContentType;
|
||||||
@@ -248,7 +251,6 @@ export function SourceMangas() {
|
|||||||
currentFiltersToApply ?? [],
|
currentFiltersToApply ?? [],
|
||||||
);
|
);
|
||||||
const [dialogFiltersToApply, setDialogFiltersToApply] = useState<IPos[]>(filtersToApply);
|
const [dialogFiltersToApply, setDialogFiltersToApply] = useState<IPos[]>(filtersToApply);
|
||||||
const [resetScrollPosition, setResetScrollPosition] = useState(false);
|
|
||||||
const [currentContentType, setCurrentContentType] = useSessionStorage<SourceContentType | undefined>(
|
const [currentContentType, setCurrentContentType] = useSessionStorage<SourceContentType | undefined>(
|
||||||
`source-mangas-${sourceId}-content-type`,
|
`source-mangas-${sourceId}-content-type`,
|
||||||
initialContentType,
|
initialContentType,
|
||||||
@@ -266,9 +268,15 @@ export function SourceMangas() {
|
|||||||
[sourceId],
|
[sourceId],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const scrollToTop = useCallback(() => {
|
||||||
|
AppStorage.session.setItem(getGridSnapshotKey(location), undefined);
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
}, [locationKey]);
|
||||||
|
|
||||||
const setFiltersToApply = (filters: IPos[]) => {
|
const setFiltersToApply = (filters: IPos[]) => {
|
||||||
setCurrentFiltersToApply(filters);
|
setCurrentFiltersToApply(filters);
|
||||||
setLocationFiltersToApply(filters);
|
setLocationFiltersToApply(filters);
|
||||||
|
scrollToTop();
|
||||||
};
|
};
|
||||||
|
|
||||||
const setContentType = (newContentType: SourceContentType) => {
|
const setContentType = (newContentType: SourceContentType) => {
|
||||||
@@ -300,7 +308,7 @@ export function SourceMangas() {
|
|||||||
const updateContentType = useCallback(
|
const updateContentType = useCallback(
|
||||||
(newContentType: SourceContentType, newSearch?: string | null) => {
|
(newContentType: SourceContentType, newSearch?: string | null) => {
|
||||||
setContentType(newContentType);
|
setContentType(newContentType);
|
||||||
setResetScrollPosition(true);
|
scrollToTop();
|
||||||
|
|
||||||
if (query && !newSearch) {
|
if (query && !newSearch) {
|
||||||
navigate(
|
navigate(
|
||||||
@@ -313,16 +321,7 @@ export function SourceMangas() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[setContentType, query],
|
[setContentType, query, scrollToTop],
|
||||||
);
|
|
||||||
|
|
||||||
const updateLocationFilters = useCallback(
|
|
||||||
(updatedFilters: IPos[]) => {
|
|
||||||
if (contentType === SourceContentType.SEARCH) {
|
|
||||||
setFiltersToApply(updatedFilters);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[contentType, query],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const setSearchContentType = !!query && contentType !== SourceContentType.SEARCH;
|
const setSearchContentType = !!query && contentType !== SourceContentType.SEARCH;
|
||||||
@@ -338,12 +337,10 @@ export function SourceMangas() {
|
|||||||
loadPage(lastPageNum + 1);
|
loadPage(lastPageNum + 1);
|
||||||
}, [lastPageNum, hasNextPage, contentType]);
|
}, [lastPageNum, hasNextPage, contentType]);
|
||||||
|
|
||||||
const resetFilters = useCallback(async () => {
|
const resetFilters = useCallback(() => {
|
||||||
setDialogFiltersToApply([]);
|
setDialogFiltersToApply([]);
|
||||||
setFiltersToApply([]);
|
setFiltersToApply([]);
|
||||||
updateLocationFilters([]);
|
}, [sourceId, contentType]);
|
||||||
setResetScrollPosition(true);
|
|
||||||
}, [sourceId, contentType, updateLocationFilters]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (filteredOutAllItemsOfFetchedPage && hasNextPage && !loading) {
|
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
|
// 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
|
// 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`));
|
abortRequest(new Error(`SourceMangas(${sourceId}): search string changed`));
|
||||||
setResetScrollPosition(true);
|
scrollToTop();
|
||||||
},
|
},
|
||||||
[query],
|
[query],
|
||||||
);
|
);
|
||||||
@@ -406,15 +403,6 @@ export function SourceMangas() {
|
|||||||
};
|
};
|
||||||
}, [t, source]);
|
}, [t, source]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!resetScrollPosition) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.scrollTo(0, 0);
|
|
||||||
setResetScrollPosition(false);
|
|
||||||
}, [resetScrollPosition]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledGridWrapper hasContent={!!mangas.length}>
|
<StyledGridWrapper hasContent={!!mangas.length}>
|
||||||
<ContentTypeMenu>
|
<ContentTypeMenu>
|
||||||
@@ -459,7 +447,6 @@ export function SourceMangas() {
|
|||||||
updateFilterValue={setDialogFiltersToApply}
|
updateFilterValue={setDialogFiltersToApply}
|
||||||
setTriggerUpdate={() => {
|
setTriggerUpdate={() => {
|
||||||
setFiltersToApply(dialogFiltersToApply);
|
setFiltersToApply(dialogFiltersToApply);
|
||||||
updateLocationFilters(dialogFiltersToApply);
|
|
||||||
}}
|
}}
|
||||||
resetFilterValue={resetFilters}
|
resetFilterValue={resetFilters}
|
||||||
update={dialogFiltersToApply}
|
update={dialogFiltersToApply}
|
||||||
|
|||||||
Reference in New Issue
Block a user