feat: updates

This commit is contained in:
qier222 2022-08-03 23:48:39 +08:00
parent 47e41dea9b
commit ebebf2a733
No known key found for this signature in database
GPG key ID: 9C85007ED905F14D
160 changed files with 4148 additions and 2001 deletions

View file

@ -0,0 +1,54 @@
import TrackListHeader from '@/web/components/New/TrackListHeader'
import useAlbum from '@/web/api/hooks/useAlbum'
import useTracks from '@/web/api/hooks/useTracks'
import { NavLink, useParams } from 'react-router-dom'
import PageTransition from '@/web/components/New/PageTransition'
import TrackList from '@/web/components/New/TrackList'
import player from '@/web/states/player'
import toast from 'react-hot-toast'
import { useSnapshot } from 'valtio'
import useArtistAlbums from '@/web/api/hooks/useArtistAlbums'
import { css, cx } from '@emotion/css'
import CoverRow from '@/web/components/New/CoverRow'
import { useCallback, useMemo } from 'react'
import MoreByArtist from './MoreByArtist'
import Header from './Header'
const Album = () => {
const params = useParams()
const { data: album } = useAlbum({
id: Number(params.id),
})
const { data: tracks } = useTracks({
ids: album?.songs?.map(track => track.id) ?? [],
})
const onPlay = useCallback(
async (trackID: number | null = null) => {
if (!album?.album?.id) {
toast('无法播放专辑,该专辑不存在')
return
}
player.playAlbum(album.album.id, trackID)
},
[album?.album?.id]
)
return (
<PageTransition>
<Header />
<TrackList
tracks={tracks?.songs || album?.album.songs || album?.songs}
className='z-10 mx-2.5 mt-3 lg:mx-0 lg:mt-10'
onPlay={onPlay}
/>
<MoreByArtist album={album?.album} />
{/* Page padding */}
<div className='h-16'></div>
</PageTransition>
)
}
export default Album

View file

@ -0,0 +1,107 @@
import useAlbum from '@/web/api/hooks/useAlbum'
import useUserAlbums, {
useMutationLikeAAlbum,
} from '@/web/api/hooks/useUserAlbums'
import Icon from '@/web/components/Icon'
import TrackListHeader from '@/web/components/New/TrackListHeader'
import useAppleMusicAlbum from '@/web/hooks/useAppleMusicAlbum'
import useVideoCover from '@/web/hooks/useVideoCover'
import player from '@/web/states/player'
import { formatDuration } from '@/web/utils/common'
import dayjs from 'dayjs'
import { useMemo } from 'react'
import toast from 'react-hot-toast'
import { useParams } from 'react-router-dom'
const Header = () => {
const params = useParams()
const { data: userLikedAlbums } = useUserAlbums()
const { data: albumRaw } = useAlbum({
id: Number(params.id),
})
const album = useMemo(() => albumRaw?.album, [albumRaw])
const { data: albumFromApple, isLoading: isLoadingAlbumFromApple } =
useAppleMusicAlbum({
id: album?.id,
name: album?.name,
artist: album?.artist.name,
})
const { data: videoCoverFromRemote } = useVideoCover({
id: album?.id,
name: album?.name,
artist: album?.artist.name,
enabled: !window.env?.isElectron,
})
// For <Cover />
const cover = album?.picUrl
const videoCover =
albumFromApple?.attributes?.editorialVideo?.motionSquareVideo1x1?.video ||
videoCoverFromRemote?.video
// For <Info />
const title = album?.name
const creatorName = album?.artist.name
const creatorLink = `/artist/${album?.artist.id}`
const description = isLoadingAlbumFromApple
? ''
: albumFromApple?.attributes?.editorialNotes?.standard || album?.description
const extraInfo = useMemo(() => {
const duration = album?.songs?.reduce((acc, cur) => acc + cur.dt, 0) || 0
const albumDuration = formatDuration(duration, 'en', 'hh[hr] mm[min]')
return (
<>
{album?.mark === 1056768 && (
<Icon
name='explicit'
className='mb-px mr-1 h-3 w-3 lg:h-3.5 lg:w-3.5'
/>
)}{' '}
{dayjs(album?.publishTime || 0).year()} · {album?.songs.length} tracks,{' '}
{albumDuration}
</>
)
}, [album])
// For <Actions />
const isLiked = useMemo(() => {
const id = Number(params.id)
if (!id) return false
return !!userLikedAlbums?.data.find(item => item.id === id)
}, [params.id, userLikedAlbums?.data])
const onPlay = async (trackID: number | null = null) => {
if (!album?.id) {
toast('无法播放专辑,该专辑不存在')
return
}
player.playAlbum(album.id, trackID)
}
const likeAAlbum = useMutationLikeAAlbum()
const onLike = async () => {
likeAAlbum.mutateAsync(album?.id || Number(params.id))
}
return (
<TrackListHeader
{...{
title,
creatorName,
creatorLink,
description,
extraInfo,
cover,
videoCover,
isLiked,
onLike,
onPlay,
}}
/>
)
}
export default Header

View file

@ -0,0 +1,86 @@
import TrackListHeader from '@/web/components/New/TrackListHeader'
import useAlbum from '@/web/api/hooks/useAlbum'
import useTracks from '@/web/api/hooks/useTracks'
import { NavLink, useParams } from 'react-router-dom'
import PageTransition from '@/web/components/New/PageTransition'
import TrackList from '@/web/components/New/TrackList'
import player from '@/web/states/player'
import toast from 'react-hot-toast'
import { useSnapshot } from 'valtio'
import useArtistAlbums from '@/web/api/hooks/useArtistAlbums'
import { css, cx } from '@emotion/css'
import CoverRow from '@/web/components/New/CoverRow'
import { useMemo } from 'react'
const MoreByArtist = ({ album }: { album?: Album }) => {
const { data: albums } = 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' || album.size === 1
)
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, 4)
}, [album?.name, albums])
return (
<div>
{/* Dividing line */}
<div className={cx('mx-2.5 my-7.5 h-px bg-white/10 lg:mx-0')}></div>
{/* Title */}
<div className='mx-2.5 mb-5 text-14 font-bold text-neutral-300 lg:mx-0'>
MORE BY{' '}
<NavLink
to={`/artist/${album?.artist.id}`}
className='transition duration-300 ease-in-out hover:text-neutral-100'
>
{album?.artist.name}
</NavLink>
</div>
<CoverRow albums={filteredAlbums} className='mx-2.5 lg:mx-0' />
</div>
)
}
export default MoreByArtist

View file

@ -0,0 +1,2 @@
import Album from './Album'
export default Album