mirror of
https://github.com/jeffvli/feishin.git
synced 2026-06-22 20:07:42 +02:00
feat(artists): preserve artist detail scroll position on back navigation (#2045)
* feat(artists): preserve artist detail scroll position on back navigation * fix(artists): target OverlayScrollbars viewport child for scroll persistence Signed-off-by: Sai Asish Y <say.apm35@gmail.com> --------- Signed-off-by: Sai Asish Y <say.apm35@gmail.com>
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
import { RefObject, useEffect, useLayoutEffect } from 'react';
|
||||
import { useLocation, useNavigationType } from 'react-router';
|
||||
|
||||
import { useScrollStore } from '/@/renderer/store/scroll.store';
|
||||
|
||||
interface UseNativeScrollPersistProps {
|
||||
enabled: boolean;
|
||||
scrollRef: RefObject<HTMLDivElement | null>;
|
||||
}
|
||||
|
||||
// OverlayScrollbars initializes on the NativeScrollArea container and moves the
|
||||
// content into a viewport child element; that child is what actually scrolls,
|
||||
// so scrollTop must be read from and written to it rather than the container
|
||||
// the ref points at.
|
||||
const getScrollNode = (scrollRef: RefObject<HTMLDivElement | null>): HTMLElement | null => {
|
||||
const node = scrollRef.current?.children[0];
|
||||
return node instanceof HTMLElement ? node : null;
|
||||
};
|
||||
|
||||
// Persists vertical scroll offset for a NativeScrollArea, keyed by react-router
|
||||
// location.key. Restores the saved offset only on POP navigation; PUSH/REPLACE
|
||||
// continue to start at the top.
|
||||
export const useNativeScrollPersist = ({ enabled, scrollRef }: UseNativeScrollPersistProps) => {
|
||||
const location = useLocation();
|
||||
const navigationType = useNavigationType();
|
||||
const setOffset = useScrollStore((s) => s.setOffset);
|
||||
const getOffset = useScrollStore((s) => s.getOffset);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const saved = getOffset(location.key);
|
||||
if (!enabled || navigationType !== 'POP' || typeof saved !== 'number') {
|
||||
return;
|
||||
}
|
||||
|
||||
const applyOffset = () => {
|
||||
const node = getScrollNode(scrollRef);
|
||||
if (node) {
|
||||
node.scrollTop = saved;
|
||||
}
|
||||
};
|
||||
|
||||
applyOffset();
|
||||
const raf = requestAnimationFrame(applyOffset);
|
||||
return () => cancelAnimationFrame(raf);
|
||||
}, [enabled, getOffset, location.key, navigationType, scrollRef]);
|
||||
|
||||
useEffect(() => {
|
||||
const node = getScrollNode(scrollRef);
|
||||
if (!enabled || !node) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handleScroll = () => {
|
||||
setOffset(location.key, node.scrollTop);
|
||||
};
|
||||
|
||||
node.addEventListener('scroll', handleScroll, { passive: true });
|
||||
return () => {
|
||||
node.removeEventListener('scroll', handleScroll);
|
||||
};
|
||||
}, [enabled, location.key, scrollRef, setOffset]);
|
||||
};
|
||||
@@ -4,6 +4,7 @@ import { useParams } from 'react-router';
|
||||
|
||||
import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
|
||||
import { NativeScrollArea } from '/@/renderer/components/native-scroll-area/native-scroll-area';
|
||||
import { useNativeScrollPersist } from '/@/renderer/components/native-scroll-area/use-native-scroll-persist';
|
||||
import { albumQueries } from '/@/renderer/features/albums/api/album-api';
|
||||
import { artistsQueries } from '/@/renderer/features/artists/api/artists-api';
|
||||
import { AlbumArtistDetailContent } from '/@/renderer/features/artists/components/album-artist-detail-content';
|
||||
@@ -28,6 +29,8 @@ const AlbumArtistDetailRouteContent = () => {
|
||||
const serverId = useCurrentServerId();
|
||||
const { artistBackground, artistBackgroundBlur } = useArtistBackground();
|
||||
|
||||
useNativeScrollPersist({ enabled: true, scrollRef: scrollAreaRef });
|
||||
|
||||
const { albumArtistId, artistId } = useParams() as {
|
||||
albumArtistId?: string;
|
||||
artistId?: string;
|
||||
|
||||
Reference in New Issue
Block a user