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

@ -5,6 +5,7 @@ import Header from './Header'
import Popular from './Popular'
import ArtistAlbum from './ArtistAlbums'
import FansAlsoLike from './FansAlsoLike'
import ArtistMVs from './ArtistMVs'
const Artist = () => {
const params = useParams()
@ -17,13 +18,16 @@ const Artist = () => {
<div>
<Header artist={artist?.artist} />
{/* Dividing line */}
<div className='mt-10 mb-7.5 h-px w-full bg-white/20'></div>
<Popular tracks={artist?.hotSongs} />
<ArtistAlbum />
<ArtistMVs />
<FansAlsoLike />
{/* Page padding */}
<div className='h-16'></div>
</div>
)
}

View file

@ -1,5 +1,6 @@
import useArtistAlbums from '@/web/api/hooks/useArtistAlbums'
import CoverRow from '@/web/components/New/CoverRow'
import React from 'react'
import { useMemo } from 'react'
import { useParams } from 'react-router-dom'
@ -11,7 +12,18 @@ const ArtistAlbum = () => {
limit: 1000,
})
const albums = useMemo(() => albumsRaw?.hotAlbums, [albumsRaw?.hotAlbums])
const pages = useMemo(() => {
const pages: Album[][] = []
albumsRaw?.hotAlbums.forEach((album, index) => {
const pageNo = Math.floor(index / 12)
if (!pages[pageNo]) {
pages[pageNo] = [album]
} else {
pages[pageNo].push(album)
}
})
return pages
}, [albumsRaw?.hotAlbums])
return (
<div>
@ -19,9 +31,19 @@ const ArtistAlbum = () => {
Albums
</div>
<CoverRow albums={albums?.slice(0, 12)} />
<div className='no-scrollbar flex gap-6 overflow-y-hidden overflow-x-scroll'>
{pages.map((page, index) => (
<CoverRow
key={index}
albums={page}
className='h-full w-full flex-shrink-0'
/>
))}
</div>
</div>
)
}
export default ArtistAlbum
const memoized = React.memo(ArtistAlbum)
memoized.displayName = 'ArtistAlbum'
export default memoized

View file

@ -0,0 +1,32 @@
import { useNavigate, useParams } from 'react-router-dom'
import useArtistMV from '@/web/api/hooks/useArtistMV'
const ArtistMVs = () => {
const params = useParams()
const navigate = useNavigate()
const { data: videos } = useArtistMV({ id: Number(params.id) || 0 })
return (
<div>
<div className='mb-6 mt-10 text-12 font-medium uppercase text-neutral-300'>
MV
</div>
<div className='grid grid-cols-3 gap-6'>
{videos?.mvs?.slice(0, 6)?.map(video => (
<div key={video.id} onClick={() => navigate(`/mv/${video.id}`)}>
<img
src={video.imgurl16v9}
className='aspect-video w-full rounded-24 object-contain'
/>
<div className='mt-2 text-12 font-medium text-neutral-600'>
{video.name}
</div>
</div>
))}
</div>
</div>
)
}
export default ArtistMVs

View file

@ -4,16 +4,22 @@ import { useParams } from 'react-router-dom'
const FansAlsoLike = () => {
const params = useParams()
const { data: artists } = useSimilarArtists({ id: Number(params.id) || 0 })
const { data: artists, isLoading } = useSimilarArtists({
id: Number(params.id) || 0,
})
return (
<div>
<div className='mb-6 mt-10 text-12 font-medium uppercase text-neutral-300'>
Fans Also Like
</div>
<>
{(isLoading || artists?.artists) && (
<div>
<div className='mb-6 mt-10 text-12 font-medium uppercase text-neutral-300'>
Fans Also Like
</div>
<ArtistRow artists={artists?.artists?.slice(0, 5)} />
</div>
<ArtistRow artists={artists?.artists?.slice(0, 5)} />
</div>
)}
</>
)
}

View file

@ -1,11 +1,44 @@
import useUserArtists, {
useMutationLikeAArtist,
} from '@/web/api/hooks/useUserArtists'
import Icon from '@/web/components/Icon'
import player from '@/web/states/player'
import { useParams } from 'react-router-dom'
const Actions = () => {
const { data: likedArtists } = useUserArtists()
const params = useParams()
const id = Number(params.id) || 0
const isLiked = !!likedArtists?.data?.find(artist => artist.id === id)
const likeArtist = useMutationLikeAArtist()
return (
<div className='mt-11 flex items-end justify-between lg:z-10 lg:mt-6'>
<div className='flex items-end'>
<button className='mr-2.5 h-14 w-14 rounded-full dark:bg-white/10'></button>
<button className='h-14 w-14 rounded-full dark:bg-white/10'></button>
{/* Menu */}
<button className='mr-2.5 flex h-14 w-14 items-center justify-center rounded-full text-white/40 transition duration-400 hover:text-white/70 dark:bg-white/10 hover:dark:bg-white/30'>
<Icon name='more' className='h-7 w-7' />
</button>
{/* Like */}
<button
onClick={() => likeArtist.mutateAsync(id)}
className='flex h-14 w-14 items-center justify-center rounded-full text-white/40 transition duration-400 hover:text-white/70 dark:bg-white/10 hover:dark:bg-white/30'
>
<Icon
name={isLiked ? 'heart' : 'heart-outline'}
className='h-7 w-7'
/>
</button>
</div>
<button className='h-14 w-[125px] rounded-full dark:bg-brand-700 lg:w-[170px]'></button>
{/* Listen */}
<button
onClick={() => player.playArtistPopularTracks(id)}
className='h-14 rounded-full px-10 text-18 font-medium text-white dark:bg-brand-700'
>
Listen
</button>
</div>
)
}

View file

@ -1,5 +1,6 @@
import useIsMobile from '@/web/hooks/useIsMobile'
import useAppleMusicArtist from '@/web/hooks/useAppleMusicArtist'
import { cx, css } from '@emotion/css'
const ArtistInfo = ({ artist }: { artist?: Artist }) => {
const isMobile = useIsMobile()
@ -11,20 +12,27 @@ const ArtistInfo = ({ artist }: { artist?: Artist }) => {
return (
<div>
<div className='text-28 font-semibold text-night-50 lg:text-32'>
<div className='text-28 font-semibold text-white/70 lg:text-32'>
{artist?.name}
</div>
<div className='mt-2.5 text-24 font-medium text-night-400 lg:mt-6'>
<div className='mt-2.5 text-24 font-medium text-white/40 lg:mt-6'>
Artist
</div>
<div className='mt-1 text-12 font-medium text-night-400'>
<div className='mt-1 text-12 font-medium text-white/40'>
{artist?.musicSize} Tracks · {artist?.albumSize} Albums ·{' '}
{artist?.mvSize} Videos
</div>
{/* Description */}
{!isMobile && !isLoadingArtistFromApple && (
<div className='line-clamp-5 mt-6 text-14 font-bold text-night-400'>
<div
className={cx(
'line-clamp-5 mt-6 text-14 font-bold text-white/40',
css`
height: 86px;
`
)}
>
{artistFromApple?.attributes?.artistBio || artist?.briefDesc}
</div>
)}

View file

@ -0,0 +1,101 @@
import { isIOS, isSafari, resizeImage } from '@/web/utils/common'
import Image from '@/web/components/New/Image'
import { cx, css } from '@emotion/css'
import useAppleMusicArtist from '@/web/hooks/useAppleMusicArtist'
import { useEffect, useRef } from 'react'
import Hls from 'hls.js'
import { motion } from 'framer-motion'
import uiStates from '@/web/states/uiStates'
const VideoCover = ({ source }: { source?: string }) => {
const ref = useRef<HTMLVideoElement>(null)
const hls = useRef<Hls>(new Hls())
useEffect(() => {
if (source && Hls.isSupported()) {
const video = document.querySelector('#video-cover') as HTMLVideoElement
hls.current.loadSource(source)
hls.current.attachMedia(video)
}
}, [source])
return (
<div className='z-10 aspect-square overflow-hidden rounded-24'>
<video
id='video-cover'
className='h-full w-full'
ref={ref}
autoPlay
muted
loop
/>
</div>
)
}
const Cover = ({ artist }: { artist?: Artist }) => {
const { data: artistFromApple, isLoading: isLoadingArtistFromApple } =
useAppleMusicArtist({
id: artist?.id,
name: artist?.name,
})
const video =
artistFromApple?.attributes?.editorialVideo?.motionArtistSquare1x1?.video
const cover = isLoadingArtistFromApple
? ''
: artistFromApple?.attributes?.artwork?.url || artist?.img1v1Url
useEffect(() => {
if (cover) uiStates.blurBackgroundImage = cover
}, [cover])
return (
<>
<div
className={cx(
'relative',
css`
grid-area: cover;
`
)}
>
<Image
className='aspect-square h-full w-full lg:z-10 lg:rounded-24'
src={resizeImage(
isLoadingArtistFromApple
? ''
: artistFromApple?.attributes?.artwork?.url ||
artist?.img1v1Url ||
'',
'lg'
)}
/>
{video && (
<motion.div
initial={{ opacity: isIOS ? 1 : 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.6 }}
className='absolute inset-0 z-10 h-full w-full'
>
{isSafari ? (
<video
src={video}
className='h-full w-full'
autoPlay
loop
muted
playsInline
></video>
) : (
<VideoCover source={video} />
)}
</motion.div>
)}
</div>
</>
)
}
export default Cover

View file

@ -1,19 +1,10 @@
import { resizeImage } from '@/web/utils/common'
import { cx, css } from '@emotion/css'
import Image from '@/web/components/New/Image'
import { breakpoint as bp } from '@/web/utils/const'
import BlurBackground from '@/web/components/New/BlurBackground'
import ArtistInfo from './ArtistInfo'
import Actions from './Actions'
import LatestRelease from './LatestRelease'
import useAppleMusicArtist from '@/web/hooks/useAppleMusicArtist'
import Cover from "./Cover"
const Header = ({ artist }: { artist?: Artist }) => {
const { data: artistFromApple, isLoading: isLoadingArtistFromApple } =
useAppleMusicArtist({
id: artist?.id,
name: artist?.name,
})
return (
<div
@ -27,30 +18,7 @@ const Header = ({ artist }: { artist?: Artist }) => {
`
)}
>
<Image
className={cx(
'aspect-square lg:z-10 lg:rounded-24',
css`
grid-area: cover;
`
)}
src={resizeImage(
isLoadingArtistFromApple
? ''
: artistFromApple?.attributes?.artwork?.url ||
artist?.img1v1Url ||
'',
'lg'
)}
/>
<BlurBackground
cover={
isLoadingArtistFromApple
? ''
: artistFromApple?.attributes?.artwork?.url || artist?.img1v1Url
}
/>
<Cover artist={artist} />
<div
className={cx(

View file

@ -5,6 +5,7 @@ import { useNavigate, useParams } from 'react-router-dom'
import Image from '@/web/components/New/Image'
import useArtistAlbums from '@/web/api/hooks/useArtistAlbums'
import { useMemo } from 'react'
import useArtistMV from '@/web/api/hooks/useArtistMV'
const Album = () => {
const params = useParams()
@ -54,30 +55,40 @@ const Album = () => {
}
const Video = () => {
const params = useParams()
const { data: videos } = useArtistMV({ id: Number(params.id) || 0 })
const video = videos?.mvs?.[0]
const navigate = useNavigate()
return (
<div className='mt-4 flex rounded-24 bg-white/10 p-2.5'>
<Image
src={resizeImage(
'https://p1.music.126.net/am47BH30IGQit_L2vYaArg==/109951167502760845.jpg',
'sm'
)}
className={cx(
css`
height: 60px;
width: 106px;
border-radius: 16px;
`
)}
/>
<div className='flex-shrink-1 ml-2'>
<div className='line-clamp-2 text-16 font-medium text-night-100'>
Swedish House Mafia & The Weeknd Live at C...
<>
{video && (
<div
className='mt-4 flex rounded-24 bg-white/10 p-2.5'
onClick={() => navigate(`/mv/${video.id}`)}
>
<img
src={video.imgurl16v9}
className={cx(
'object-contain',
css`
height: 60px;
border-radius: 16px;
`
)}
/>
<div className='flex-shrink-1 ml-2'>
<div className='line-clamp-1 text-16 font-medium text-night-100'>
{video.name}
</div>
<div className='mt-1 text-14 font-bold text-night-500'>MV</div>
<div className='mt-1.5 text-12 font-medium text-night-500'>
{dayjs(video.publishTime).format('MMM DD, YYYY')}
</div>
</div>
</div>
<div className='mt-1.5 text-12 font-medium text-night-500'>
{dayjs().format('MMM DD, YYYY')}
</div>
</div>
</div>
)}
</>
)
}