import { useQuery } from '@tanstack/react-query'; import { forwardRef, Fragment, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Link, useParams } from 'react-router'; import styles from './album-detail-header.module.css'; import { albumQueries } from '/@/renderer/features/albums/api/album-api'; import { JoinedArtists } from '/@/renderer/features/albums/components/joined-artists'; import { ContextMenuController } from '/@/renderer/features/context-menu/context-menu-controller'; import { isMbzAlbumId } from '/@/renderer/features/musicbrainz/utils'; 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 { useCurrentServerId, useShowRatings } from '/@/renderer/store'; import { usePlayButtonBehavior } from '/@/renderer/store/settings.store'; import { formatDateAbsoluteUTC, formatDurationString } from '/@/renderer/utils'; import { normalizeReleaseTypes } from '/@/renderer/utils/normalize-release-types'; import { Group } from '/@/shared/components/group/group'; import { Separator } from '/@/shared/components/separator/separator'; import { Stack } from '/@/shared/components/stack/stack'; import { Text } from '/@/shared/components/text/text'; import { LibraryItem, ServerType } from '/@/shared/types/domain-types'; import { Play } from '/@/shared/types/types'; export const AlbumDetailHeader = forwardRef((_props, ref) => { const { albumId } = useParams() as { albumId: string }; const { t } = useTranslation(); const serverId = useCurrentServerId(); const showRatings = useShowRatings(); const isMbz = isMbzAlbumId(albumId); const detailQuery = useQuery( albumQueries.detail({ query: { id: albumId }, serverId: isMbz ? 'musicbrainz' : serverId, }), ); const isExternal = detailQuery?.data?._serverType === ServerType.EXTERNAL; const showRating = !isExternal && showRatings && (detailQuery?.data?._serverType === ServerType.NAVIDROME || detailQuery?.data?._serverType === ServerType.SUBSONIC); const { addToQueueByFetch } = usePlayer(); const playButtonBehavior = usePlayButtonBehavior(); const setRating = useSetRating(); const setFavorite = useSetFavorite(); const handleFavorite = () => { if (!detailQuery?.data) return; setFavorite( detailQuery.data._serverId, [detailQuery.data.id], LibraryItem.ALBUM, !detailQuery.data.userFavorite, ); }; const handleUpdateRating = showRating ? (rating: number) => { if (!detailQuery?.data) return; if (detailQuery.data.userRating === rating) { return setRating( detailQuery.data._serverId, [detailQuery.data.id], LibraryItem.ALBUM, 0, ); } return setRating( detailQuery.data._serverId, [detailQuery.data.id], LibraryItem.ALBUM, rating, ); } : undefined; const handlePlay = (type?: Play) => { if (isExternal || !serverId || !albumId) return; addToQueueByFetch(serverId, [albumId], LibraryItem.ALBUM, type || playButtonBehavior); }; const handleMoreOptions = (e: React.MouseEvent) => { if (!detailQuery?.data) return; ContextMenuController.call({ cmd: { items: [detailQuery.data], type: LibraryItem.ALBUM }, event: e, }); }; const releaseYear = detailQuery?.data?.releaseYear; const releaseDate = detailQuery?.data?.releaseDate; const metadataItems = useMemo(() => { const items: Array<{ id: string; value: React.ReactNode | string | undefined }> = []; const album = detailQuery?.data; if (!album) return []; const originalDifferentFromRelease = album?.originalDate && album?.originalDate !== album?.releaseDate; const originalYearDifferentFromRelease = album?.originalYear !== album?.releaseYear; const playCount = album?.playCount; const releasePrefix = originalDifferentFromRelease ? t('page.albumDetail.released', { postProcess: 'sentenceCase' }) : '♫'; const releaseYearPrefix = originalYearDifferentFromRelease ? t('page.albumDetail.released', { postProcess: 'sentenceCase' }) : '♫'; if (album.originalDate) { if (originalDifferentFromRelease) { items.push({ id: 'originalDate', value: `♫ ${formatDateAbsoluteUTC(album.originalDate)}`, }); } if (releaseDate) { items.push({ id: 'releaseDate', value: `${releasePrefix} ${formatDateAbsoluteUTC(releaseDate)}`, }); } } else if (album.originalYear) { if (originalYearDifferentFromRelease) { items.push({ id: 'originalYear', value: `♫ ${album.originalYear}`, }); } if (releaseDate) { items.push({ id: 'releaseDate', value: `${releaseYearPrefix} ${formatDateAbsoluteUTC(releaseDate)}`, }); } else if (releaseYear) { items.push({ id: 'releaseYear', value: `${releaseYearPrefix} ${releaseYear}`, }); } } items.push( ...[ { id: 'songCount', value: t('entity.trackWithCount', { count: detailQuery?.data?.songCount || 0 }), }, { id: 'duration', value: formatDurationString(detailQuery?.data?.duration || 0), }, { id: 'explicitStatus', value: detailQuery?.data?.explicitStatus, }, { id: 'playCount', value: playCount ? t('entity.play', { count: playCount }) : undefined, }, ], ); return items.filter((item) => !!item.value); }, [detailQuery?.data, releaseDate, releaseYear, t]); const headerItem = useMemo(() => { const album = detailQuery?.data; if (!album) return null; const releaseTypes = album.releaseType ? normalizeReleaseTypes([album.releaseType], t) : null; const releaseTypeText = releaseTypes?.length ? releaseTypes[0] : null; if (releaseTypeText) { return ( {releaseTypeText} {album.version && ( <> {album.version} )} ); } return null; }, [detailQuery?.data, t]); return ( {metadataItems.map((item, index) => ( {index > 0 && ( )} {item.value} ))} handlePlay(type)} onRating={handleUpdateRating} onShuffle={() => handlePlay(Play.SHUFFLE)} rating={detailQuery?.data?.userRating || 0} /> ); });