import dayjs from 'dayjs' import { NavLink, useParams } from 'react-router-dom' import Button, { Color as ButtonColor } from '@/web/components/Button' import CoverRow, { Subtitle } from '@/web/components/CoverRow' import Skeleton from '@/web/components/Skeleton' import Icon from '@/web/components/Icon' import TracksAlbum from '@/web/components/TracksAlbum' import useAlbum from '@/web/api/hooks/useAlbum' import useArtistAlbums from '@/web/api/hooks/useArtistAlbums' import { player } from '@/web/store' import { Mode as PlayerMode, State as PlayerState, TrackListSourceType, } from '@/web/utils/player' import { formatDate, formatDuration, resizeImage, scrollToTop, } from '@/web/utils/common' import useTracks from '@/web/api/hooks/useTracks' import useUserAlbums, { useMutationLikeAAlbum, } from '@/web/api/hooks/useUserAlbums' import { useMemo, useState } from 'react' import toast from 'react-hot-toast' import { useSnapshot } from 'valtio' const PlayButton = ({ album, handlePlay, isLoading, }: { album: Album | undefined handlePlay: () => void isLoading: boolean }) => { const playerSnapshot = useSnapshot(player) const isThisAlbumPlaying = useMemo( () => playerSnapshot.mode === PlayerMode.TrackList && playerSnapshot.trackListSource?.type === TrackListSourceType.Album && playerSnapshot.trackListSource?.id === album?.id, [ playerSnapshot.mode, playerSnapshot.trackListSource?.type, playerSnapshot.trackListSource?.id, album?.id, ] ) const isPlaying = isThisAlbumPlaying && [PlayerState.Playing, PlayerState.Loading].includes(playerSnapshot.state) const wrappedHandlePlay = () => { if (isThisAlbumPlaying) { player.playOrPause() } else { handlePlay() } } return ( ) } const Header = ({ album, isLoading, handlePlay, }: { album: Album | undefined isLoading: boolean handlePlay: () => void }) => { const coverUrl = resizeImage(album?.picUrl || '', 'lg') const albumDuration = useMemo(() => { const duration = album?.songs?.reduce((acc, cur) => acc + cur.dt, 0) || 0 return formatDuration(duration, 'zh-CN', 'hh[hr] mm[min]') }, [album?.songs]) const [isCoverError, setCoverError] = useState( coverUrl.includes('3132508627578625') ) const { data: userAlbums } = useUserAlbums() const isThisAlbumLiked = useMemo(() => { if (!album) return false return !!userAlbums?.data?.find(a => a.id === album.id) }, [album, userAlbums?.data]) const mutationLikeAAlbum = useMutationLikeAAlbum() return ( <> {/* Header background */}
{coverUrl && !isCoverError && ( <> )}
{/* Cover */}
{/* Neon shadow */} {!isLoading && coverUrl && !isCoverError && (
)} {!isLoading && isCoverError ? ( // Fallback cover
) : ( coverUrl && ( setCoverError(true)} /> ) )} {isLoading && }
{/* Info */}
{/* Name */} {isLoading ? ( PLACEHOLDER ) : (
{album?.name}
)} {/* Artist */} {isLoading ? ( PLACEHOLDER ) : (
Album ·{' '} {album?.artist.name}
)} {/* Release date & track count & album duration */} {isLoading ? ( PLACEHOLDER ) : (
{album?.mark === 1056768 && ( )} {dayjs(album?.publishTime || 0).year()} · {album?.size} 首歌 ·{' '} {albumDuration}
)} {/* Description */} {isLoading ? ( PLACEHOLDER ) : (
{album?.description}
)} {/* Buttons */}
) } const MoreAlbum = ({ album }: { album: Album | undefined }) => { // Fetch artist's albums const { data: albums, isLoading } = useArtistAlbums({ id: album?.artist.id ?? 0, limit: 1000, }) const filteredAlbums = useMemo((): Album[] => { if (!albums) return [] const allReleases = albums?.hotAlbums || [] const filteredAlbums = allReleases.filter( album => ['专辑', 'EP/Single', 'EP'].includes(album.type) && album.size > 1 ) const singles = allReleases.filter(album => album.type === 'Single') const qualifiedAlbums = [...filteredAlbums, ...singles] const formatName = (name: string) => name.toLowerCase().replace(/(\s|deluxe|edition|\(|\))/g, '') const uniqueAlbums: Album[] = [] qualifiedAlbums.forEach(a => { // 去除当前页面的专辑 if (formatName(a.name) === formatName(album?.name ?? '')) return // 去除重复的专辑(包含 deluxe edition 的专辑会视为重复) if ( uniqueAlbums.findIndex(aa => { return formatName(a.name) === formatName(aa.name) }) !== -1 ) { return } // 去除 remix 专辑 if ( a.name.toLowerCase().includes('remix)') || a.name.toLowerCase().includes('remixes)') ) { return } uniqueAlbums.push(a) }) return uniqueAlbums.slice(0, 5) }, [album?.name, albums]) return (
{!isLoading && albums?.hotAlbums?.length && (
More by{' '} {album?.artist.name}
)}
) } const Album = () => { const params = useParams() const { data: album, isLoading } = useAlbum({ id: Number(params.id) || 0, }) const { data: tracks } = useTracks({ ids: album?.songs?.map(track => track.id) ?? [], }) const handlePlay = async (trackID: number | null = null) => { if (!album?.album.id) { toast('无法播放专辑,该专辑不存在') return } await player.playAlbum(album.album.id, trackID) } return (
{album?.album && (
Released {formatDate(album.album.publishTime || 0, 'en')}
{album.album.company && (
© {album.album.company}
)}
)} {!isLoading && }
) } export default Album