diff --git a/src/renderer/features/albums/components/album-list-content.tsx b/src/renderer/features/albums/components/album-list-content.tsx new file mode 100644 index 00000000..fc41972c --- /dev/null +++ b/src/renderer/features/albums/components/album-list-content.tsx @@ -0,0 +1,161 @@ +import { + ALBUM_CARD_ROWS, + VirtualGridAutoSizerContainer, + VirtualInfiniteGrid, +} from '/@/renderer/components'; +import { AppRoute } from '/@/renderer/router/routes'; +import { CardDisplayType, CardRow, LibraryItem } from '/@/renderer/types'; +import AutoSizer from 'react-virtualized-auto-sizer'; +import { useCallback, useMemo } from 'react'; +import { ListOnScrollProps } from 'react-window'; +import { api } from '/@/renderer/api'; +import { controller } from '/@/renderer/api/controller'; +import { queryKeys } from '/@/renderer/api/query-keys'; +import { Album, AlbumListSort } from '/@/renderer/api/types'; +import { useAlbumList } from '/@/renderer/features/albums/queries/album-list-query'; +import { useHandlePlayQueueAdd } from '/@/renderer/features/player/hooks/use-handle-playqueue-add'; +import { useQueryClient } from '@tanstack/react-query'; +import { useCurrentServer, useSetAlbumStore, useAlbumListStore } from '/@/renderer/store'; + +export const AlbumListContent = () => { + const queryClient = useQueryClient(); + const server = useCurrentServer(); + const page = useAlbumListStore(); + const setPage = useSetAlbumStore(); + const handlePlayQueueAdd = useHandlePlayQueueAdd(); + + const albumListQuery = useAlbumList({ + limit: 1, + startIndex: 0, + ...page.filter, + }); + + const fetch = useCallback( + async ({ skip, take }: { skip: number; take: number }) => { + const queryKey = queryKeys.albums.list(server?.id || '', { + limit: take, + startIndex: skip, + ...page.filter, + }); + + const albums = await queryClient.fetchQuery(queryKey, async ({ signal }) => + controller.getAlbumList({ + query: { + limit: take, + startIndex: skip, + ...page.filter, + }, + server, + signal, + }), + ); + + return api.normalize.albumList(albums, server); + }, + [page.filter, queryClient, server], + ); + + const handleGridScroll = useCallback( + (e: ListOnScrollProps) => { + setPage({ + list: { + ...page, + grid: { + ...page.grid, + scrollOffset: e.scrollOffset, + }, + }, + }); + }, + [page, setPage], + ); + + const cardRows = useMemo(() => { + const rows: CardRow[] = [ALBUM_CARD_ROWS.name]; + + switch (page.filter.sortBy) { + case AlbumListSort.ALBUM_ARTIST: + rows.push(ALBUM_CARD_ROWS.albumArtists); + rows.push(ALBUM_CARD_ROWS.releaseYear); + break; + case AlbumListSort.ARTIST: + rows.push(ALBUM_CARD_ROWS.artists); + rows.push(ALBUM_CARD_ROWS.releaseYear); + break; + case AlbumListSort.COMMUNITY_RATING: + rows.push(ALBUM_CARD_ROWS.albumArtists); + break; + case AlbumListSort.DURATION: + rows.push(ALBUM_CARD_ROWS.albumArtists); + rows.push(ALBUM_CARD_ROWS.duration); + break; + case AlbumListSort.FAVORITED: + rows.push(ALBUM_CARD_ROWS.albumArtists); + rows.push(ALBUM_CARD_ROWS.releaseYear); + break; + case AlbumListSort.NAME: + rows.push(ALBUM_CARD_ROWS.albumArtists); + rows.push(ALBUM_CARD_ROWS.releaseYear); + break; + case AlbumListSort.PLAY_COUNT: + rows.push(ALBUM_CARD_ROWS.albumArtists); + rows.push(ALBUM_CARD_ROWS.playCount); + break; + case AlbumListSort.RANDOM: + rows.push(ALBUM_CARD_ROWS.albumArtists); + rows.push(ALBUM_CARD_ROWS.releaseYear); + break; + case AlbumListSort.RATING: + rows.push(ALBUM_CARD_ROWS.albumArtists); + rows.push(ALBUM_CARD_ROWS.rating); + break; + case AlbumListSort.RECENTLY_ADDED: + rows.push(ALBUM_CARD_ROWS.albumArtists); + rows.push(ALBUM_CARD_ROWS.createdAt); + break; + case AlbumListSort.RECENTLY_PLAYED: + rows.push(ALBUM_CARD_ROWS.albumArtists); + rows.push(ALBUM_CARD_ROWS.lastPlayedAt); + break; + case AlbumListSort.SONG_COUNT: + rows.push(ALBUM_CARD_ROWS.albumArtists); + rows.push(ALBUM_CARD_ROWS.songCount); + break; + case AlbumListSort.YEAR: + rows.push(ALBUM_CARD_ROWS.albumArtists); + rows.push(ALBUM_CARD_ROWS.releaseYear); + break; + } + + return rows; + }, [page.filter.sortBy]); + + return ( + + + {({ height, width }) => ( + + )} + + + ); +}; diff --git a/src/renderer/features/albums/components/album-list-header.tsx b/src/renderer/features/albums/components/album-list-header.tsx index 740eb163..a0e9ab9a 100644 --- a/src/renderer/features/albums/components/album-list-header.tsx +++ b/src/renderer/features/albums/components/album-list-header.tsx @@ -33,27 +33,31 @@ import { JellyfinAlbumFilters } from '/@/renderer/features/albums/components/jel const FILTERS = { jellyfin: [ - { name: 'Album Artist', value: AlbumListSort.ALBUM_ARTIST }, - { name: 'Community Rating', value: AlbumListSort.COMMUNITY_RATING }, - { name: 'Critic Rating', value: AlbumListSort.CRITIC_RATING }, - { name: 'Name', value: AlbumListSort.NAME }, - { name: 'Random', value: AlbumListSort.RANDOM }, - { name: 'Recently Added', value: AlbumListSort.RECENTLY_ADDED }, - { name: 'Release Date', value: AlbumListSort.RELEASE_DATE }, + { defaultOrder: SortOrder.ASC, name: 'Album Artist', value: AlbumListSort.ALBUM_ARTIST }, + { + defaultOrder: SortOrder.DESC, + name: 'Community Rating', + value: AlbumListSort.COMMUNITY_RATING, + }, + { defaultOrder: SortOrder.DESC, name: 'Critic Rating', value: AlbumListSort.CRITIC_RATING }, + { defaultOrder: SortOrder.ASC, name: 'Name', value: AlbumListSort.NAME }, + { defaultOrder: SortOrder.ASC, name: 'Random', value: AlbumListSort.RANDOM }, + { defaultOrder: SortOrder.DESC, name: 'Recently Added', value: AlbumListSort.RECENTLY_ADDED }, + { defaultOrder: SortOrder.DESC, name: 'Release Date', value: AlbumListSort.RELEASE_DATE }, ], navidrome: [ - { name: 'Album Artist', value: AlbumListSort.ALBUM_ARTIST }, - { name: 'Artist', value: AlbumListSort.ARTIST }, - { name: 'Duration', value: AlbumListSort.DURATION }, - { name: 'Name', value: AlbumListSort.NAME }, - { name: 'Play Count', value: AlbumListSort.PLAY_COUNT }, - { name: 'Random', value: AlbumListSort.RANDOM }, - { name: 'Rating', value: AlbumListSort.RATING }, - { name: 'Recently Added', value: AlbumListSort.RECENTLY_ADDED }, - { name: 'Recently Played', value: AlbumListSort.RECENTLY_PLAYED }, - { name: 'Song Count', value: AlbumListSort.SONG_COUNT }, - { name: 'Favorited', value: AlbumListSort.FAVORITED }, - { name: 'Year', value: AlbumListSort.YEAR }, + { defaultOrder: SortOrder.ASC, name: 'Album Artist', value: AlbumListSort.ALBUM_ARTIST }, + { defaultOrder: SortOrder.ASC, name: 'Artist', value: AlbumListSort.ARTIST }, + { defaultOrder: SortOrder.DESC, name: 'Duration', value: AlbumListSort.DURATION }, + { defaultOrder: SortOrder.DESC, name: 'Most Played', value: AlbumListSort.PLAY_COUNT }, + { defaultOrder: SortOrder.ASC, name: 'Name', value: AlbumListSort.NAME }, + { defaultOrder: SortOrder.ASC, name: 'Random', value: AlbumListSort.RANDOM }, + { defaultOrder: SortOrder.DESC, name: 'Rating', value: AlbumListSort.RATING }, + { defaultOrder: SortOrder.DESC, name: 'Recently Added', value: AlbumListSort.RECENTLY_ADDED }, + { defaultOrder: SortOrder.DESC, name: 'Recently Played', value: AlbumListSort.RECENTLY_PLAYED }, + { defaultOrder: SortOrder.DESC, name: 'Song Count', value: AlbumListSort.SONG_COUNT }, + { defaultOrder: SortOrder.ASC, name: 'Favorited', value: AlbumListSort.FAVORITED }, + { defaultOrder: SortOrder.DESC, name: 'Year', value: AlbumListSort.YEAR }, ], }; @@ -79,9 +83,7 @@ export const AlbumListHeader = () => { const sortByLabel = (server?.type && - (FILTERS[server.type as keyof typeof FILTERS] as { name: string; value: string }[]).find( - (f) => f.value === filters.sortBy, - )?.name) || + FILTERS[server.type as keyof typeof FILTERS].find((f) => f.value === filters.sortBy)?.name) || 'Unknown'; const setSize = throttle( @@ -94,12 +96,18 @@ export const AlbumListHeader = () => { const handleSetSortBy = useCallback( (e: MouseEvent) => { - if (!e.currentTarget?.value) return; + if (!e.currentTarget?.value || !server?.type) return; + + const sortOrder = FILTERS[server.type as keyof typeof FILTERS].find( + (f) => f.value === e.currentTarget.value, + )?.defaultOrder; + setFilter({ sortBy: e.currentTarget.value as AlbumListSort, + sortOrder: sortOrder || SortOrder.ASC, }); }, - [setFilter], + [server?.type, setFilter], ); const handleSetMusicFolder = useCallback( @@ -170,15 +178,14 @@ export const AlbumListHeader = () => {