feat: updates (#1419)

* feat: TrackList 高亮播放中Track & Track 子标题(歌名翻译)

* fix: 不对id为0的歌手应用下划线

* feat: TrackList的Track支持深色模式

* fix: typo

* feat: 专辑页面的subtitle支持深色模式

* fix: typo

* feat: 在TrackList中高亮播放Track里的歌手信息
This commit is contained in:
memorydream 2022-03-18 14:13:56 +08:00 committed by GitHub
parent e3486ab550
commit 08abf8229f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 28 deletions

View file

@ -11,7 +11,9 @@ const ArtistInline = ({
<div className={classNames('flex truncate', className)}> <div className={classNames('flex truncate', className)}>
{artists.map((artist, index) => ( {artists.map((artist, index) => (
<span key={artist.id}> <span key={artist.id}>
<span className='hover:underline'>{artist.name}</span> <span className={classNames({ 'hover:underline': !!artist.id })}>
{artist.name}
</span>
{index < artists.length - 1 ? ', ' : ''}&nbsp; {index < artists.length - 1 ? ', ' : ''}&nbsp;
</span> </span>
))} ))}

View file

@ -49,12 +49,14 @@ const Track = memo(
isLiked = false, isLiked = false,
isSkeleton = false, isSkeleton = false,
isHighlight = false, isHighlight = false,
subtitle = undefined,
onClick, onClick,
}: { }: {
track: Track track: Track
isLiked?: boolean isLiked?: boolean
isSkeleton?: boolean isSkeleton?: boolean
isHighlight?: boolean isHighlight?: boolean
subtitle?: string
onClick: (e: React.MouseEvent<HTMLElement>, trackID: number) => void onClick: (e: React.MouseEvent<HTMLElement>, trackID: number) => void
}) => { }) => {
if (enableRenderLog) if (enableRenderLog)
@ -113,7 +115,17 @@ const Track = memo(
isHighlight ? 'text-brand-500' : 'text-black dark:text-white' isHighlight ? 'text-brand-500' : 'text-black dark:text-white'
)} )}
> >
{track.name} <span>{track.name}</span>
{subtitle && (
<span
title={subtitle}
className={classNames(
'ml-1',
isHighlight ? 'text-brand-500/[.8]' : 'text-gray-400'
)}>
({subtitle})
</span>
)}
</div> </div>
)} )}
</div> </div>
@ -238,6 +250,7 @@ const TracksAlbum = ({
isLiked={userLikedSongs?.ids?.includes(track.id) ?? false} isLiked={userLikedSongs?.ids?.includes(track.id) ?? false}
isSkeleton={false} isSkeleton={false}
isHighlight={track.id === playingTrack?.id} isHighlight={track.id === playingTrack?.id}
subtitle={track.tns?.at(0) ?? track.alia?.at(0)}
/> />
))} ))}
</div> </div>

View file

@ -7,6 +7,7 @@ import { prefetchAlbum } from '@/hooks/useAlbum'
import useUser from '@/hooks/useUser' import useUser from '@/hooks/useUser'
import useUserLikedSongsIDs from '@/hooks/useUserLikedSongsIDs' import useUserLikedSongsIDs from '@/hooks/useUserLikedSongsIDs'
import { formatDuration, resizeImage } from '@/utils/common' import { formatDuration, resizeImage } from '@/utils/common'
import { player } from '@/store'
const Track = memo( const Track = memo(
({ ({
@ -14,12 +15,14 @@ const Track = memo(
isLiked = false, isLiked = false,
isSkeleton = false, isSkeleton = false,
isPlaying = false, isPlaying = false,
subtitle = undefined,
onClick, onClick,
}: { }: {
track: Track track: Track
isLiked?: boolean isLiked?: boolean
isSkeleton?: boolean isSkeleton?: boolean
isPlaying?: boolean isPlaying?: boolean
subtitle?: string
onClick: (e: React.MouseEvent<HTMLElement>, trackID: number) => void onClick: (e: React.MouseEvent<HTMLElement>, trackID: number) => void
}) => { }) => {
return ( return (
@ -28,8 +31,8 @@ const Track = memo(
className={classNames( className={classNames(
'group grid w-full rounded-xl after:scale-[.98] after:rounded-xl dark:after:bg-white/[.08]', 'group grid w-full rounded-xl after:scale-[.98] after:rounded-xl dark:after:bg-white/[.08]',
'grid-cols-12 p-2 pr-4', 'grid-cols-12 p-2 pr-4',
!isSkeleton && !isPlaying && 'btn-hover-animation after:bg-gray-100', !isSkeleton && !isPlaying && 'btn-hover-animation after:bg-gray-100 dark:after:bg-white/[.08]',
!isSkeleton && isPlaying && 'bg-brand-100' !isSkeleton && isPlaying && 'bg-brand-100 dark:bg-gray-800'
)} )}
> >
{/* Track info */} {/* Track info */}
@ -51,12 +54,33 @@ const Track = memo(
{isSkeleton ? ( {isSkeleton ? (
<Skeleton className='text-lg'>PLACEHOLDER12345</Skeleton> <Skeleton className='text-lg'>PLACEHOLDER12345</Skeleton>
) : ( ) : (
<div className='line-clamp-1 break-all text-lg font-semibold dark:text-white'> <div
{track.name} className={classNames(
'line-clamp-1 break-all text-lg font-semibold',
isPlaying ? 'text-brand-500' : 'text-black dark:text-white'
)}
>
<span>{track.name}</span>
{subtitle && (
<span
title={subtitle}
className={classNames(
'ml-1',
isPlaying ? 'text-brand-500/[.8]' : 'text-gray-400'
)}
>
({subtitle})
</span>
)}
</div> </div>
)} )}
<div className='text-sm text-gray-600 dark:text-gray-400'> <div
className={classNames(
'text-sm',
isPlaying ? 'text-brand-500' : 'text-gray-600 dark:text-gray-400'
)}
>
{isSkeleton ? ( {isSkeleton ? (
<Skeleton className='w-2/3 translate-y-px'>PLACE</Skeleton> <Skeleton className='w-2/3 translate-y-px'>PLACE</Skeleton>
) : ( ) : (
@ -75,7 +99,10 @@ const Track = memo(
<NavLink <NavLink
to={`/album/${track.al.id}`} to={`/album/${track.al.id}`}
onMouseOver={() => prefetchAlbum({ id: track.al.id })} onMouseOver={() => prefetchAlbum({ id: track.al.id })}
className='hover:underline' className={classNames(
'hover:underline',
isPlaying && 'text-brand-500'
)}
> >
{track.al.name} {track.al.name}
</NavLink> </NavLink>
@ -107,7 +134,12 @@ const Track = memo(
{isSkeleton ? ( {isSkeleton ? (
<Skeleton>0:00</Skeleton> <Skeleton>0:00</Skeleton>
) : ( ) : (
<div className='min-w-[2.5rem] text-right text-gray-600 dark:text-gray-400'> <div
className={classNames(
'min-w-[2.5rem] text-right',
isPlaying ? 'text-brand-500' : 'text-gray-600 dark:text-gray-400'
)}
>
{formatDuration(track.dt, 'en', 'hh:mm:ss')} {formatDuration(track.dt, 'en', 'hh:mm:ss')}
</div> </div>
)} )}
@ -143,6 +175,12 @@ const TracksList = memo(
if (e.detail === 2) onTrackDoubleClick?.(trackID) if (e.detail === 2) onTrackDoubleClick?.(trackID)
} }
const playerSnapshot = useSnapshot(player)
const playingTrack = useMemo(
() => playerSnapshot.track,
[playerSnapshot.track]
)
return ( return (
<Fragment> <Fragment>
{/* Tracks table header */} {/* Tracks table header */}
@ -157,25 +195,26 @@ const TracksList = memo(
<div className='grid w-full gap-1'> <div className='grid w-full gap-1'>
{/* Tracks */} {/* Tracks */}
{!isSkeleton && {isSkeleton
tracks.map(track => ( ? skeletonTracks.map((track, index) => (
<Track <Track
onClick={handleClick} key={index}
key={track.id} track={track}
track={track} onClick={() => null}
isLiked={userLikedSongs?.ids?.includes(track.id) ?? false} isSkeleton={true}
isSkeleton={false} />
/> ))
))} : tracks.map(track => (
{isSkeleton && <Track
skeletonTracks.map((track, index) => ( onClick={handleClick}
<Track key={track.id}
key={index} track={track}
track={track} isLiked={userLikedSongs?.ids?.includes(track.id) ?? false}
onClick={() => null} isSkeleton={false}
isSkeleton={true} isPlaying={track.id === playingTrack?.id}
/> subtitle={track.tns?.at(0) ?? track.alia?.at(0)}
))} />
))}
</div> </div>
</Fragment> </Fragment>
) )

View file

@ -111,6 +111,7 @@ declare interface Track {
tagPicList: null tagPicList: null
v: number v: number
version: number version: number
tns: (string | null)[]
} }
declare interface Artist { declare interface Artist {
alias: unknown[] alias: unknown[]