From 92cde507d912f671ffb444f588eab0503568e9a3 Mon Sep 17 00:00:00 2001 From: jeffvli Date: Fri, 13 Jan 2023 13:51:19 -0800 Subject: [PATCH] Add artist top songs list --- ...m-artist-detail-top-songs-list-content.tsx | 73 ++++++++++++++ ...um-artist-detail-top-songs-list-header.tsx | 96 +++++++++++++++++++ ...bum-artist-detail-top-songs-list-route.tsx | 46 +++++++++ src/renderer/router/app-router.tsx | 12 ++- 4 files changed, 225 insertions(+), 2 deletions(-) create mode 100644 src/renderer/features/artists/components/album-artist-detail-top-songs-list-content.tsx create mode 100644 src/renderer/features/artists/components/album-artist-detail-top-songs-list-header.tsx create mode 100644 src/renderer/features/artists/routes/album-artist-detail-top-songs-list-route.tsx diff --git a/src/renderer/features/artists/components/album-artist-detail-top-songs-list-content.tsx b/src/renderer/features/artists/components/album-artist-detail-top-songs-list-content.tsx new file mode 100644 index 00000000..b151fc90 --- /dev/null +++ b/src/renderer/features/artists/components/album-artist-detail-top-songs-list-content.tsx @@ -0,0 +1,73 @@ +import { MutableRefObject, useMemo } from 'react'; +import type { ColDef, RowDoubleClickedEvent } from '@ag-grid-community/core'; +import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact'; +import { getColumnDefs, VirtualGridAutoSizerContainer, VirtualTable } from '/@/renderer/components'; +import { useCurrentServer, useSongListStore } from '/@/renderer/store'; +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 { + data: QueueSong[]; + tableRef: MutableRefObject; +} + +export const AlbumArtistDetailTopSongsListContent = ({ + tableRef, + data, +}: AlbumArtistSongListContentProps) => { + const server = useCurrentServer(); + const page = useSongListStore(); + + const handlePlayQueueAdd = usePlayQueueAdd(); + const playButtonBehavior = usePlayButtonBehavior(); + + const columnDefs: ColDef[] = useMemo( + () => getColumnDefs(page.table.columns), + [page.table.columns], + ); + + 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.uniqueId} + rowBuffer={20} + rowData={data} + rowHeight={page.table.rowHeight || 40} + rowModelType="clientSide" + rowSelection="multiple" + onCellContextMenu={handleContextMenu} + onRowDoubleClicked={handleRowDoubleClick} + /> + + + ); +}; diff --git a/src/renderer/features/artists/components/album-artist-detail-top-songs-list-header.tsx b/src/renderer/features/artists/components/album-artist-detail-top-songs-list-header.tsx new file mode 100644 index 00000000..2e16c107 --- /dev/null +++ b/src/renderer/features/artists/components/album-artist-detail-top-songs-list-header.tsx @@ -0,0 +1,96 @@ +import { Flex, Group } from '@mantine/core'; +import { RiMoreFill } from 'react-icons/ri'; +import { QueueSong } from '/@/renderer/api/types'; +import { + Button, + DropdownMenu, + PageHeader, + TextTitle, + Badge, + SpinnerIcon, +} from '/@/renderer/components'; +import { usePlayQueueAdd } from '/@/renderer/features/player'; +import { useContainerQuery } from '/@/renderer/hooks'; +import { Play } from '/@/renderer/types'; + +interface AlbumArtistDetailTopSongsListHeaderProps { + data: QueueSong[]; + itemCount?: number; + title: string; +} + +export const AlbumArtistDetailTopSongsListHeader = ({ + title, + itemCount, + data, +}: AlbumArtistDetailTopSongsListHeaderProps) => { + const handlePlayQueueAdd = usePlayQueueAdd(); + const cq = useContainerQuery(); + + const handlePlay = async (play: Play) => { + handlePlayQueueAdd?.({ + byData: data, + play, + }); + }; + + return ( + + + + + + + + + + handlePlay(Play.NOW)}>Play + handlePlay(Play.LAST)}> + Add to queue + + handlePlay(Play.NEXT)}> + Add to queue next + + + + + + + ); +}; diff --git a/src/renderer/features/artists/routes/album-artist-detail-top-songs-list-route.tsx b/src/renderer/features/artists/routes/album-artist-detail-top-songs-list-route.tsx new file mode 100644 index 00000000..decbf1d1 --- /dev/null +++ b/src/renderer/features/artists/routes/album-artist-detail-top-songs-list-route.tsx @@ -0,0 +1,46 @@ +import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact'; +import { useRef } from 'react'; +import { useParams } from 'react-router'; +import { VirtualGridContainer } from '/@/renderer/components'; +import { AlbumArtistDetailTopSongsListContent } from '/@/renderer/features/artists/components/album-artist-detail-top-songs-list-content'; +import { AlbumArtistDetailTopSongsListHeader } from '/@/renderer/features/artists/components/album-artist-detail-top-songs-list-header'; +import { useAlbumArtistDetail } from '/@/renderer/features/artists/queries/album-artist-detail-query'; +import { useTopSongsList } from '/@/renderer/features/artists/queries/top-songs-list-query'; +import { AnimatedPage } from '/@/renderer/features/shared'; +import { useCurrentServer } from '/@/renderer/store'; +import { ServerType } from '/@/renderer/types'; + +const AlbumArtistDetailTopSongsListRoute = () => { + const tableRef = useRef(null); + const { albumArtistId } = useParams() as { albumArtistId: string }; + const server = useCurrentServer(); + + const detailQuery = useAlbumArtistDetail({ id: albumArtistId }); + + const topSongsQuery = useTopSongsList( + { artist: detailQuery?.data?.name || '' }, + { enabled: server?.type !== ServerType.JELLYFIN && !!detailQuery?.data?.name }, + ); + + const itemCount = topSongsQuery?.data?.items?.length || 0; + + if (detailQuery.isLoading || topSongsQuery?.isLoading) return null; + + return ( + + + + + + + ); +}; + +export default AlbumArtistDetailTopSongsListRoute; diff --git a/src/renderer/router/app-router.tsx b/src/renderer/router/app-router.tsx index b7c8bc3c..84f2aca6 100644 --- a/src/renderer/router/app-router.tsx +++ b/src/renderer/router/app-router.tsx @@ -3,7 +3,7 @@ import { Route, createRoutesFromElements, RouterProvider, - createHashRouter, + createBrowserRouter, } from 'react-router-dom'; import { AppRoute } from './routes'; import { DefaultLayout } from '/@/renderer/layouts'; @@ -52,6 +52,10 @@ const AlbumArtistDetailSongListRoute = lazy( () => import('../features/artists/routes/album-artist-detail-song-list-route'), ); +const AlbumArtistDetailTopSongsListRoute = lazy( + () => import('../features/artists/routes/album-artist-detail-top-songs-list-route'), +); + const AlbumArtistDetailDiscographyRoute = lazy( () => import('../features/artists/routes/album-artist-detail-discography-route'), ); @@ -65,7 +69,7 @@ const RouteErrorBoundary = lazy( ); export const AppRouter = () => { - const router = createHashRouter( + const router = createBrowserRouter( createRoutesFromElements( <> }> @@ -140,6 +144,10 @@ export const AppRouter = () => { element={} path={AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL_SONGS} /> + } + path={AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL_TOP_SONGS} + />