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 { useCurrentServerId } from '/@/renderer/store';
@@ -19,6 +23,7 @@ import {
import { ItemListKey } from '/@/shared/types/types';
interface AlbumArtistCarouselProps {
containerQuery?: ReturnType<typeof useGridCarouselContainerQuery>;
excludeIds?: string[];
query?: Partial<Omit<AlbumArtistListQuery, 'startIndex'>>;
rowCount?: number;
@@ -27,13 +32,22 @@ interface AlbumArtistCarouselProps {
title: React.ReactNode | string;
}
export const BaseAlbumArtistInfiniteCarousel = (props: AlbumArtistCarouselProps) => {
const { excludeIds, query: additionalQuery, rowCount = 1, sortBy, sortOrder, title } = props;
const rows = useGridRows(LibraryItem.ALBUM_ARTIST, ItemListKey.ALBUM_ARTIST);
const BaseAlbumArtistInfiniteCarousel = (props: AlbumArtistCarouselProps & { rows: DataRow[] }) => {
const {
containerQuery,
excludeIds,
query: additionalQuery,
rowCount = 1,
rows,
sortBy,
sortOrder,
title,
} = props;
const {
data: albumArtists,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
refetch,
} = useAlbumArtistListInfinite(sortBy, sortOrder, 20, additionalQuery);
@@ -41,7 +55,8 @@ export const BaseAlbumArtistInfiniteCarousel = (props: AlbumArtistCarouselProps)
const cards = useMemo(() => {
// Flatten all pages and filter excluded IDs
const allItems = albumArtists.pages.flatMap((page: AlbumArtistListResponse) => page.items);
const allItems =
albumArtists?.pages.flatMap((page: AlbumArtistListResponse) => page.items) || [];
const filteredItems = excludeIds
? allItems.filter((albumArtist) => !excludeIds.includes(albumArtist.id))
: allItems;
@@ -60,7 +75,7 @@ export const BaseAlbumArtistInfiniteCarousel = (props: AlbumArtistCarouselProps)
),
id: albumArtist.id,
}));
}, [albumArtists.pages, controls, excludeIds, rows]);
}, [albumArtists, controls, excludeIds, rows]);
const handleNextPage = useCallback(() => {}, []);
@@ -71,10 +86,10 @@ export const BaseAlbumArtistInfiniteCarousel = (props: AlbumArtistCarouselProps)
}, [refetch]);
const firstPageItems = excludeIds
? albumArtists.pages[0]?.items.filter(
? albumArtists?.pages[0]?.items.filter(
(albumArtist) => !excludeIds.includes(albumArtist.id),
) || []
: albumArtists.pages[0]?.items || [];
: albumArtists?.pages[0]?.items || [];
if (firstPageItems.length === 0) {
return null;
@@ -83,11 +98,15 @@ export const BaseAlbumArtistInfiniteCarousel = (props: AlbumArtistCarouselProps)
return (
<GridCarousel
cards={cards}
containerQuery={containerQuery}
hasNextPage={hasNextPage}
isFetchingNextPage={isFetchingNextPage}
loadNextPage={fetchNextPage}
onNextPage={handleNextPage}
onPrevPage={handlePrevPage}
onRefresh={handleRefresh}
placeholderItemType={LibraryItem.ALBUM_ARTIST}
placeholderRows={rows}
rowCount={rowCount}
title={title}
/>
@@ -95,7 +114,22 @@ export const BaseAlbumArtistInfiniteCarousel = (props: AlbumArtistCarouselProps)
};
export const AlbumArtistInfiniteCarousel = (props: AlbumArtistCarouselProps) => {
return <BaseAlbumArtistInfiniteCarousel {...props} />;
const rows = useGridRows(LibraryItem.ALBUM_ARTIST, ItemListKey.ALBUM_ARTIST);
return (
<Suspense
fallback={
<GridCarouselSkeletonFallback
containerQuery={props.containerQuery}
placeholderItemType={LibraryItem.ALBUM_ARTIST}
placeholderRows={rows}
title={props.title}
/>
}
>
<BaseAlbumArtistInfiniteCarousel {...props} rows={rows} />
</Suspense>
);
};
function useAlbumArtistListInfinite(