diff --git a/src/renderer/features/albums/components/album-list-content.tsx b/src/renderer/features/albums/components/album-list-content.tsx index 949c70c5..5abdbee5 100644 --- a/src/renderer/features/albums/components/album-list-content.tsx +++ b/src/renderer/features/albums/components/album-list-content.tsx @@ -16,7 +16,6 @@ import { api } from '/@/renderer/api'; import { controller } from '/@/renderer/api/controller'; import { queryKeys } from '/@/renderer/api/query-keys'; import { Album, AlbumListSort, LibraryItem } from '/@/renderer/api/types'; -import { useAlbumList } from '/@/renderer/features/albums/queries/album-list-query'; import { useQueryClient } from '@tanstack/react-query'; import { useCurrentServer, @@ -47,10 +46,11 @@ import { usePlayQueueAdd } from '/@/renderer/features/player'; interface AlbumListContentProps { gridRef: MutableRefObject; + itemCount?: number; tableRef: MutableRefObject; } -export const AlbumListContent = ({ gridRef, tableRef }: AlbumListContentProps) => { +export const AlbumListContent = ({ itemCount, gridRef, tableRef }: AlbumListContentProps) => { const queryClient = useQueryClient(); const navigate = useNavigate(); const server = useCurrentServer(); @@ -66,12 +66,6 @@ export const AlbumListContent = ({ gridRef, tableRef }: AlbumListContentProps) = const isPaginationEnabled = page.display === ListDisplayType.TABLE_PAGINATED; - const checkAlbumList = useAlbumList({ - limit: 1, - startIndex: 0, - ...page.filter, - }); - const columnDefs: ColDef[] = useMemo( () => getColumnDefs(page.table.columns), [page.table.columns], @@ -311,12 +305,12 @@ export const AlbumListContent = ({ gridRef, tableRef }: AlbumListContentProps) = handlePlayQueueAdd={handlePlayQueueAdd} height={height} initialScrollOffset={page?.grid.scrollOffset || 0} - itemCount={checkAlbumList?.data?.totalRecordCount || 0} + itemCount={itemCount || 0} itemData={itemData} itemGap={20} itemSize={150 + page.grid?.size} itemType={LibraryItem.ALBUM} - loading={checkAlbumList.isLoading} + loading={!itemCount} minimumBatchSize={40} route={{ route: AppRoute.LIBRARY_ALBUMS_DETAIL, @@ -340,7 +334,7 @@ export const AlbumListContent = ({ gridRef, tableRef }: AlbumListContentProps) = blockLoadDebounceMillis={200} columnDefs={columnDefs} getRowId={(data) => data.data.id} - infiniteInitialRowCount={checkAlbumList.data?.totalRecordCount || 1} + infiniteInitialRowCount={itemCount || 1} pagination={isPaginationEnabled} paginationAutoPageSize={isPaginationEnabled} paginationPageSize={page.table.pagination.itemsPerPage || 100} diff --git a/src/renderer/features/albums/components/album-list-header.tsx b/src/renderer/features/albums/components/album-list-header.tsx index aaa0448b..c575b123 100644 --- a/src/renderer/features/albums/components/album-list-header.tsx +++ b/src/renderer/features/albums/components/album-list-header.tsx @@ -17,9 +17,10 @@ import styled from 'styled-components'; import { api } from '/@/renderer/api'; import { controller } from '/@/renderer/api/controller'; import { queryKeys } from '/@/renderer/api/query-keys'; -import { AlbumListSort, ServerType, SortOrder } from '/@/renderer/api/types'; +import { AlbumListSort, LibraryItem, ServerType, SortOrder } from '/@/renderer/api/types'; import { ALBUM_TABLE_COLUMNS, + Badge, Button, DropdownMenu, MultiSelect, @@ -27,6 +28,7 @@ import { Popover, SearchInput, Slider, + SpinnerIcon, Switch, Text, TextTitle, @@ -45,7 +47,8 @@ import { useSetAlbumTable, useSetAlbumTablePagination, } from '/@/renderer/store'; -import { ListDisplayType, TableColumn } from '/@/renderer/types'; +import { ListDisplayType, Play, TableColumn } from '/@/renderer/types'; +import { usePlayQueueAdd } from '/@/renderer/features/player'; const FILTERS = { jellyfin: [ @@ -90,10 +93,11 @@ const HeaderItems = styled.div` interface AlbumListHeaderProps { gridRef: MutableRefObject; + itemCount?: number; tableRef: MutableRefObject; } -export const AlbumListHeader = ({ gridRef, tableRef }: AlbumListHeaderProps) => { +export const AlbumListHeader = ({ itemCount, gridRef, tableRef }: AlbumListHeaderProps) => { const queryClient = useQueryClient(); const server = useCurrentServer(); const setPage = useSetAlbumStore(); @@ -213,6 +217,11 @@ export const AlbumListHeader = ({ gridRef, tableRef }: AlbumListHeaderProps) => [page.display, tableRef, setPagination, server, queryClient, gridRef, fetch], ); + const handleRefresh = useCallback(() => { + queryClient.invalidateQueries(queryKeys.albums.list(server?.id || '')); + handleFilterChange(filters); + }, [filters, handleFilterChange, queryClient, server?.id]); + const handleSetSortBy = useCallback( (e: MouseEvent) => { if (!e.currentTarget?.value || !server?.type) return; @@ -301,6 +310,31 @@ export const AlbumListHeader = ({ gridRef, tableRef }: AlbumListHeaderProps) => } }; + const handlePlayQueueAdd = usePlayQueueAdd(); + + const handlePlay = async (play: Play) => { + if (!itemCount || itemCount === 0) return; + + const query = { startIndex: 0, ...filters }; + const queryKey = queryKeys.albums.list(server?.id || '', query); + + const albumListRes = await queryClient.fetchQuery({ + queryFn: ({ signal }) => api.controller.getAlbumList({ query, server, signal }), + queryKey, + }); + + const albumIds = + api.normalize.albumList(albumListRes, server).items?.map((item) => item.id) || []; + + handlePlayQueueAdd?.({ + byItemType: { + id: albumIds, + type: LibraryItem.ALBUM, + }, + play, + }); + }; + return ( @@ -311,20 +345,30 @@ export const AlbumListHeader = ({ gridRef, tableRef }: AlbumListHeaderProps) => > - + + + Albums + + + {itemCount === null || itemCount === undefined ? : itemCount} + + + + Display type @@ -495,10 +539,15 @@ export const AlbumListHeader = ({ gridRef, tableRef }: AlbumListHeaderProps) => - Play - Add to queue (next) - Add to queue (last) - Add to playlist + handlePlay(Play.NOW)}>Play + handlePlay(Play.LAST)}> + Add to queue (last) + + handlePlay(Play.NEXT)}> + Add to queue (next) + + + Refresh diff --git a/src/renderer/features/albums/routes/album-list-route.tsx b/src/renderer/features/albums/routes/album-list-route.tsx index f4916c8b..1c1068e2 100644 --- a/src/renderer/features/albums/routes/album-list-route.tsx +++ b/src/renderer/features/albums/routes/album-list-route.tsx @@ -4,20 +4,42 @@ import { AlbumListHeader } from '/@/renderer/features/albums/components/album-li import { AlbumListContent } from '/@/renderer/features/albums/components/album-list-content'; import { useRef } from 'react'; import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact'; +import { useAlbumList } from '/@/renderer/features/albums/queries/album-list-query'; +import { useAlbumListFilters } from '/@/renderer/store'; const AlbumListRoute = () => { const gridRef = useRef(null); const tableRef = useRef(null); + const filters = useAlbumListFilters(); + + const itemCountCheck = useAlbumList( + { + limit: 1, + startIndex: 0, + ...filters, + }, + { + cacheTime: 1000 * 60 * 60 * 2, + staleTime: 1000 * 60 * 60 * 2, + }, + ); + + const itemCount = + itemCountCheck.data?.totalRecordCount === null + ? undefined + : itemCountCheck.data?.totalRecordCount; return ( diff --git a/src/renderer/features/songs/components/song-list-content.tsx b/src/renderer/features/songs/components/song-list-content.tsx index 62b15045..2b547a98 100644 --- a/src/renderer/features/songs/components/song-list-content.tsx +++ b/src/renderer/features/songs/components/song-list-content.tsx @@ -19,7 +19,6 @@ import { VirtualGridAutoSizerContainer, VirtualTable, } from '/@/renderer/components'; -import { useSongList } from '/@/renderer/features/songs/queries/song-list-query'; import { useCurrentServer, useSetSongTable, @@ -38,10 +37,11 @@ import { LibraryItem, QueueSong } from '/@/renderer/api/types'; import { usePlayQueueAdd } from '/@/renderer/features/player'; interface SongListContentProps { + itemCount?: number; tableRef: MutableRefObject; } -export const SongListContent = ({ tableRef }: SongListContentProps) => { +export const SongListContent = ({ itemCount, tableRef }: SongListContentProps) => { const queryClient = useQueryClient(); const server = useCurrentServer(); const page = useSongListStore(); @@ -54,12 +54,6 @@ export const SongListContent = ({ tableRef }: SongListContentProps) => { const isPaginationEnabled = page.display === ListDisplayType.TABLE_PAGINATED; - const checkSongList = useSongList({ - limit: 1, - startIndex: 0, - ...page.filter, - }); - const columnDefs: ColDef[] = useMemo( () => getColumnDefs(page.table.columns), [page.table.columns], @@ -224,7 +218,7 @@ export const SongListContent = ({ tableRef }: SongListContentProps) => { defaultColDef={defaultColumnDefs} enableCellChangeFlash={false} getRowId={(data) => data.data.id} - infiniteInitialRowCount={checkSongList.data?.totalRecordCount || 100} + infiniteInitialRowCount={itemCount || 100} pagination={isPaginationEnabled} paginationAutoPageSize={isPaginationEnabled} paginationPageSize={page.table.pagination.itemsPerPage || 100} diff --git a/src/renderer/features/songs/components/song-list-header.tsx b/src/renderer/features/songs/components/song-list-header.tsx index 1d6dd824..84720b84 100644 --- a/src/renderer/features/songs/components/song-list-header.tsx +++ b/src/renderer/features/songs/components/song-list-header.tsx @@ -13,7 +13,13 @@ import { } from 'react-icons/ri'; import { api } from '/@/renderer/api'; import { queryKeys } from '/@/renderer/api/query-keys'; -import { ServerType, SongListSort, SortOrder } from '/@/renderer/api/types'; +import { + LibraryItem, + ServerType, + SongListQuery, + SongListSort, + SortOrder, +} from '/@/renderer/api/types'; import { Button, DropdownMenu, @@ -25,7 +31,10 @@ import { MultiSelect, Text, SONG_TABLE_COLUMNS, + Badge, + SpinnerIcon, } from '/@/renderer/components'; +import { usePlayQueueAdd } from '/@/renderer/features/player'; import { useMusicFolders } from '/@/renderer/features/shared'; import { JellyfinSongFilters } from '/@/renderer/features/songs/components/jellyfin-song-filters'; import { NavidromeSongFilters } from '/@/renderer/features/songs/components/navidrome-song-filters'; @@ -40,7 +49,7 @@ import { useSetSongTablePagination, useSongListStore, } from '/@/renderer/store'; -import { ListDisplayType, TableColumn } from '/@/renderer/types'; +import { ListDisplayType, Play, TableColumn } from '/@/renderer/types'; const FILTERS = { jellyfin: [ @@ -80,16 +89,18 @@ const ORDER = [ ]; interface SongListHeaderProps { + itemCount?: number; tableRef: MutableRefObject; } -export const SongListHeader = ({ tableRef }: SongListHeaderProps) => { +export const SongListHeader = ({ itemCount, tableRef }: SongListHeaderProps) => { const server = useCurrentServer(); const page = useSongListStore(); const setPage = useSetSongStore(); const setFilter = useSetSongFilters(); const setTable = useSetSongTable(); const setPagination = useSetSongTablePagination(); + const handlePlayQueueAdd = usePlayQueueAdd(); const cq = useContainerQuery(); const musicFoldersQuery = useMusicFolders(); @@ -244,6 +255,24 @@ export const SongListHeader = ({ tableRef }: SongListHeaderProps) => { setTable({ rowHeight: e }); }; + const handleRefresh = () => { + queryClient.invalidateQueries(queryKeys.songs.list(server?.id || '')); + handleFilterChange(page.filter); + }; + + const handlePlay = async (play: Play) => { + if (!itemCount || itemCount === 0) return; + const query: SongListQuery = { startIndex: 0, ...page.filter }; + + handlePlayQueueAdd?.({ + byItemType: { + id: query, + type: LibraryItem.SONG, + }, + play, + }); + }; + return ( { sx={{ paddingLeft: 0, paddingRight: 0 }} variant="subtle" > - - Tracks - + + + Tracks + + + {itemCount === null || itemCount === undefined ? : itemCount} + + @@ -420,10 +457,15 @@ export const SongListHeader = ({ tableRef }: SongListHeaderProps) => { - Play - Add to queue (last) - Add to queue (next) - Add to playlist + handlePlay(Play.NOW)}>Play + handlePlay(Play.LAST)}> + Add to queue (last) + + handlePlay(Play.NEXT)}> + Add to queue (next) + + + Refresh diff --git a/src/renderer/features/songs/routes/song-list-route.tsx b/src/renderer/features/songs/routes/song-list-route.tsx index 777ef0ba..5df390a8 100644 --- a/src/renderer/features/songs/routes/song-list-route.tsx +++ b/src/renderer/features/songs/routes/song-list-route.tsx @@ -4,15 +4,42 @@ import { VirtualGridContainer } from '/@/renderer/components'; import { AnimatedPage } from '/@/renderer/features/shared'; import { SongListContent } from '/@/renderer/features/songs/components/song-list-content'; import { SongListHeader } from '/@/renderer/features/songs/components/song-list-header'; +import { useSongList } from '/@/renderer/features/songs/queries/song-list-query'; +import { useSongListFilters } from '/@/renderer/store'; const TrackListRoute = () => { const tableRef = useRef(null); + const filters = useSongListFilters(); + + const itemCountCheck = useSongList( + { + limit: 1, + startIndex: 0, + ...filters, + }, + { + cacheTime: 1000 * 60 * 60 * 2, + staleTime: 1000 * 60 * 60 * 2, + }, + ); + + const itemCount = + itemCountCheck.data?.totalRecordCount === null + ? undefined + : itemCountCheck.data?.totalRecordCount; + return ( - - + + );