diff --git a/src/renderer/features/albums/components/album-list-content.tsx b/src/renderer/features/albums/components/album-list-content.tsx index 4bfced04..96ba4858 100644 --- a/src/renderer/features/albums/components/album-list-content.tsx +++ b/src/renderer/features/albums/components/album-list-content.tsx @@ -94,8 +94,6 @@ export const AlbumListContent = ({ }, }; - console.log('query', query); - const queryKey = queryKeys.albums.list(server?.id || '', query); const albumsRes = await queryClient.fetchQuery( diff --git a/src/renderer/features/artists/components/album-artist-detail-discography-detail-list.tsx b/src/renderer/features/artists/components/album-artist-detail-discography-detail-list.tsx deleted file mode 100644 index 96646995..00000000 --- a/src/renderer/features/artists/components/album-artist-detail-discography-detail-list.tsx +++ /dev/null @@ -1,198 +0,0 @@ -// import { useMemo, useRef } from 'react'; -// import { ColDef } from '@ag-grid-community/core'; -// import { Box, Group, Stack } from '@mantine/core'; -// import { useVirtualizer, Virtualizer } from '@tanstack/react-virtual'; -// import { useParams } from 'react-router'; -// import { AlbumListSort, SortOrder, SongListSort, Song } from '/@/renderer/api/types'; -// import { -// getColumnDefs, -// VirtualTable, -// Text, -// TextTitle, -// NativeScrollArea, -// } from '/@/renderer/components'; -// import { useAlbumList } from '/@/renderer/features/albums'; -// import { PlayButton } from '/@/renderer/features/shared'; -// import { useSongList } from '/@/renderer/features/songs'; -// import { useSongListStore } from '/@/renderer/store'; -// import { usePlayQueueAdd } from '/@/renderer/features/player'; -// import { Play } from '/@/renderer/types'; - -// const RowVirtualizer = ({ -// rows, -// columnDefs, -// handlePlay, -// rowVirtualizer, -// }: { -// columnDefs: ColDef[]; -// handlePlay: (play: Play, data: any[]) => void; -// rowVirtualizer: Virtualizer; -// rows: any[]; -// }) => { -// const items = rowVirtualizer.getVirtualItems(); - -// return ( -//
-// {items.map((virtualRow) => ( -//
-// -// -// {`${rows?.[virtualRow.index]?.name}-cover`} -// -// -// {rows?.[virtualRow.index]?.name} -// -// {rows?.[virtualRow.index]?.releaseYear} -// handlePlay?.(Play.NOW, rows?.[virtualRow.index]?.songs)} -// /> -// -// -// -// data.data.id} -// rowData={rows?.[virtualRow.index]?.songs} -// rowHeight={60} -// rowModelType="clientSide" -// rowSelection="multiple" -// /> -// -// -//
-// ))} -//
-// ); -// }; - -// export const AlbumArtistDiscographyDetailList = () => { -// const { albumArtistId } = useParams() as { albumArtistId: string }; -// // const albumArtistQuery = useAlbumArtistDetail({ id: albumArtistId }); - -// const albumsQuery = useAlbumList({ -// jfParams: { artistIds: albumArtistId }, -// ndParams: { artist_id: albumArtistId }, -// sortBy: AlbumListSort.YEAR, -// sortOrder: SortOrder.DESC, -// startIndex: 0, -// }); - -// const songsQuery = useSongList( -// { -// albumIds: albumsQuery.data?.items?.map((album) => album.id), -// sortBy: SongListSort.ALBUM, -// sortOrder: SortOrder.ASC, -// startIndex: 0, -// }, -// { -// enabled: !albumsQuery.isLoading, -// }, -// ); - -// const songsByAlbum = useMemo(() => { -// if (songsQuery.isLoading || albumsQuery.isLoading) return null; - -// const songsByAlbumMap = songsQuery?.data?.items?.reduce((acc, song) => { -// if (!acc[song.albumId as keyof typeof acc]) { -// acc[song.albumId as keyof typeof acc] = []; -// } - -// acc[song.albumId as keyof typeof acc].push(song); - -// return acc; -// }, {} as Record); - -// const albumDetailWithSongs = albumsQuery?.data?.items?.map((album) => { -// return { -// ...album, -// songs: songsByAlbumMap?.[album.id], -// }; -// }); - -// return albumDetailWithSongs; -// }, [ -// albumsQuery?.data?.items, -// albumsQuery?.isLoading, -// songsQuery?.data?.items, -// songsQuery?.isLoading, -// ]); - -// const page = useSongListStore(); - -// const columnDefs: ColDef[] = useMemo( -// () => -// getColumnDefs(page.table.columns).filter((c) => c.colId !== 'album' && c.colId !== 'artist'), -// [page.table.columns], -// ); - -// const handlePlayQueueAdd = usePlayQueueAdd(); - -// const parentRef = useRef(); - -// const rowVirtualizer = useVirtualizer({ -// count: songsByAlbum?.length || 0, -// estimateSize: (i) => (songsByAlbum?.[i].songs?.length || 0) * 60 + 300, -// getScrollElement: () => parentRef.current, -// overscan: 3, -// }); - -// const handlePlay = (play: Play, data: any[]) => { -// handlePlayQueueAdd?.({ -// byData: data, -// play, -// }); -// }; - -// if (albumsQuery.isLoading || songsQuery.isLoading) return null; - -// return ( -// -// {songsByAlbum && ( -// -// )} -// -// ); -// }; diff --git a/src/renderer/features/artists/components/album-artist-detail-discography-header.tsx b/src/renderer/features/artists/components/album-artist-detail-discography-header.tsx deleted file mode 100644 index 5a42e34e..00000000 --- a/src/renderer/features/artists/components/album-artist-detail-discography-header.tsx +++ /dev/null @@ -1,473 +0,0 @@ -import type { IDatasource } from '@ag-grid-community/core'; -import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact'; -import { Flex, Group, Stack } from '@mantine/core'; -import { ChangeEvent, MouseEvent, MutableRefObject, useCallback } from 'react'; -import { - RiArrowDownSLine, - RiFilter3Line, - RiFolder2Line, - RiMoreFill, - RiSortAsc, - RiSortDesc, -} from 'react-icons/ri'; -import { api } from '/@/renderer/api'; -import { queryKeys } from '/@/renderer/api/query-keys'; -import { - LibraryItem, - ServerType, - SongListQuery, - SongListSort, - SortOrder, -} from '/@/renderer/api/types'; -import { - Button, - DropdownMenu, - PageHeader, - Slider, - TextTitle, - Switch, - 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'; -import { useContainerQuery } from '/@/renderer/hooks'; -import { queryClient } from '/@/renderer/lib/react-query'; -import { - SongListFilter, - useCurrentServer, - useSetSongFilters, - useSetSongStore, - useSetSongTable, - useSetSongTablePagination, - useSongListStore, -} from '/@/renderer/store'; -import { ListDisplayType, Play, TableColumn } from '/@/renderer/types'; - -const FILTERS = { - jellyfin: [ - { defaultOrder: SortOrder.ASC, name: 'Album', value: SongListSort.ALBUM }, - { defaultOrder: SortOrder.ASC, name: 'Album Artist', value: SongListSort.ALBUM_ARTIST }, - { defaultOrder: SortOrder.ASC, name: 'Artist', value: SongListSort.ARTIST }, - { defaultOrder: SortOrder.ASC, name: 'Duration', value: SongListSort.DURATION }, - { defaultOrder: SortOrder.ASC, name: 'Most Played', value: SongListSort.PLAY_COUNT }, - { defaultOrder: SortOrder.ASC, name: 'Name', value: SongListSort.NAME }, - { defaultOrder: SortOrder.ASC, name: 'Random', value: SongListSort.RANDOM }, - { defaultOrder: SortOrder.ASC, name: 'Recently Added', value: SongListSort.RECENTLY_ADDED }, - { defaultOrder: SortOrder.ASC, name: 'Recently Played', value: SongListSort.RECENTLY_PLAYED }, - { defaultOrder: SortOrder.ASC, name: 'Release Date', value: SongListSort.RELEASE_DATE }, - ], - navidrome: [ - { defaultOrder: SortOrder.ASC, name: 'Album', value: SongListSort.ALBUM }, - { defaultOrder: SortOrder.ASC, name: 'Album Artist', value: SongListSort.ALBUM_ARTIST }, - { defaultOrder: SortOrder.ASC, name: 'Artist', value: SongListSort.ARTIST }, - { defaultOrder: SortOrder.DESC, name: 'BPM', value: SongListSort.BPM }, - { defaultOrder: SortOrder.ASC, name: 'Channels', value: SongListSort.CHANNELS }, - { defaultOrder: SortOrder.ASC, name: 'Comment', value: SongListSort.COMMENT }, - { defaultOrder: SortOrder.DESC, name: 'Duration', value: SongListSort.DURATION }, - { defaultOrder: SortOrder.DESC, name: 'Favorited', value: SongListSort.FAVORITED }, - { defaultOrder: SortOrder.ASC, name: 'Genre', value: SongListSort.GENRE }, - { defaultOrder: SortOrder.ASC, name: 'Name', value: SongListSort.NAME }, - { defaultOrder: SortOrder.DESC, name: 'Play Count', value: SongListSort.PLAY_COUNT }, - { defaultOrder: SortOrder.DESC, name: 'Rating', value: SongListSort.RATING }, - { defaultOrder: SortOrder.DESC, name: 'Recently Added', value: SongListSort.RECENTLY_ADDED }, - { defaultOrder: SortOrder.DESC, name: 'Recently Played', value: SongListSort.RECENTLY_PLAYED }, - { defaultOrder: SortOrder.DESC, name: 'Year', value: SongListSort.YEAR }, - ], -}; - -const ORDER = [ - { name: 'Ascending', value: SortOrder.ASC }, - { name: 'Descending', value: SortOrder.DESC }, -]; - -interface SongListHeaderProps { - itemCount?: number; - tableRef: MutableRefObject; -} - -export const AlbumArtistDiscographyHeader = ({ 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(); - - const sortByLabel = - (server?.type && - (FILTERS[server.type as keyof typeof FILTERS] as { name: string; value: string }[]).find( - (f) => f.value === page.filter.sortBy, - )?.name) || - 'Unknown'; - - const sortOrderLabel = ORDER.find((s) => s.value === page.filter.sortOrder)?.name; - - const handleFilterChange = useCallback( - async (filters?: SongListFilter) => { - const dataSource: IDatasource = { - getRows: async (params) => { - const limit = params.endRow - params.startRow; - const startIndex = params.startRow; - - const pageFilters = filters || page.filter; - - const queryKey = queryKeys.songs.list(server?.id || '', { - limit, - startIndex, - ...pageFilters, - }); - - const songsRes = await queryClient.fetchQuery( - queryKey, - async ({ signal }) => - api.controller.getSongList({ - query: { - limit, - startIndex, - ...pageFilters, - }, - server, - signal, - }), - { cacheTime: 1000 * 60 * 1 }, - ); - - const songs = api.normalize.songList(songsRes, server); - params.successCallback(songs?.items || [], songsRes?.totalRecordCount || 0); - }, - rowCount: undefined, - }; - tableRef.current?.api.setDatasource(dataSource); - tableRef.current?.api.purgeInfiniteCache(); - tableRef.current?.api.ensureIndexVisible(0, 'top'); - setPagination({ currentPage: 0 }); - }, - [page.filter, server, setPagination, tableRef], - ); - - const handleSetSortBy = useCallback( - (e: MouseEvent) => { - if (!e.currentTarget?.value || !server?.type) return; - - const sortOrder = FILTERS[server.type as keyof typeof FILTERS].find( - (f) => f.value === e.currentTarget.value, - )?.defaultOrder; - - const updatedFilters = setFilter({ - sortBy: e.currentTarget.value as SongListSort, - sortOrder: sortOrder || SortOrder.ASC, - }); - - handleFilterChange(updatedFilters); - }, - [handleFilterChange, server?.type, setFilter], - ); - - const handleSetMusicFolder = useCallback( - (e: MouseEvent) => { - if (!e.currentTarget?.value) return; - - let updatedFilters = null; - if (e.currentTarget.value === String(page.filter.musicFolderId)) { - updatedFilters = setFilter({ musicFolderId: undefined }); - } else { - updatedFilters = setFilter({ musicFolderId: e.currentTarget.value }); - } - - handleFilterChange(updatedFilters); - }, - [handleFilterChange, page.filter.musicFolderId, setFilter], - ); - - const handleToggleSortOrder = useCallback(() => { - const newSortOrder = page.filter.sortOrder === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC; - const updatedFilters = setFilter({ sortOrder: newSortOrder }); - handleFilterChange(updatedFilters); - }, [page.filter.sortOrder, handleFilterChange, setFilter]); - - const handleSetViewType = useCallback( - (e: MouseEvent) => { - if (!e.currentTarget?.value) return; - const display = e.currentTarget.value as ListDisplayType; - setPage({ list: { ...page, display: e.currentTarget.value as ListDisplayType } }); - - if (display === ListDisplayType.TABLE) { - tableRef.current?.api.paginationSetPageSize(tableRef.current.props.infiniteInitialRowCount); - setPagination({ currentPage: 0 }); - } else if (display === ListDisplayType.TABLE_PAGINATED) { - setPagination({ currentPage: 0 }); - } - }, - [page, setPage, setPagination, tableRef], - ); - - // const handleSearch = debounce((e: ChangeEvent) => { - // const previousSearchTerm = page.filter.searchTerm; - // const searchTerm = e.target.value === '' ? undefined : e.target.value; - // const updatedFilters = setFilter({ searchTerm }); - // if (previousSearchTerm !== searchTerm) handleFilterChange(updatedFilters); - // }, 500); - - const handleTableColumns = (values: TableColumn[]) => { - const existingColumns = page.table.columns; - - if (values.length === 0) { - return setTable({ - columns: [], - }); - } - - // If adding a column - if (values.length > existingColumns.length) { - const newColumn = { column: values[values.length - 1], width: 100 }; - - return setTable({ columns: [...existingColumns, newColumn] }); - } - - // If removing a column - const removed = existingColumns.filter((column) => !values.includes(column.column)); - const newColumns = existingColumns.filter((column) => !removed.includes(column)); - - return setTable({ columns: newColumns }); - }; - - const handleAutoFitColumns = (e: ChangeEvent) => { - setTable({ autoFit: e.currentTarget.checked }); - - if (e.currentTarget.checked) { - tableRef.current?.api.sizeColumnsToFit(); - } - }; - - const handleRowHeight = (e: number) => { - 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 ( - - - - - - - - - Display type - - Table - - - Table (paginated) - - - Item Size - - - - Table Columns - - - column.column)} - width={300} - onChange={handleTableColumns} - /> - - Auto Fit Columns - - - - - - - - - - - - {FILTERS[server?.type as keyof typeof FILTERS].map((filter) => ( - - {filter.name} - - ))} - - - - {server?.type === ServerType.JELLYFIN && ( - - - - - - {musicFoldersQuery.data?.map((folder) => ( - - {folder.name} - - ))} - - - )} - - - - - - {server?.type === ServerType.NAVIDROME ? ( - - ) : ( - - )} - - - - - - - - handlePlay(Play.NOW)}>Play - handlePlay(Play.LAST)}> - Add to queue - - handlePlay(Play.NEXT)}> - Add to queue next - - - Refresh - - - - - - ); -}; diff --git a/src/renderer/features/artists/components/album-artist-detail-song-list-content.tsx b/src/renderer/features/artists/components/album-artist-detail-song-list-content.tsx deleted file mode 100644 index 8816a5bb..00000000 --- a/src/renderer/features/artists/components/album-artist-detail-song-list-content.tsx +++ /dev/null @@ -1,213 +0,0 @@ -import { MutableRefObject, useCallback, useMemo } from 'react'; -import type { - ColDef, - GridReadyEvent, - IDatasource, - PaginationChangedEvent, - RowDoubleClickedEvent, -} from '@ag-grid-community/core'; -import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact'; -import { useSetState } from '@mantine/hooks'; -import { useQueryClient } from '@tanstack/react-query'; -import { api } from '/@/renderer/api'; -import { queryKeys } from '/@/renderer/api/query-keys'; -import { - getColumnDefs, - TablePagination, - VirtualGridAutoSizerContainer, - VirtualTable, -} from '/@/renderer/components'; -import { - SongListFilter, - useCurrentServer, - useSetSongTable, - useSongListStore, -} from '/@/renderer/store'; -import { ListDisplayType, TablePagination as TablePaginationType } from '/@/renderer/types'; -import { AnimatePresence } from 'framer-motion'; -import debounce from 'lodash/debounce'; -import { useHandleTableContextMenu } from '/@/renderer/features/context-menu'; -import { SONG_CONTEXT_MENU_ITEMS } from '/@/renderer/features/context-menu/context-menu-items'; -import { usePlayButtonBehavior } from '/@/renderer/store/settings.store'; -import { LibraryItem, QueueSong } from '/@/renderer/api/types'; -import { usePlayQueueAdd } from '/@/renderer/features/player'; - -interface AlbumArtistSongListContentProps { - filter: SongListFilter; - itemCount?: number; - tableRef: MutableRefObject; -} - -export const AlbumArtistDetailSongListContent = ({ - itemCount, - filter, - tableRef, -}: AlbumArtistSongListContentProps) => { - const queryClient = useQueryClient(); - const server = useCurrentServer(); - const page = useSongListStore(); - - const [pagination, setPagination] = useSetState({ - currentPage: 0, - itemsPerPage: 100, - totalItems: itemCount || 0, - totalPages: 0, - }); - - const setTable = useSetSongTable(); - const handlePlayQueueAdd = usePlayQueueAdd(); - const playButtonBehavior = usePlayButtonBehavior(); - - const isPaginationEnabled = page.display === ListDisplayType.TABLE_PAGINATED; - - const columnDefs: ColDef[] = useMemo( - () => getColumnDefs(page.table.columns), - [page.table.columns], - ); - - const onGridReady = useCallback( - (params: GridReadyEvent) => { - const dataSource: IDatasource = { - getRows: async (params) => { - const limit = params.endRow - params.startRow; - const startIndex = params.startRow; - - const queryKey = queryKeys.songs.list(server?.id || '', { - ...filter, - limit, - startIndex, - }); - - const songsRes = await queryClient.fetchQuery( - queryKey, - async ({ signal }) => - api.controller.getSongList({ - query: { - ...filter, - limit, - startIndex, - }, - server, - signal, - }), - { cacheTime: 1000 * 60 * 1 }, - ); - - const songs = api.normalize.songList(songsRes, server); - params.successCallback(songs?.items || [], songsRes?.totalRecordCount || 0); - }, - rowCount: undefined, - }; - params.api.setDatasource(dataSource); - }, - [filter, queryClient, server], - ); - - const onPaginationChanged = useCallback( - (event: PaginationChangedEvent) => { - if (!isPaginationEnabled || !event.api) return; - - // Scroll to top of page on pagination change - const currentPageStartIndex = pagination.currentPage * pagination.itemsPerPage; - event.api?.ensureIndexVisible(currentPageStartIndex, 'top'); - - setPagination({ - itemsPerPage: event.api.paginationGetPageSize(), - totalItems: event.api.paginationGetRowCount(), - totalPages: event.api.paginationGetTotalPages() + 1, - }); - }, - [isPaginationEnabled, pagination.currentPage, pagination.itemsPerPage, setPagination], - ); - - const handleColumnChange = useCallback(() => { - const { columnApi } = tableRef?.current || {}; - const columnsOrder = columnApi?.getAllGridColumns(); - - if (!columnsOrder) return; - - const columnsInSettings = page.table.columns; - const updatedColumns = []; - for (const column of columnsOrder) { - const columnInSettings = columnsInSettings.find((c) => c.column === column.getColDef().colId); - - if (columnInSettings) { - updatedColumns.push({ - ...columnInSettings, - ...(!page.table.autoFit && { - width: column.getActualWidth(), - }), - }); - } - } - - setTable({ columns: updatedColumns }); - }, [page.table.autoFit, page.table.columns, setTable, tableRef]); - - const debouncedColumnChange = debounce(handleColumnChange, 200); - - const handleContextMenu = useHandleTableContextMenu(LibraryItem.SONG, SONG_CONTEXT_MENU_ITEMS); - - const handleRowDoubleClick = (e: RowDoubleClickedEvent) => { - if (!e.data) return; - handlePlayQueueAdd?.({ - byData: [e.data], - play: playButtonBehavior, - }); - }; - - return ( - <> - - data.data.id} - infiniteInitialRowCount={itemCount || 100} - pagination={isPaginationEnabled} - paginationAutoPageSize={isPaginationEnabled} - paginationPageSize={page.table.pagination.itemsPerPage || 100} - rowBuffer={20} - rowHeight={page.table.rowHeight || 40} - rowModelType="infinite" - rowSelection="multiple" - onCellContextMenu={handleContextMenu} - onColumnMoved={handleColumnChange} - onColumnResized={debouncedColumnChange} - onGridReady={onGridReady} - onPaginationChanged={onPaginationChanged} - onRowDoubleClicked={handleRowDoubleClick} - /> - - - {page.display === ListDisplayType.TABLE_PAGINATED && ( - - )} - - - ); -}; diff --git a/src/renderer/features/artists/components/album-artist-detail-song-list-header.tsx b/src/renderer/features/artists/components/album-artist-detail-song-list-header.tsx deleted file mode 100644 index 3dd3c2f5..00000000 --- a/src/renderer/features/artists/components/album-artist-detail-song-list-header.tsx +++ /dev/null @@ -1,470 +0,0 @@ -import type { IDatasource } from '@ag-grid-community/core'; -import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact'; -import { Flex, Group, Stack } from '@mantine/core'; -import debounce from 'lodash/debounce'; -import { ChangeEvent, MouseEvent, MutableRefObject, useCallback } from 'react'; -import { RiArrowDownSLine, RiFolder2Line, RiMoreFill, RiSortAsc, RiSortDesc } from 'react-icons/ri'; -import { api } from '/@/renderer/api'; -import { queryKeys } from '/@/renderer/api/query-keys'; -import { - LibraryItem, - ServerType, - SongListQuery, - SongListSort, - SortOrder, -} from '/@/renderer/api/types'; -import { - Button, - DropdownMenu, - PageHeader, - SearchInput, - Slider, - TextTitle, - Switch, - MultiSelect, - Text, - SONG_TABLE_COLUMNS, - Badge, - SpinnerIcon, -} from '/@/renderer/components'; -import { usePlayQueueAdd } from '/@/renderer/features/player'; -import { useMusicFolders } from '/@/renderer/features/shared'; -import { useContainerQuery } from '/@/renderer/hooks'; -import { queryClient } from '/@/renderer/lib/react-query'; -import { - SongListFilter, - useCurrentServer, - useSetSongStore, - useSetSongTable, - useSetSongTablePagination, - useSongListStore, -} from '/@/renderer/store'; -import { ListDisplayType, Play, TableColumn } from '/@/renderer/types'; - -const FILTERS = { - jellyfin: [ - { defaultOrder: SortOrder.ASC, name: 'Album', value: SongListSort.ALBUM }, - { defaultOrder: SortOrder.ASC, name: 'Album Artist', value: SongListSort.ALBUM_ARTIST }, - { defaultOrder: SortOrder.ASC, name: 'Artist', value: SongListSort.ARTIST }, - { defaultOrder: SortOrder.ASC, name: 'Duration', value: SongListSort.DURATION }, - { defaultOrder: SortOrder.ASC, name: 'Most Played', value: SongListSort.PLAY_COUNT }, - { defaultOrder: SortOrder.ASC, name: 'Name', value: SongListSort.NAME }, - { defaultOrder: SortOrder.ASC, name: 'Random', value: SongListSort.RANDOM }, - { defaultOrder: SortOrder.ASC, name: 'Recently Added', value: SongListSort.RECENTLY_ADDED }, - { defaultOrder: SortOrder.ASC, name: 'Recently Played', value: SongListSort.RECENTLY_PLAYED }, - { defaultOrder: SortOrder.ASC, name: 'Release Date', value: SongListSort.RELEASE_DATE }, - ], - navidrome: [ - { defaultOrder: SortOrder.ASC, name: 'Album', value: SongListSort.ALBUM }, - { defaultOrder: SortOrder.ASC, name: 'Album Artist', value: SongListSort.ALBUM_ARTIST }, - { defaultOrder: SortOrder.ASC, name: 'Artist', value: SongListSort.ARTIST }, - { defaultOrder: SortOrder.DESC, name: 'BPM', value: SongListSort.BPM }, - { defaultOrder: SortOrder.ASC, name: 'Channels', value: SongListSort.CHANNELS }, - { defaultOrder: SortOrder.ASC, name: 'Comment', value: SongListSort.COMMENT }, - { defaultOrder: SortOrder.DESC, name: 'Duration', value: SongListSort.DURATION }, - { defaultOrder: SortOrder.DESC, name: 'Favorited', value: SongListSort.FAVORITED }, - { defaultOrder: SortOrder.ASC, name: 'Genre', value: SongListSort.GENRE }, - { defaultOrder: SortOrder.ASC, name: 'Name', value: SongListSort.NAME }, - { defaultOrder: SortOrder.DESC, name: 'Play Count', value: SongListSort.PLAY_COUNT }, - { defaultOrder: SortOrder.DESC, name: 'Rating', value: SongListSort.RATING }, - { defaultOrder: SortOrder.DESC, name: 'Recently Added', value: SongListSort.RECENTLY_ADDED }, - { defaultOrder: SortOrder.DESC, name: 'Recently Played', value: SongListSort.RECENTLY_PLAYED }, - { defaultOrder: SortOrder.DESC, name: 'Year', value: SongListSort.YEAR }, - ], -}; - -const ORDER = [ - { name: 'Ascending', value: SortOrder.ASC }, - { name: 'Descending', value: SortOrder.DESC }, -]; - -interface AlbumArtistDetailSongListHeaderProps { - filter: SongListFilter; - itemCount?: number; - setFilter: (filter: Partial) => void; - tableRef: MutableRefObject; - title: string; -} - -export const AlbumArtistDetailSongListHeader = ({ - filter, - setFilter, - title, - itemCount, - tableRef, -}: AlbumArtistDetailSongListHeaderProps) => { - const server = useCurrentServer(); - const page = useSongListStore(); - const setPage = useSetSongStore(); - const setTable = useSetSongTable(); - const setPagination = useSetSongTablePagination(); - const handlePlayQueueAdd = usePlayQueueAdd(); - const cq = useContainerQuery(); - - const musicFoldersQuery = useMusicFolders(); - - const sortByLabel = - (server?.type && - (FILTERS[server.type as keyof typeof FILTERS] as { name: string; value: string }[]).find( - (f) => f.value === filter.sortBy, - )?.name) || - 'Unknown'; - - const sortOrderLabel = ORDER.find((s) => s.value === filter.sortOrder)?.name; - - const handleFilterChange = useCallback( - async (filters?: SongListFilter) => { - const dataSource: IDatasource = { - getRows: async (params) => { - const limit = params.endRow - params.startRow; - const startIndex = params.startRow; - - const pageFilters = filters || filter; - - const queryKey = queryKeys.songs.list(server?.id || '', { - limit, - startIndex, - ...pageFilters, - }); - - const songsRes = await queryClient.fetchQuery( - queryKey, - async ({ signal }) => - api.controller.getSongList({ - query: { - limit, - startIndex, - ...pageFilters, - }, - server, - signal, - }), - { cacheTime: 1000 * 60 * 1 }, - ); - - const songs = api.normalize.songList(songsRes, server); - params.successCallback(songs?.items || [], songsRes?.totalRecordCount || 0); - }, - rowCount: undefined, - }; - tableRef.current?.api.setDatasource(dataSource); - tableRef.current?.api.purgeInfiniteCache(); - tableRef.current?.api.ensureIndexVisible(0, 'top'); - setPagination({ currentPage: 0 }); - }, - [filter, server, setPagination, tableRef], - ); - - const handleSetSortBy = useCallback( - (e: MouseEvent) => { - 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 SongListSort, - sortOrder: sortOrder || SortOrder.ASC, - }); - - handleFilterChange({ - ...filter, - sortBy: e.currentTarget.value as SongListSort, - sortOrder: sortOrder || SortOrder.ASC, - }); - }, - [filter, handleFilterChange, server?.type, setFilter], - ); - - const handleSetMusicFolder = useCallback( - (e: MouseEvent) => { - if (!e.currentTarget?.value) return; - - let updatedFilters = null; - if (e.currentTarget.value === String(page.filter.musicFolderId)) { - updatedFilters = { musicFolderId: undefined }; - setFilter(updatedFilters); - } else { - updatedFilters = { musicFolderId: e.currentTarget.value }; - setFilter(updatedFilters); - } - - handleFilterChange({ ...filter, ...updatedFilters }); - }, - [filter, handleFilterChange, page.filter.musicFolderId, setFilter], - ); - - const handleToggleSortOrder = useCallback(() => { - const newSortOrder = filter.sortOrder === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC; - setFilter({ sortOrder: newSortOrder }); - handleFilterChange({ ...filter, sortOrder: newSortOrder }); - }, [filter, handleFilterChange, setFilter]); - - const handleSetViewType = useCallback( - (e: MouseEvent) => { - if (!e.currentTarget?.value) return; - const display = e.currentTarget.value as ListDisplayType; - setPage({ list: { ...page, display: e.currentTarget.value as ListDisplayType } }); - - if (display === ListDisplayType.TABLE) { - tableRef.current?.api.paginationSetPageSize(tableRef.current.props.infiniteInitialRowCount); - setPagination({ currentPage: 0 }); - } else if (display === ListDisplayType.TABLE_PAGINATED) { - setPagination({ currentPage: 0 }); - } - }, - [page, setPage, setPagination, tableRef], - ); - - const handleSearch = debounce((e: ChangeEvent) => { - const previousSearchTerm = filter.searchTerm; - const searchTerm = e.target.value === '' ? undefined : e.target.value; - setFilter({ searchTerm }); - if (previousSearchTerm !== searchTerm) handleFilterChange({ ...filter, searchTerm }); - }, 500); - - const handleTableColumns = (values: TableColumn[]) => { - const existingColumns = page.table.columns; - - if (values.length === 0) { - return setTable({ - columns: [], - }); - } - - // If adding a column - if (values.length > existingColumns.length) { - const newColumn = { column: values[values.length - 1], width: 100 }; - - return setTable({ columns: [...existingColumns, newColumn] }); - } - - // If removing a column - const removed = existingColumns.filter((column) => !values.includes(column.column)); - const newColumns = existingColumns.filter((column) => !removed.includes(column)); - - return setTable({ columns: newColumns }); - }; - - const handleAutoFitColumns = (e: ChangeEvent) => { - setTable({ autoFit: e.currentTarget.checked }); - - if (e.currentTarget.checked) { - tableRef.current?.api.sizeColumnsToFit(); - } - }; - - const handleRowHeight = (e: number) => { - setTable({ rowHeight: e }); - }; - - const handleRefresh = () => { - queryClient.invalidateQueries(queryKeys.songs.list(server?.id || '')); - handleFilterChange(filter); - }; - - const handlePlay = async (play: Play) => { - const query: SongListQuery = { startIndex: 0, ...filter }; - - handlePlayQueueAdd?.({ - byItemType: { - id: query, - type: LibraryItem.SONG, - }, - play, - }); - }; - - return ( - - - - - - - - - Display type - - Table - - - Table (paginated) - - - Item Size - - - - Table Columns - - - column.column)} - width={300} - onChange={handleTableColumns} - /> - - Auto Fit Columns - - - - - - - - - - - - - {FILTERS[server?.type as keyof typeof FILTERS].map((filter) => ( - - {filter.name} - - ))} - - - - {server?.type === ServerType.JELLYFIN && ( - - - - - - {musicFoldersQuery.data?.map((folder) => ( - - {folder.name} - - ))} - - - )} - - - - - - handlePlay(Play.NOW)}>Play - handlePlay(Play.LAST)}> - Add to queue - - handlePlay(Play.NEXT)}> - Add to queue next - - - Refresh - - - - - - - - - ); -}; diff --git a/src/renderer/features/artists/components/album-artist-list-content.tsx b/src/renderer/features/artists/components/album-artist-list-content.tsx index 8cd1ea3b..11e163a5 100644 --- a/src/renderer/features/artists/components/album-artist-list-content.tsx +++ b/src/renderer/features/artists/components/album-artist-list-content.tsx @@ -19,10 +19,6 @@ import { useQueryClient } from '@tanstack/react-query'; import { useCurrentServer, useAlbumArtistListStore, - useAlbumArtistTablePagination, - useSetAlbumArtistStore, - useSetAlbumArtistTable, - useSetAlbumArtistTablePagination, useAlbumArtistListItemData, } from '/@/renderer/store'; import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact'; @@ -41,6 +37,8 @@ import { ALBUM_CONTEXT_MENU_ITEMS } from '/@/renderer/features/context-menu/cont import { generatePath, useNavigate } from 'react-router'; import { useAlbumArtistList } from '/@/renderer/features/artists/queries/album-artist-list-query'; import { usePlayQueueAdd } from '/@/renderer/features/player'; +import { useAlbumArtistListFilter, useListStoreActions } from '../../../store/list.store'; +import { useAlbumArtistListContext } from '/@/renderer/features/artists/context/album-artist-list-context'; interface AlbumArtistListContentProps { gridRef: MutableRefObject; @@ -51,23 +49,21 @@ export const AlbumArtistListContent = ({ gridRef, tableRef }: AlbumArtistListCon const queryClient = useQueryClient(); const navigate = useNavigate(); const server = useCurrentServer(); - const page = useAlbumArtistListStore(); - const setPage = useSetAlbumArtistStore(); const handlePlayQueueAdd = usePlayQueueAdd(); + const { id, pageKey } = useAlbumArtistListContext(); + const filter = useAlbumArtistListFilter({ id, key: pageKey }); + const { table, grid, display } = useAlbumArtistListStore(); + const { setTable, setTablePagination, setGrid } = useListStoreActions(); const { itemData, setItemData } = useAlbumArtistListItemData(); - const pagination = useAlbumArtistTablePagination(); - const setPagination = useSetAlbumArtistTablePagination(); - const setTable = useSetAlbumArtistTable(); - - const isPaginationEnabled = page.display === ListDisplayType.TABLE_PAGINATED; + const isPaginationEnabled = display === ListDisplayType.TABLE_PAGINATED; const checkAlbumArtistList = useAlbumArtistList( { limit: 1, startIndex: 0, - ...page.filter, + ...filter, }, { cacheTime: Infinity, @@ -75,10 +71,7 @@ export const AlbumArtistListContent = ({ gridRef, tableRef }: AlbumArtistListCon }, ); - const columnDefs: ColDef[] = useMemo( - () => getColumnDefs(page.table.columns), - [page.table.columns], - ); + const columnDefs: ColDef[] = useMemo(() => getColumnDefs(table.columns), [table.columns]); const onTableReady = useCallback( (params: GridReadyEvent) => { @@ -90,7 +83,7 @@ export const AlbumArtistListContent = ({ gridRef, tableRef }: AlbumArtistListCon const queryKey = queryKeys.albumArtists.list(server?.id || '', { limit, startIndex, - ...page.filter, + ...filter, }); const albumArtistsRes = await queryClient.fetchQuery( @@ -100,7 +93,7 @@ export const AlbumArtistListContent = ({ gridRef, tableRef }: AlbumArtistListCon query: { limit, startIndex, - ...page.filter, + ...filter, }, server, signal, @@ -115,9 +108,9 @@ export const AlbumArtistListContent = ({ gridRef, tableRef }: AlbumArtistListCon }; params.api.setDatasource(dataSource); - params.api.ensureIndexVisible(page.table.scrollOffset || 0, 'top'); + params.api.ensureIndexVisible(table.scrollOffset || 0, 'top'); }, - [page.filter, page.table.scrollOffset, queryClient, server], + [filter, table.scrollOffset, queryClient, server], ); const onTablePaginationChanged = useCallback( @@ -126,19 +119,28 @@ export const AlbumArtistListContent = ({ gridRef, tableRef }: AlbumArtistListCon try { // Scroll to top of page on pagination change - const currentPageStartIndex = pagination.currentPage * pagination.itemsPerPage; + const currentPageStartIndex = table.pagination.currentPage * table.pagination.itemsPerPage; event.api?.ensureIndexVisible(currentPageStartIndex, 'top'); } catch (err) { console.log(err); } - setPagination({ - itemsPerPage: event.api.paginationGetPageSize(), - totalItems: event.api.paginationGetRowCount(), - totalPages: event.api.paginationGetTotalPages() + 1, + setTablePagination({ + data: { + itemsPerPage: event.api.paginationGetPageSize(), + totalItems: event.api.paginationGetRowCount(), + totalPages: event.api.paginationGetTotalPages() + 1, + }, + key: pageKey, }); }, - [isPaginationEnabled, pagination.currentPage, pagination.itemsPerPage, setPagination], + [ + isPaginationEnabled, + pageKey, + setTablePagination, + table.pagination.currentPage, + table.pagination.itemsPerPage, + ], ); const handleTableColumnChange = useCallback(() => { @@ -147,7 +149,7 @@ export const AlbumArtistListContent = ({ gridRef, tableRef }: AlbumArtistListCon if (!columnsOrder) return; - const columnsInSettings = page.table.columns; + const columnsInSettings = table.columns; const updatedColumns = []; for (const column of columnsOrder) { const columnInSettings = columnsInSettings.find((c) => c.column === column.getColDef().colId); @@ -155,21 +157,21 @@ export const AlbumArtistListContent = ({ gridRef, tableRef }: AlbumArtistListCon if (columnInSettings) { updatedColumns.push({ ...columnInSettings, - ...(!page.table.autoFit && { + ...(!table.autoFit && { width: column.getColDef().width, }), }); } } - setTable({ columns: updatedColumns }); - }, [page.table.autoFit, page.table.columns, setTable, tableRef]); + setTable({ data: { columns: updatedColumns }, key: pageKey }); + }, [tableRef, table.columns, table.autoFit, setTable, pageKey]); const debouncedTableColumnChange = debounce(handleTableColumnChange, 200); const handleTableScroll = (e: BodyScrollEvent) => { - const scrollOffset = Number((e.top / page.table.rowHeight).toFixed(0)); - setTable({ scrollOffset }); + const scrollOffset = Number((e.top / table.rowHeight).toFixed(0)); + setTable({ data: { scrollOffset }, key: pageKey }); }; const fetch = useCallback( @@ -177,7 +179,7 @@ export const AlbumArtistListContent = ({ gridRef, tableRef }: AlbumArtistListCon const queryKey = queryKeys.albumArtists.list(server?.id || '', { limit, startIndex, - ...page.filter, + ...filter, }); const albumArtistsRes = await queryClient.fetchQuery( @@ -187,7 +189,7 @@ export const AlbumArtistListContent = ({ gridRef, tableRef }: AlbumArtistListCon query: { limit, startIndex, - ...page.filter, + ...filter, }, server, signal, @@ -197,26 +199,18 @@ export const AlbumArtistListContent = ({ gridRef, tableRef }: AlbumArtistListCon return api.normalize.albumArtistList(albumArtistsRes, server); }, - [page.filter, queryClient, server], + [filter, queryClient, server], ); const handleGridScroll = useCallback( (e: ListOnScrollProps) => { - setPage({ - list: { - ...page, - grid: { - ...page.grid, - scrollOffset: e.scrollOffset, - }, - }, - }); + setGrid({ data: { scrollOffset: e.scrollOffset }, key: pageKey }); }, - [page, setPage], + [pageKey, setGrid], ); const handleGridSizeChange = () => { - if (page.table.autoFit) { + if (table.autoFit) { tableRef?.current?.api.sizeColumnsToFit(); } }; @@ -224,7 +218,7 @@ export const AlbumArtistListContent = ({ gridRef, tableRef }: AlbumArtistListCon const cardRows = useMemo(() => { const rows: CardRow[] = [ALBUMARTIST_CARD_ROWS.name]; - switch (page.filter.sortBy) { + switch (filter.sortBy) { case AlbumArtistListSort.DURATION: rows.push(ALBUMARTIST_CARD_ROWS.duration); break; @@ -253,7 +247,7 @@ export const AlbumArtistListContent = ({ gridRef, tableRef }: AlbumArtistListCon } return rows; - }, [page.filter.sortBy]); + }, [filter.sortBy]); const handleContextMenu = useHandleTableContextMenu( LibraryItem.ALBUM_ARTIST, @@ -267,22 +261,22 @@ export const AlbumArtistListContent = ({ gridRef, tableRef }: AlbumArtistListCon return ( <> - {page.display === ListDisplayType.CARD || page.display === ListDisplayType.POSTER ? ( + {display === ListDisplayType.CARD || display === ListDisplayType.POSTER ? ( {({ height, width }) => ( data.data.id} infiniteInitialRowCount={checkAlbumArtistList.data?.totalRecordCount || 1} pagination={isPaginationEnabled} paginationAutoPageSize={isPaginationEnabled} - paginationPageSize={page.table.pagination.itemsPerPage || 100} - rowHeight={page.table.rowHeight || 40} + paginationPageSize={table.pagination.itemsPerPage || 100} + rowHeight={table.rowHeight || 40} rowModelType="infinite" onBodyScrollEnd={handleTableScroll} onCellContextMenu={handleContextMenu} @@ -330,10 +324,11 @@ export const AlbumArtistListContent = ({ gridRef, tableRef }: AlbumArtistListCon initial={false} mode="wait" > - {page.display === ListDisplayType.TABLE_PAGINATED && ( + {display === ListDisplayType.TABLE_PAGINATED && ( )} diff --git a/src/renderer/features/artists/components/album-artist-list-header-filters.tsx b/src/renderer/features/artists/components/album-artist-list-header-filters.tsx index a622c874..7beb7eec 100644 --- a/src/renderer/features/artists/components/album-artist-list-header-filters.tsx +++ b/src/renderer/features/artists/components/album-artist-list-header-filters.tsx @@ -28,14 +28,13 @@ import { useMusicFolders } from '/@/renderer/features/shared'; import { useContainerQuery } from '/@/renderer/hooks'; import { useCurrentServer, - useSetAlbumArtistStore, - useSetAlbumArtistFilters, useAlbumArtistListStore, - useSetAlbumArtistTablePagination, - useSetAlbumArtistTable, AlbumArtistListFilter, + useListStoreActions, + useAlbumArtistListFilter, } from '/@/renderer/store'; import { ListDisplayType, TableColumn, ServerType } from '/@/renderer/types'; +import { useAlbumArtistListContext } from '../context/album-artist-list-context'; const FILTERS = { jellyfin: [ @@ -76,32 +75,27 @@ export const AlbumArtistListHeaderFilters = ({ }: AlbumArtistListHeaderFiltersProps) => { const queryClient = useQueryClient(); const server = useCurrentServer(); - const setPage = useSetAlbumArtistStore(); - const setFilter = useSetAlbumArtistFilters(); - const page = useAlbumArtistListStore(); - const filters = page.filter; + const { pageKey } = useAlbumArtistListContext(); + const { display, table, grid } = useAlbumArtistListStore(); + const { setFilter, setTable, setTablePagination, setDisplayType, setGrid } = + useListStoreActions(); + const filter = useAlbumArtistListFilter({ key: pageKey }); const cq = useContainerQuery(); const musicFoldersQuery = useMusicFolders(); - const setPagination = useSetAlbumArtistTablePagination(); - const setTable = useSetAlbumArtistTable(); - const sortByLabel = (server?.type && - FILTERS[server.type as keyof typeof FILTERS].find((f) => f.value === filters.sortBy)?.name) || + FILTERS[server.type as keyof typeof FILTERS].find((f) => f.value === filter.sortBy)?.name) || 'Unknown'; - const sortOrderLabel = ORDER.find((o) => o.value === filters.sortOrder)?.name || 'Unknown'; + const sortOrderLabel = ORDER.find((o) => o.value === filter.sortOrder)?.name || 'Unknown'; const handleItemSize = (e: number) => { - if ( - page.display === ListDisplayType.TABLE || - page.display === ListDisplayType.TABLE_PAGINATED - ) { - setTable({ rowHeight: e }); + if (display === ListDisplayType.TABLE || display === ListDisplayType.TABLE_PAGINATED) { + setTable({ data: { rowHeight: e }, key: pageKey }); } else { - setPage({ list: { ...page, grid: { ...page.grid, size: e } } }); + setGrid({ data: { size: e }, key: pageKey }); } }; @@ -135,10 +129,7 @@ export const AlbumArtistListHeaderFilters = ({ const handleFilterChange = useCallback( async (filters: AlbumArtistListFilter) => { - if ( - page.display === ListDisplayType.TABLE || - page.display === ListDisplayType.TABLE_PAGINATED - ) { + if (display === ListDisplayType.TABLE || display === ListDisplayType.TABLE_PAGINATED) { const dataSource: IDatasource = { getRows: async (params) => { const limit = params.endRow - params.startRow; @@ -177,8 +168,8 @@ export const AlbumArtistListHeaderFilters = ({ tableRef.current?.api.purgeInfiniteCache(); tableRef.current?.api.ensureIndexVisible(0, 'top'); - if (page.display === ListDisplayType.TABLE_PAGINATED) { - setPagination({ currentPage: 0 }); + if (display === ListDisplayType.TABLE_PAGINATED) { + setTablePagination({ data: { currentPage: 0 }, key: pageKey }); } } else { gridRef.current?.scrollTo(0); @@ -193,7 +184,7 @@ export const AlbumArtistListHeaderFilters = ({ gridRef.current?.setItemData(data.items); } }, - [page.display, tableRef, setPagination, server, queryClient, gridRef, fetch], + [display, tableRef, server, queryClient, setTablePagination, pageKey, gridRef, fetch], ); const handleSetSortBy = useCallback( @@ -205,13 +196,16 @@ export const AlbumArtistListHeaderFilters = ({ )?.defaultOrder; const updatedFilters = setFilter({ - sortBy: e.currentTarget.value as AlbumArtistListSort, - sortOrder: sortOrder || SortOrder.ASC, - }); + data: { + sortBy: e.currentTarget.value as AlbumArtistListSort, + sortOrder: sortOrder || SortOrder.ASC, + }, + key: pageKey, + }) as AlbumArtistListFilter; handleFilterChange(updatedFilters); }, - [handleFilterChange, server?.type, setFilter], + [handleFilterChange, pageKey, server?.type, setFilter], ); const handleSetMusicFolder = useCallback( @@ -219,37 +213,50 @@ export const AlbumArtistListHeaderFilters = ({ if (!e.currentTarget?.value) return; let updatedFilters = null; - if (e.currentTarget.value === String(page.filter.musicFolderId)) { - updatedFilters = setFilter({ musicFolderId: undefined }); + if (e.currentTarget.value === String(filter.musicFolderId)) { + updatedFilters = setFilter({ + data: { musicFolderId: undefined }, + key: pageKey, + }) as AlbumArtistListFilter; } else { - updatedFilters = setFilter({ musicFolderId: e.currentTarget.value }); + updatedFilters = setFilter({ + data: { musicFolderId: e.currentTarget.value }, + key: pageKey, + }) as AlbumArtistListFilter; } handleFilterChange(updatedFilters); }, - [handleFilterChange, page.filter.musicFolderId, setFilter], + [filter.musicFolderId, handleFilterChange, setFilter, pageKey], ); const handleToggleSortOrder = useCallback(() => { - const newSortOrder = filters.sortOrder === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC; - const updatedFilters = setFilter({ sortOrder: newSortOrder }); + const newSortOrder = filter.sortOrder === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC; + const updatedFilters = setFilter({ + data: { sortOrder: newSortOrder }, + key: pageKey, + }) as AlbumArtistListFilter; handleFilterChange(updatedFilters); - }, [filters.sortOrder, handleFilterChange, setFilter]); + }, [filter.sortOrder, handleFilterChange, pageKey, setFilter]); const handleSetViewType = useCallback( (e: MouseEvent) => { if (!e.currentTarget?.value) return; - setPage({ list: { ...page, display: e.currentTarget.value as ListDisplayType } }); + + setDisplayType({ data: e.currentTarget.value as ListDisplayType, key: pageKey }); }, - [page, setPage], + [pageKey, setDisplayType], ); const handleTableColumns = (values: TableColumn[]) => { - const existingColumns = page.table.columns; + const existingColumns = table.columns; if (values.length === 0) { return setTable({ - columns: [], + data: { + columns: [], + }, + key: pageKey, }); } @@ -257,20 +264,20 @@ export const AlbumArtistListHeaderFilters = ({ if (values.length > existingColumns.length) { const newColumn = { column: values[values.length - 1], width: 100 }; - setTable({ columns: [...existingColumns, newColumn] }); + setTable({ data: { columns: [...existingColumns, newColumn] }, key: pageKey }); } else { // If removing a column const removed = existingColumns.filter((column) => !values.includes(column.column)); const newColumns = existingColumns.filter((column) => !removed.includes(column)); - setTable({ columns: newColumns }); + setTable({ data: { columns: newColumns }, key: pageKey }); } return tableRef.current?.api.sizeColumnsToFit(); }; const handleAutoFitColumns = (e: ChangeEvent) => { - setTable({ autoFit: e.currentTarget.checked }); + setTable({ data: { autoFit: e.currentTarget.checked }, key: pageKey }); if (e.currentTarget.checked) { tableRef.current?.api.sizeColumnsToFit(); @@ -279,8 +286,8 @@ export const AlbumArtistListHeaderFilters = ({ const handleRefresh = useCallback(() => { queryClient.invalidateQueries(queryKeys.albumArtists.list(server?.id || '')); - handleFilterChange(filters); - }, [filters, handleFilterChange, queryClient, server?.id]); + handleFilterChange(filter); + }, [filter, handleFilterChange, queryClient, server?.id]); return ( @@ -301,14 +308,14 @@ export const AlbumArtistListHeaderFilters = ({ - {FILTERS[server?.type as keyof typeof FILTERS].map((filter) => ( + {FILTERS[server?.type as keyof typeof FILTERS].map((f) => ( - {filter.name} + {f.name} ))} @@ -324,7 +331,7 @@ export const AlbumArtistListHeaderFilters = ({ sortOrderLabel ) : ( <> - {filters.sortOrder === SortOrder.ASC ? ( + {filter.sortOrder === SortOrder.ASC ? ( ) : ( @@ -348,7 +355,7 @@ export const AlbumArtistListHeaderFilters = ({ {musicFoldersQuery.data?.map((folder) => ( @@ -392,28 +399,28 @@ export const AlbumArtistListHeaderFilters = ({ Display type Card Poster Table @@ -424,9 +431,9 @@ export const AlbumArtistListHeaderFilters = ({ - {(page.display === ListDisplayType.TABLE || - page.display === ListDisplayType.TABLE_PAGINATED) && ( + {(display === ListDisplayType.TABLE || display === ListDisplayType.TABLE_PAGINATED) && ( <> Table Columns column.column)} + defaultValue={table?.columns.map((column) => column.column)} width={300} onChange={handleTableColumns} /> Auto Fit Columns diff --git a/src/renderer/features/artists/components/album-artist-list-header.tsx b/src/renderer/features/artists/components/album-artist-list-header.tsx index 75d344a2..86ca3473 100644 --- a/src/renderer/features/artists/components/album-artist-list-header.tsx +++ b/src/renderer/features/artists/components/album-artist-list-header.tsx @@ -20,11 +20,11 @@ import { AlbumArtistListFilter, useAlbumArtistListStore, useCurrentServer, - useSetAlbumArtistFilters, - useSetAlbumArtistTablePagination, + useListStoreActions, } from '/@/renderer/store'; import { ListDisplayType } from '/@/renderer/types'; import { AlbumArtistListHeaderFilters } from '/@/renderer/features/artists/components/album-artist-list-header-filters'; +import { useAlbumArtistListContext } from '/@/renderer/features/artists/context/album-artist-list-context'; interface AlbumArtistListHeaderProps { gridRef: MutableRefObject; @@ -39,12 +39,11 @@ export const AlbumArtistListHeader = ({ }: AlbumArtistListHeaderProps) => { const queryClient = useQueryClient(); const server = useCurrentServer(); - const setFilter = useSetAlbumArtistFilters(); - const page = useAlbumArtistListStore(); + const { pageKey } = useAlbumArtistListContext(); + const { display, filter } = useAlbumArtistListStore(); + const { setFilter, setTablePagination } = useListStoreActions(); const cq = useContainerQuery(); - const setPagination = useSetAlbumArtistTablePagination(); - const fetch = useCallback( async (startIndex: number, limit: number, filters: AlbumArtistListFilter) => { const queryKey = queryKeys.albumArtists.list(server?.id || '', { @@ -75,10 +74,7 @@ export const AlbumArtistListHeader = ({ const handleFilterChange = useCallback( async (filters: AlbumArtistListFilter) => { - if ( - page.display === ListDisplayType.TABLE || - page.display === ListDisplayType.TABLE_PAGINATED - ) { + if (display === ListDisplayType.TABLE || display === ListDisplayType.TABLE_PAGINATED) { const dataSource: IDatasource = { getRows: async (params) => { const limit = params.endRow - params.startRow; @@ -117,8 +113,8 @@ export const AlbumArtistListHeader = ({ tableRef.current?.api.purgeInfiniteCache(); tableRef.current?.api.ensureIndexVisible(0, 'top'); - if (page.display === ListDisplayType.TABLE_PAGINATED) { - setPagination({ currentPage: 0 }); + if (display === ListDisplayType.TABLE_PAGINATED) { + setTablePagination({ data: { currentPage: 0 }, key: pageKey }); } } else { gridRef.current?.scrollTo(0); @@ -133,13 +129,16 @@ export const AlbumArtistListHeader = ({ gridRef.current?.setItemData(data.items); } }, - [page.display, tableRef, setPagination, server, queryClient, gridRef, fetch], + [display, tableRef, server, queryClient, setTablePagination, pageKey, gridRef, fetch], ); const handleSearch = debounce((e: ChangeEvent) => { - const previousSearchTerm = page.filter.searchTerm; + const previousSearchTerm = filter.searchTerm; const searchTerm = e.target.value === '' ? undefined : e.target.value; - const updatedFilters = setFilter({ searchTerm }); + const updatedFilters = setFilter({ + data: { searchTerm }, + key: pageKey, + }) as AlbumArtistListFilter; if (previousSearchTerm !== searchTerm) handleFilterChange(updatedFilters); }, 500); @@ -166,7 +165,7 @@ export const AlbumArtistListHeader = ({ diff --git a/src/renderer/features/artists/context/album-artist-detail-song-list-context.tsx b/src/renderer/features/artists/context/album-artist-detail-song-list-context.tsx new file mode 100644 index 00000000..cf246213 --- /dev/null +++ b/src/renderer/features/artists/context/album-artist-detail-song-list-context.tsx @@ -0,0 +1,11 @@ +import { createContext, useContext } from 'react'; +import { ListKey } from '/@/renderer/store'; + +export const AlbumArtistDetailSongListContext = createContext<{ id?: string; pageKey: ListKey }>({ + pageKey: 'albumArtist', +}); + +export const useAlbumArtistDetailSongListContext = () => { + const ctxValue = useContext(AlbumArtistDetailSongListContext); + return ctxValue; +}; diff --git a/src/renderer/features/artists/context/album-artist-list-context.tsx b/src/renderer/features/artists/context/album-artist-list-context.tsx new file mode 100644 index 00000000..ef5be66d --- /dev/null +++ b/src/renderer/features/artists/context/album-artist-list-context.tsx @@ -0,0 +1,11 @@ +import { createContext, useContext } from 'react'; +import { ListKey } from '/@/renderer/store'; + +export const AlbumArtistListContext = createContext<{ id?: string; pageKey: ListKey }>({ + pageKey: 'albumArtist', +}); + +export const useAlbumArtistListContext = () => { + const ctxValue = useContext(AlbumArtistListContext); + return ctxValue; +}; diff --git a/src/renderer/features/artists/routes/album-artist-detail-discography-route.tsx b/src/renderer/features/artists/routes/album-artist-detail-discography-route.tsx deleted file mode 100644 index 96d5c45c..00000000 --- a/src/renderer/features/artists/routes/album-artist-detail-discography-route.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { useParams } from 'react-router'; -import { AlbumListSort, SortOrder, SongListSort } from '/@/renderer/api/types'; -import { VirtualGridContainer } from '/@/renderer/components'; -import { useAlbumList } from '/@/renderer/features/albums'; -import { AnimatedPage } from '/@/renderer/features/shared'; -import { useSongList } from '/@/renderer/features/songs'; -// import { useSongListStore } from '/@/renderer/store'; -// import { usePlayQueueAdd } from '/@/renderer/features/player'; -// import { Play } from '/@/renderer/types'; -// import { useAlbumArtistDetail } from '/@/renderer/features/artists/queries/album-artist-detail-query'; - -const AlbumArtistDetailDiscographyRoute = () => { - const { albumArtistId } = useParams() as { albumArtistId: string }; - // const albumArtistQuery = useAlbumArtistDetail({ id: albumArtistId }); - - const albumsQuery = useAlbumList({ - jfParams: { artistIds: albumArtistId }, - ndParams: { artist_id: albumArtistId }, - sortBy: AlbumListSort.YEAR, - sortOrder: SortOrder.DESC, - startIndex: 0, - }); - - const songsQuery = useSongList( - { - albumIds: albumsQuery.data?.items?.map((album) => album.id), - sortBy: SongListSort.ALBUM, - sortOrder: SortOrder.ASC, - startIndex: 0, - }, - { - enabled: !albumsQuery.isLoading, - }, - ); - - // const page = useSongListStore(); - - // const handlePlayQueueAdd = usePlayQueueAdd(); - - if (albumsQuery.isLoading || songsQuery.isLoading) return null; - - // const handlePlay = (play: Play, data: any[]) => { - // handlePlayQueueAdd?.({ - // byData: data, - // play, - // }); - // }; - - return ( - - - {/* - - - {albumArtistQuery?.data?.name || ''} - - - - - - * - */} - - - ); -}; - -export default AlbumArtistDetailDiscographyRoute; diff --git a/src/renderer/features/artists/routes/album-artist-detail-song-list-route.tsx b/src/renderer/features/artists/routes/album-artist-detail-song-list-route.tsx deleted file mode 100644 index c4be82aa..00000000 --- a/src/renderer/features/artists/routes/album-artist-detail-song-list-route.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact'; -import { useSetState } from '@mantine/hooks'; -import { useRef } from 'react'; -import { useParams } from 'react-router'; -import { SongListSort, SortOrder } from '/@/renderer/api/types'; -import { AlbumArtistDetailSongListContent } from '/@/renderer/features/artists/components/album-artist-detail-song-list-content'; -import { AlbumArtistDetailSongListHeader } from '/@/renderer/features/artists/components/album-artist-detail-song-list-header'; -import { useAlbumArtistDetail } from '/@/renderer/features/artists/queries/album-artist-detail-query'; -import { AnimatedPage } from '/@/renderer/features/shared'; -import { useSongList } from '/@/renderer/features/songs'; -import { SongListFilter } from '/@/renderer/store'; - -const AlbumArtistDetailSongListRoute = () => { - const tableRef = useRef(null); - const { albumArtistId } = useParams() as { albumArtistId: string }; - - const detailQuery = useAlbumArtistDetail({ id: albumArtistId }); - - const [filter, setFilter] = useSetState({ - artistIds: [albumArtistId], - sortBy: SongListSort.ALBUM, - sortOrder: SortOrder.ASC, - }); - - const itemCountCheck = useSongList( - { - limit: 1, - startIndex: 0, - ...filter, - }, - { - cacheTime: 1000 * 60 * 60 * 2, - staleTime: 1000 * 60 * 60 * 2, - }, - ); - - const itemCount = - itemCountCheck.data?.totalRecordCount === null - ? undefined - : itemCountCheck.data?.totalRecordCount; - - if (detailQuery.isLoading) return null; - - return ( - - - - - ); -}; - -export default AlbumArtistDetailSongListRoute; diff --git a/src/renderer/features/artists/routes/album-artist-list-route.tsx b/src/renderer/features/artists/routes/album-artist-list-route.tsx index bc0b1c67..7d56b955 100644 --- a/src/renderer/features/artists/routes/album-artist-list-route.tsx +++ b/src/renderer/features/artists/routes/album-artist-list-route.tsx @@ -5,23 +5,25 @@ import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/li import { useRef } from 'react'; import { AlbumArtistListContent } from '/@/renderer/features/artists/components/album-artist-list-content'; import { useAlbumArtistList } from '/@/renderer/features/artists/queries/album-artist-list-query'; -import { useAlbumArtistListStore } from '/@/renderer/store'; +import { generatePageKey, useAlbumArtistListFilter } from '/@/renderer/store'; +import { AlbumArtistListContext } from '/@/renderer/features/artists/context/album-artist-list-context'; const AlbumArtistListRoute = () => { const gridRef = useRef(null); const tableRef = useRef(null); - const page = useAlbumArtistListStore(); - const filters = page.filter; + const pageKey = generatePageKey('albumArtist', undefined); + + const albumArtistListFilter = useAlbumArtistListFilter({ id: undefined, key: pageKey }); const itemCountCheck = useAlbumArtistList( { limit: 1, startIndex: 0, - ...filters, + ...albumArtistListFilter, }, { - cacheTime: 1000 * 60 * 60 * 2, - staleTime: 1000 * 60 * 60 * 2, + cacheTime: 1000 * 60, + staleTime: 1000 * 60, }, ); @@ -32,15 +34,17 @@ const AlbumArtistListRoute = () => { return ( - - + + + + ); }; diff --git a/src/renderer/store/list.store.ts b/src/renderer/store/list.store.ts index 29fef745..d1c96a27 100644 --- a/src/renderer/store/list.store.ts +++ b/src/renderer/store/list.store.ts @@ -476,4 +476,9 @@ export const useAlbumListFilter = (args: { id?: string; key?: string }) => return state._actions.getFilter({ id: args.id, key: args.key }) as AlbumListFilter; }, shallow); +export const useAlbumArtistListFilter = (args: { id?: string; key?: string }) => + useListStore((state) => { + return state._actions.getFilter({ id: args.id, key: args.key }) as AlbumArtistListFilter; + }, shallow); + export const useListDetail = (key: string) => useListStore((state) => state.detail[key], shallow);