import { memo, useCallback, useEffect, useMemo } from 'react' import Button, { Color as ButtonColor } from '@/web/components/Button' import Skeleton from '@/web/components/Skeleton' import SvgIcon from '@/web/components/SvgIcon' import TracksList from '@/web/components/TracksList' import usePlaylist from '@/web/hooks/usePlaylist' import useScroll from '@/web/hooks/useScroll' import useTracksInfinite from '@/web/hooks/useTracksInfinite' import { player } from '@/web/store' import { formatDate, resizeImage } from '@/web/utils/common' import useUserPlaylists, { useMutationLikeAPlaylist, } from '@/web/hooks/useUserPlaylists' import useUser from '@/web/hooks/useUser' import { Mode as PlayerMode, TrackListSourceType, State as PlayerState, } from '@/web/utils/player' import toast from 'react-hot-toast' import { useParams } from 'react-router-dom' import { useSnapshot } from 'valtio' const PlayButton = ({ playlist, handlePlay, isLoading, }: { playlist: Playlist | undefined handlePlay: () => void isLoading: boolean }) => { const playerSnapshot = useSnapshot(player) const isThisPlaylistPlaying = useMemo( () => playerSnapshot.mode === PlayerMode.TrackList && playerSnapshot.trackListSource?.type === TrackListSourceType.Playlist && playerSnapshot.trackListSource?.id === playlist?.id, [ playerSnapshot.mode, playerSnapshot.trackListSource?.id, playerSnapshot.trackListSource?.type, playlist?.id, ] ) const wrappedHandlePlay = () => { if (isThisPlaylistPlaying) { player.playOrPause() } else { handlePlay() } } const isPlaying = isThisPlaylistPlaying && [PlayerState.Playing, PlayerState.Loading].includes(playerSnapshot.state) return ( ) } const Header = memo( ({ playlist, isLoading, handlePlay, }: { playlist: Playlist | undefined isLoading: boolean handlePlay: () => void }) => { const coverUrl = resizeImage(playlist?.coverImgUrl || '', 'lg') const mutationLikeAPlaylist = useMutationLikeAPlaylist() const { data: userPlaylists } = useUserPlaylists() const isThisPlaylistLiked = useMemo(() => { if (!playlist) return false return !!userPlaylists?.playlist?.find(p => p.id === playlist.id) }, [playlist, userPlaylists?.playlist]) const { data: user } = useUser() const isThisPlaylistCreatedByCurrentUser = useMemo(() => { if (!playlist || !user) return false return playlist.creator.userId === user?.profile?.userId }, [playlist, user]) return ( <> {/* Header background */}
{/* Cover */}
{!isLoading && (
)} {!isLoading && ( )} {isLoading && ( )}
{/* */}
{/* */} {!isLoading && (
{playlist?.name}
)} {isLoading && ( PLACEHOLDER )} {/* */} {!isLoading && (
歌单 · {playlist?.creator?.nickname}
)} {isLoading && ( PLACEHOLDER )} {/* */} {!isLoading && (
更新于 {formatDate(playlist?.updateTime || 0, 'zh-CN')} ·{' '} {playlist?.trackCount} 首歌
)} {isLoading && ( PLACEHOLDER )} {/* */} {!isLoading && (
{playlist?.description}
)} {isLoading && ( PLACEHOLDER )} {/* */}
{!isThisPlaylistCreatedByCurrentUser && ( )}
) } ) Header.displayName = 'Header' const Tracks = memo( ({ playlist, handlePlay, isLoadingPlaylist, }: { playlist: Playlist | undefined handlePlay: (trackID: number | null) => void isLoadingPlaylist: boolean }) => { const { data: tracksPages, hasNextPage, isLoading: isLoadingTracks, isFetchingNextPage, fetchNextPage, } = useTracksInfinite({ ids: playlist?.trackIds?.map(t => t.id) || [], }) const scroll = useScroll(document.getElementById('mainContainer'), { throttle: 500, offset: { bottom: 256, }, }) useEffect(() => { if (!scroll.arrivedState.bottom || !hasNextPage || isFetchingNextPage) return fetchNextPage() }, [ fetchNextPage, hasNextPage, isFetchingNextPage, scroll.arrivedState.bottom, ]) const tracks = useMemo(() => { if (!tracksPages) return [] const allTracks: Track[] = [] tracksPages.pages.forEach(page => allTracks.push(...(page?.songs ?? []))) return allTracks }, [tracksPages]) return ( <> {isLoadingPlaylist ? ( ) : isLoadingTracks ? ( ) : ( )} ) } ) Tracks.displayName = 'Tracks' const Playlist = () => { const params = useParams() const { data: playlist, isLoading } = usePlaylist({ id: Number(params.id) || 0, }) const handlePlay = useCallback( (trackID: number | null = null) => { if (!playlist?.playlist?.id) { toast('无法播放歌单') return } player.playPlaylist(playlist.playlist.id, trackID) }, [playlist] ) return (
) } export default Playlist