import Cover from '@/web/components/Cover'
import Skeleton from '@/web/components/Skeleton'
import SvgIcon from '@/web/components/SvgIcon'
import { prefetchAlbum } from '@/web/hooks/useAlbum'
import { prefetchPlaylist } from '@/web/hooks/usePlaylist'
import { formatDate, resizeImage, scrollToTop } from '@/web/utils/common'
import cx from 'classnames'
import { useMemo } from 'react'
import { useNavigate } from 'react-router-dom'
export enum Subtitle {
Copywriter = 'copywriter',
Creator = 'creator',
TypeReleaseYear = 'type+releaseYear',
Artist = 'artist',
}
const Title = ({
title,
seeMoreLink,
}: {
title: string
seeMoreLink: string
}) => {
return (
{title}
{seeMoreLink && (
See More
)}
)
}
const getSubtitleText = (
item: Album | Playlist | Artist,
subtitle: Subtitle
) => {
const nickname = 'creator' in item ? item.creator.nickname : 'someone'
const artist =
'artist' in item
? item.artist.name
: 'artists' in item
? item.artists?.[0]?.name
: 'unknown'
const copywriter = 'copywriter' in item ? item.copywriter : 'unknown'
const releaseYear =
('publishTime' in item &&
formatDate(item.publishTime ?? 0, 'en', 'YYYY')) ||
'unknown'
const type = {
playlist: 'playlist',
album: 'Album',
专辑: 'Album',
Single: 'Single',
'EP/Single': 'EP',
EP: 'EP',
unknown: 'unknown',
精选集: 'Collection',
}[('type' in item && typeof item.type !== 'number' && item.type) || 'unknown']
const table = {
[Subtitle.Creator]: `by ${nickname}`,
[Subtitle.TypeReleaseYear]: `${type} · ${releaseYear}`,
[Subtitle.Artist]: artist,
[Subtitle.Copywriter]: copywriter,
}
return table[subtitle]
}
const getImageUrl = (item: Album | Playlist | Artist) => {
let cover: string | undefined = ''
if ('coverImgUrl' in item) cover = item.coverImgUrl
if ('picUrl' in item) cover = item.picUrl
if ('img1v1Url' in item) cover = item.img1v1Url
return resizeImage(cover || '', 'md')
}
const CoverRow = ({
title,
albums,
artists,
playlists,
subtitle = Subtitle.Copywriter,
seeMoreLink,
isSkeleton,
className,
rows = 2,
navigateCallback, // Callback function when click on the cover/title
}: {
title?: string
albums?: Album[]
artists?: Artist[]
playlists?: Playlist[]
subtitle?: Subtitle
seeMoreLink?: string
isSkeleton?: boolean
className?: string
rows?: number
navigateCallback?: () => void
}) => {
const renderItems = useMemo(() => {
if (isSkeleton) {
return new Array(rows * 5).fill({}) as Array
}
return albums ?? playlists ?? artists ?? []
}, [albums, artists, isSkeleton, playlists, rows])
const navigate = useNavigate()
const goTo = (id: number) => {
if (isSkeleton) return
if (albums) navigate(`/album/${id}`)
if (playlists) navigate(`/playlist/${id}`)
if (artists) navigate(`/artist/${id}`)
if (navigateCallback) navigateCallback()
scrollToTop()
}
const prefetch = (id: number) => {
if (albums) prefetchAlbum({ id })
if (playlists) prefetchPlaylist({ id })
}
return (
{title &&
}
{renderItems.map((item, index) => (
prefetch(item.id)}
className='grid gap-x-6 gap-y-7'
>
{/* Cover */}
{isSkeleton ? (
) : (
goTo(item.id)}
imageUrl={getImageUrl(item)}
showPlayButton={true}
roundedClass={artists ? 'rounded-full' : 'rounded-xl'}
/>
)}
{/* Info */}
{/* Name */}
{isSkeleton ? (
PLACEHOLDER
PLACEHOLDER
) : (
{/* Playlist private icon */}
{(item as Playlist).privacy === 10 && (
)}
{/* Explicit icon */}
{(item as Album)?.mark === 1056768 && (
)}
{/* Name */}
goTo(item.id)}
className='decoration-gray-600 decoration-2 hover:underline dark:text-white dark:decoration-gray-200'
>
{item.name}
)}
{/* Subtitle */}
{isSkeleton ? (
PLACEHOLDER
) : (
!artists && (
{getSubtitleText(item, subtitle)}
)
)}
))}
)
}
export default CoverRow