add loading placeholder cards to grid carousel

This commit is contained in:
jeffvli
2026-01-16 01:31:48 -08:00
parent b6a670689c
commit 46b80b9a18
6 changed files with 339 additions and 126 deletions
@@ -1,10 +1,14 @@
import { useSuspenseInfiniteQuery } from '@tanstack/react-query';
import { useCallback, useMemo } from 'react';
import { Suspense, useCallback, useMemo } from 'react';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { GridCarousel } from '/@/renderer/components/grid-carousel/grid-carousel-v2';
import { MemoizedItemCard } from '/@/renderer/components/item-card/item-card';
import {
GridCarousel,
GridCarouselSkeletonFallback,
useGridCarouselContainerQuery,
} from '/@/renderer/components/grid-carousel/grid-carousel-v2';
import { DataRow, MemoizedItemCard } from '/@/renderer/components/item-card/item-card';
import { useDefaultItemListControls } from '/@/renderer/components/item-list/helpers/item-list-controls';
import { useGridRows } from '/@/renderer/components/item-list/helpers/use-grid-rows';
import { DefaultItemControlProps } from '/@/renderer/components/item-list/types';
@@ -21,6 +25,7 @@ import {
import { ItemListKey, Play } from '/@/shared/types/types';
interface SongCarouselProps {
containerQuery?: ReturnType<typeof useGridCarouselContainerQuery>;
enableRefresh?: boolean;
excludeIds?: string[];
query?: Partial<Omit<SongListQuery, 'startIndex'>>;
@@ -30,21 +35,23 @@ interface SongCarouselProps {
title: React.ReactNode | string;
}
const BaseSongInfiniteCarousel = (props: SongCarouselProps) => {
const BaseSongInfiniteCarousel = (props: SongCarouselProps & { rows: DataRow[] }) => {
const {
containerQuery,
enableRefresh,
excludeIds,
query: additionalQuery,
rowCount = 1,
rows,
sortBy,
sortOrder,
title,
} = props;
const rows = useGridRows(LibraryItem.SONG, ItemListKey.SONG);
const {
data: songs,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
refetch,
} = useSongListInfinite(sortBy, sortOrder, 20, additionalQuery);
@@ -66,7 +73,7 @@ const BaseSongInfiniteCarousel = (props: SongCarouselProps) => {
const cards = useMemo(() => {
// Flatten all pages and filter excluded IDs
const allItems = songs.pages.flatMap((page: SongListResponse) => page.items);
const allItems = songs?.pages.flatMap((page: SongListResponse) => page.items) || [];
const filteredItems = excludeIds
? allItems.filter((song) => !excludeIds.includes(song.id))
: allItems;
@@ -85,7 +92,7 @@ const BaseSongInfiniteCarousel = (props: SongCarouselProps) => {
),
id: song.id,
}));
}, [songs.pages, controls, excludeIds, rows]);
}, [songs, controls, excludeIds, rows]);
const handleNextPage = useCallback(() => {}, []);
@@ -96,8 +103,8 @@ const BaseSongInfiniteCarousel = (props: SongCarouselProps) => {
}, [refetch]);
const firstPageItems = excludeIds
? songs.pages[0]?.items.filter((song) => !excludeIds.includes(song.id)) || []
: songs.pages[0]?.items || [];
? songs?.pages[0]?.items.filter((song) => !excludeIds.includes(song.id)) || []
: songs?.pages[0]?.items || [];
if (firstPageItems.length === 0) {
return null;
@@ -106,12 +113,16 @@ const BaseSongInfiniteCarousel = (props: SongCarouselProps) => {
return (
<GridCarousel
cards={cards}
containerQuery={containerQuery}
enableRefresh={enableRefresh}
hasNextPage={hasNextPage}
isFetchingNextPage={isFetchingNextPage}
loadNextPage={fetchNextPage}
onNextPage={handleNextPage}
onPrevPage={handlePrevPage}
onRefresh={handleRefresh}
placeholderItemType={LibraryItem.SONG}
placeholderRows={rows}
rowCount={rowCount}
title={title}
/>
@@ -119,7 +130,22 @@ const BaseSongInfiniteCarousel = (props: SongCarouselProps) => {
};
export const SongInfiniteCarousel = (props: SongCarouselProps) => {
return <BaseSongInfiniteCarousel {...props} />;
const rows = useGridRows(LibraryItem.SONG, ItemListKey.SONG);
return (
<Suspense
fallback={
<GridCarouselSkeletonFallback
containerQuery={props.containerQuery}
placeholderItemType={LibraryItem.SONG}
placeholderRows={rows}
title={props.title}
/>
}
>
<BaseSongInfiniteCarousel {...props} rows={rows} />
</Suspense>
);
};
function useSongListInfinite(