import { useQuery, useSuspenseQuery, UseSuspenseQueryResult } from '@tanstack/react-query'; import { forwardRef, Fragment, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useParams } from 'react-router'; import styles from './album-artist-detail-header.module.css'; import { useItemImageUrl } from '/@/renderer/components/item-image/item-image'; import { artistsQueries } from '/@/renderer/features/artists/api/artists-api'; import { getArtistAlbumsGrouped } from '/@/renderer/features/artists/hooks/use-artist-albums-grouped'; import { ContextMenuController } from '/@/renderer/features/context-menu/context-menu-controller'; import { usePlayer } from '/@/renderer/features/player/context/player-context'; import { LibraryHeader, LibraryHeaderMenu, } from '/@/renderer/features/shared/components/library-header'; import { useSetFavorite } from '/@/renderer/features/shared/hooks/use-set-favorite'; import { useSetRating } from '/@/renderer/features/shared/hooks/use-set-rating'; import { AppRoute } from '/@/renderer/router/routes'; import { useAppStore, useCurrentServer, useShowRatings } from '/@/renderer/store'; import { useArtistReleaseTypeItems, usePlayButtonBehavior } from '/@/renderer/store/settings.store'; import { formatDurationString } from '/@/renderer/utils'; import { SEPARATOR_STRING, sortAlbumList } from '/@/shared/api/utils'; import { Group } from '/@/shared/components/group/group'; import { Stack } from '/@/shared/components/stack/stack'; import { Text } from '/@/shared/components/text/text'; import { AlbumListResponse, LibraryItem, ServerType } from '/@/shared/types/domain-types'; import { Play } from '/@/shared/types/types'; interface AlbumArtistDetailHeaderProps { albumsQuery: UseSuspenseQueryResult; } export const AlbumArtistDetailHeader = forwardRef( ({ albumsQuery }, ref) => { const { albumArtistId, artistId } = useParams() as { albumArtistId?: string; artistId?: string; }; const routeId = (artistId || albumArtistId) as string; const server = useCurrentServer(); const showRatings = useShowRatings(); const { t } = useTranslation(); const detailQuery = useSuspenseQuery( artistsQueries.albumArtistDetail({ query: { id: routeId }, serverId: server?.id, }), ); const albumCount = detailQuery.data?.albumCount; const songCount = detailQuery.data?.songCount; const duration = detailQuery.data?.duration; const durationEnabled = duration !== null && duration !== undefined; const metadataItems = [ { enabled: albumCount !== null && albumCount !== undefined, id: 'albumCount', secondary: false, value: t('entity.albumWithCount', { count: albumCount || 0 }), }, { enabled: songCount !== null && songCount !== undefined, id: 'songCount', secondary: false, value: t('entity.trackWithCount', { count: songCount || 0 }), }, { enabled: durationEnabled, id: 'duration', secondary: true, value: durationEnabled && formatDurationString(duration), }, ]; const { addToQueueByFetch } = usePlayer(); const playButtonBehavior = usePlayButtonBehavior(); const setFavorite = useSetFavorite(); const setRating = useSetRating(); const albumArtistDetailSort = useAppStore((state) => state.albumArtistDetailSort); const sortBy = albumArtistDetailSort.sortBy; const sortOrder = albumArtistDetailSort.sortOrder; const groupingType = albumArtistDetailSort.groupingType; const artistReleaseTypeItems = useArtistReleaseTypeItems(); const handlePlay = useCallback( (type?: Play) => { if (!server?.id || !routeId) return; const albums = albumsQuery.data?.items || []; const sortedAlbums = sortAlbumList(albums, sortBy, sortOrder); const { flatSortedAlbums } = getArtistAlbumsGrouped( sortedAlbums, routeId, groupingType, artistReleaseTypeItems, t, ); const albumIds = flatSortedAlbums.map((album) => album.id); if (albumIds.length === 0) return; addToQueueByFetch( server.id, albumIds, LibraryItem.ALBUM, type || playButtonBehavior, ); }, [ addToQueueByFetch, playButtonBehavior, routeId, server.id, albumsQuery.data?.items, sortBy, sortOrder, groupingType, artistReleaseTypeItems, t, ], ); const handleFavorite = useCallback(() => { if (!detailQuery.data) return; setFavorite( detailQuery.data._serverId, [detailQuery.data.id], LibraryItem.ALBUM_ARTIST, !detailQuery.data.userFavorite, ); }, [detailQuery.data, setFavorite]); const handleUpdateRating = useCallback( (rating: number) => { if (!detailQuery.data) return; if (detailQuery.data.userRating === rating) { return setRating( detailQuery.data._serverId, [detailQuery.data.id], LibraryItem.ALBUM_ARTIST, 0, ); } return setRating( detailQuery.data._serverId, [detailQuery.data.id], LibraryItem.ALBUM_ARTIST, rating, ); }, [detailQuery.data, setRating], ); const handleMoreOptions = useCallback( (e: React.MouseEvent) => { if (!detailQuery.data) return; ContextMenuController.call({ cmd: { items: [detailQuery.data], type: LibraryItem.ALBUM_ARTIST }, event: e, }); }, [detailQuery.data], ); const imageUrl = useItemImageUrl({ id: detailQuery.data?.imageId || undefined, imageUrl: detailQuery.data?.imageUrl, itemType: LibraryItem.ALBUM_ARTIST, type: 'itemCard', }); const artistInfoQuery = useQuery({ ...artistsQueries.albumArtistInfo({ query: { id: routeId, limit: 10 }, serverId: server?.id, }), enabled: Boolean(server?.id && routeId), }); const showRating = showRatings && detailQuery?.data?._serverType === ServerType.NAVIDROME; const selectedImageUrl = useMemo(() => { return detailQuery.data?.imageUrl || imageUrl; }, [detailQuery.data?.imageUrl, imageUrl]); const alternateImageUrl = artistInfoQuery.data?.imageUrl; return ( {metadataItems .filter((i) => i.enabled) .map((item, index) => ( {index > 0 && ( {SEPARATOR_STRING} )} {item.value} ))} handlePlay(type)} onRating={showRating ? handleUpdateRating : undefined} onShuffle={() => handlePlay(Play.SHUFFLE)} rating={detailQuery.data?.userRating || 0} /> ); }, );