diff --git a/src/renderer/components/item-list/item-detail-list/item-detail.tsx b/src/renderer/components/item-list/item-detail-list/item-detail.tsx index 0ca8bdcba..40e10f1c8 100644 --- a/src/renderer/components/item-list/item-detail-list/item-detail.tsx +++ b/src/renderer/components/item-list/item-detail-list/item-detail.tsx @@ -15,7 +15,7 @@ import { } from 'react'; import { useTranslation } from 'react-i18next'; import { generatePath, Link } from 'react-router'; -import { List, RowComponentProps, useDynamicRowHeight } from 'react-window-v2'; +import { List, RowComponentProps, useDynamicRowHeight, useListRef } from 'react-window-v2'; import styles from './item-detail.module.css'; @@ -73,7 +73,9 @@ interface ItemDetailListProps { itemCount?: number; items?: unknown[]; onRangeChanged?: (range: { startIndex: number; stopIndex: number }) => Promise | void; + onScrollEnd?: (rowIndex: number) => void; rowHeight?: number; + scrollOffset?: number; } interface RowData { @@ -815,6 +817,8 @@ const DetailListHeader = memo( DetailListHeader.displayName = 'DetailListHeader'; +const SCROLL_END_DEBOUNCE_MS = 150; + export const ItemDetailList = ({ currentPage, data, @@ -823,9 +827,13 @@ export const ItemDetailList = ({ itemCount: externalItemCount, items, onRangeChanged, + onScrollEnd, }: ItemDetailListProps) => { const containerRef = useRef(null); + const listRef = useListRef(null); + const lastVisibleStartIndexRef = useRef(0); const queryClient = useQueryClient(); + const controls = useDefaultItemListControls(); const isMutatingCreateFavorite = useIsMutatingCreateFavorite(); const isMutatingDeleteFavorite = useIsMutatingDeleteFavorite(); @@ -909,6 +917,7 @@ export const ItemDetailList = ({ const handleRowsRendered = useCallback( (range: { startIndex: number; stopIndex: number }) => { + lastVisibleStartIndexRef.current = range.startIndex; const el = headerLeftRef.current; if (el) { const album = getItem?.(range.startIndex) as Album | undefined; @@ -1017,8 +1026,27 @@ export const ItemDetailList = ({ target: container, }); - return () => osInstance()?.destroy(); - }, [initialize, osInstance]); + let scrollEndTimeoutId: null | ReturnType = null; + const handleScroll = () => { + if (scrollEndTimeoutId) clearTimeout(scrollEndTimeoutId); + scrollEndTimeoutId = setTimeout(() => { + scrollEndTimeoutId = null; + onScrollEnd?.(lastVisibleStartIndexRef.current); + }, SCROLL_END_DEBOUNCE_MS); + }; + + if (onScrollEnd) { + viewport.addEventListener('scroll', handleScroll, { passive: true }); + } + + return () => { + if (onScrollEnd) { + viewport.removeEventListener('scroll', handleScroll); + if (scrollEndTimeoutId) clearTimeout(scrollEndTimeoutId); + } + osInstance()?.destroy(); + }; + }, [initialize, onScrollEnd, osInstance]); return (
@@ -1033,6 +1061,7 @@ export const ItemDetailList = ({ )}
) => ReactElement diff --git a/src/renderer/features/albums/components/album-list-infinite-detail.tsx b/src/renderer/features/albums/components/album-list-infinite-detail.tsx index 940a14ff4..599814886 100644 --- a/src/renderer/features/albums/components/album-list-infinite-detail.tsx +++ b/src/renderer/features/albums/components/album-list-infinite-detail.tsx @@ -2,7 +2,6 @@ import { UseSuspenseQueryOptions } from '@tanstack/react-query'; import { api } from '/@/renderer/api'; import { useItemListInfiniteLoader } from '/@/renderer/components/item-list/helpers/item-list-infinite-loader'; -import { useItemListScrollPersist } from '/@/renderer/components/item-list/helpers/use-item-list-scroll-persist'; import { ItemDetailList } from '/@/renderer/components/item-list/item-detail-list/item-detail'; import { ItemListComponentProps } from '/@/renderer/components/item-list/types'; import { albumQueries } from '/@/renderer/features/albums/api/album-api'; @@ -25,7 +24,6 @@ export const AlbumListInfiniteDetail = ({ sortBy: AlbumListSort.NAME, sortOrder: SortOrder.ASC, }, - saveScrollOffset = true, serverId, }: AlbumListInfiniteDetailProps) => { const listCountQuery = albumQueries.listCount({ @@ -45,10 +43,6 @@ export const AlbumListInfiniteDetail = ({ serverId, }); - const { handleOnScrollEnd, scrollOffset } = useItemListScrollPersist({ - enabled: saveScrollOffset, - }); - return ( { const listCountQuery = albumQueries.listCount({ @@ -50,10 +48,6 @@ export const AlbumListPaginatedDetail = ({ serverId, }); - const { handleOnScrollEnd, scrollOffset } = useItemListScrollPersist({ - enabled: saveScrollOffset, - }); - return ( { + updater: (prev: undefined | { items: Song[] }) => { if (!prev) return prev; const updatedItems = updateItemInArray(prev.items, itemIdSet, (item) => createFavoriteUpdater(item), @@ -701,10 +701,7 @@ export const applyFavoriteOptimisticUpdatesDeferred = ( queryKeys.playlists.songList(variables.apiClientProps.serverId), 'playlist-song-list', ); - collectQueries( - queryKeys.songs.list(variables.apiClientProps.serverId), - 'song-list', - ); + collectQueries(queryKeys.songs.list(variables.apiClientProps.serverId), 'song-list'); collectQueries( queryKeys.albumArtists.topSongs(variables.apiClientProps.serverId), 'top-songs',