mirror of
https://github.com/GiriNeko/YesPlayMusic.git
synced 2025-12-16 13:17:46 +00:00
feat: updates
This commit is contained in:
parent
ebebf2a733
commit
a1b0bcf4d3
68 changed files with 4776 additions and 5559 deletions
|
|
@ -1,22 +1,17 @@
|
|||
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 { 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 { useCallback } from 'react'
|
||||
import MoreByArtist from './MoreByArtist'
|
||||
import Header from './Header'
|
||||
|
||||
const Album = () => {
|
||||
const params = useParams()
|
||||
const { data: album } = useAlbum({
|
||||
const { data: album, isLoading } = useAlbum({
|
||||
id: Number(params.id),
|
||||
})
|
||||
|
||||
|
|
@ -39,9 +34,18 @@ const Album = () => {
|
|||
<PageTransition>
|
||||
<Header />
|
||||
<TrackList
|
||||
tracks={tracks?.songs || album?.album.songs || album?.songs}
|
||||
tracks={
|
||||
tracks?.songs?.length
|
||||
? tracks?.songs
|
||||
: album?.album?.songs?.length
|
||||
? album?.album.songs
|
||||
: album?.songs?.length
|
||||
? album.songs
|
||||
: undefined
|
||||
}
|
||||
className='z-10 mx-2.5 mt-3 lg:mx-0 lg:mt-10'
|
||||
onPlay={onPlay}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<MoreByArtist album={album?.album} />
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ const Header = () => {
|
|||
const params = useParams()
|
||||
const { data: userLikedAlbums } = useUserAlbums()
|
||||
|
||||
const { data: albumRaw } = useAlbum({
|
||||
const { data: albumRaw, isLoading: isLoadingAlbum } = useAlbum({
|
||||
id: Number(params.id),
|
||||
})
|
||||
const album = useMemo(() => albumRaw?.album, [albumRaw])
|
||||
|
|
@ -89,6 +89,7 @@ const Header = () => {
|
|||
return (
|
||||
<TrackListHeader
|
||||
{...{
|
||||
isLoading: isLoadingAlbum,
|
||||
title,
|
||||
creatorName,
|
||||
creatorLink,
|
||||
|
|
|
|||
|
|
@ -78,7 +78,12 @@ const MoreByArtist = ({ album }: { album?: Album }) => {
|
|||
</NavLink>
|
||||
</div>
|
||||
|
||||
<CoverRow albums={filteredAlbums} className='mx-2.5 lg:mx-0' />
|
||||
<CoverRow
|
||||
albums={filteredAlbums}
|
||||
itemTitle='name'
|
||||
itemSubtitle='year'
|
||||
className='mx-2.5 lg:mx-0'
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
import useArtist from '@/web/api/hooks/useArtist'
|
||||
import { cx, css } from '@emotion/css'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import Header from './Header'
|
||||
import Popular from './Popular'
|
||||
import ArtistAlbum from './ArtistAlbums'
|
||||
|
|
@ -8,20 +5,12 @@ import FansAlsoLike from './FansAlsoLike'
|
|||
import ArtistMVs from './ArtistMVs'
|
||||
|
||||
const Artist = () => {
|
||||
const params = useParams()
|
||||
|
||||
const { data: artist, isLoading: isLoadingArtist } = useArtist({
|
||||
id: Number(params.id) || 0,
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Header artist={artist?.artist} />
|
||||
|
||||
<Header />
|
||||
{/* Dividing line */}
|
||||
<div className='mt-10 mb-7.5 h-px w-full bg-white/20'></div>
|
||||
|
||||
<Popular tracks={artist?.hotSongs} />
|
||||
<Popular />
|
||||
<ArtistAlbum />
|
||||
<ArtistMVs />
|
||||
<FansAlsoLike />
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ const ArtistAlbum = () => {
|
|||
<CoverRow
|
||||
key={index}
|
||||
albums={page}
|
||||
itemTitle='name'
|
||||
itemSubtitle='year'
|
||||
className='h-full w-full flex-shrink-0'
|
||||
/>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,13 @@ import useUserArtists, {
|
|||
useMutationLikeAArtist,
|
||||
} from '@/web/api/hooks/useUserArtists'
|
||||
import Icon from '@/web/components/Icon'
|
||||
import { openContextMenu } from '@/web/states/contextMenus'
|
||||
import player from '@/web/states/player'
|
||||
import { cx } from '@emotion/css'
|
||||
import toast from 'react-hot-toast'
|
||||
import { useParams } from 'react-router-dom'
|
||||
|
||||
const Actions = () => {
|
||||
const Actions = ({ isLoading }: { isLoading: boolean }) => {
|
||||
const { data: likedArtists } = useUserArtists()
|
||||
const params = useParams()
|
||||
const id = Number(params.id) || 0
|
||||
|
|
@ -16,14 +19,33 @@ const Actions = () => {
|
|||
<div className='mt-11 flex items-end justify-between lg:z-10 lg:mt-6'>
|
||||
<div className='flex items-end'>
|
||||
{/* 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
|
||||
onClick={event => {
|
||||
openContextMenu({
|
||||
event,
|
||||
type: 'artist',
|
||||
dataSourceID: id,
|
||||
})
|
||||
}}
|
||||
className={cx(
|
||||
'mr-2.5 flex h-14 w-14 items-center justify-center rounded-full transition duration-400 dark:bg-white/10 ',
|
||||
isLoading
|
||||
? 'text-transparent'
|
||||
: 'text-white/40 hover:text-white/70 hover:dark:bg-white/30 '
|
||||
)}
|
||||
>
|
||||
<Icon name='more' className='pointer-events-none 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'
|
||||
className={cx(
|
||||
'mr-2.5 flex h-14 w-14 items-center justify-center rounded-full transition duration-400 dark:bg-white/10 ',
|
||||
isLoading
|
||||
? 'text-transparent'
|
||||
: 'text-white/40 hover:text-white/70 hover:dark:bg-white/30 '
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
name={isLiked ? 'heart' : 'heart-outline'}
|
||||
|
|
@ -35,7 +57,10 @@ const Actions = () => {
|
|||
{/* Listen */}
|
||||
<button
|
||||
onClick={() => player.playArtistPopularTracks(id)}
|
||||
className='h-14 rounded-full px-10 text-18 font-medium text-white dark:bg-brand-700'
|
||||
className={cx(
|
||||
'h-14 rounded-full px-10 text-18 font-medium',
|
||||
isLoading ? 'bg-white/10 text-transparent' : 'bg-brand-700 text-white'
|
||||
)}
|
||||
>
|
||||
Listen
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,13 @@ import useIsMobile from '@/web/hooks/useIsMobile'
|
|||
import useAppleMusicArtist from '@/web/hooks/useAppleMusicArtist'
|
||||
import { cx, css } from '@emotion/css'
|
||||
|
||||
const ArtistInfo = ({ artist }: { artist?: Artist }) => {
|
||||
const ArtistInfo = ({
|
||||
artist,
|
||||
isLoading,
|
||||
}: {
|
||||
artist?: Artist
|
||||
isLoading: boolean
|
||||
}) => {
|
||||
const isMobile = useIsMobile()
|
||||
const { data: artistFromApple, isLoading: isLoadingArtistFromApple } =
|
||||
useAppleMusicArtist({
|
||||
|
|
@ -12,30 +18,67 @@ const ArtistInfo = ({ artist }: { artist?: Artist }) => {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<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-white/40 lg:mt-6'>
|
||||
Artist
|
||||
</div>
|
||||
<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={cx(
|
||||
'line-clamp-5 mt-6 text-14 font-bold text-white/40',
|
||||
css`
|
||||
height: 86px;
|
||||
`
|
||||
)}
|
||||
>
|
||||
{artistFromApple?.attributes?.artistBio || artist?.briefDesc}
|
||||
{/* Name */}
|
||||
{isLoading ? (
|
||||
<div className=' text-28 font-semibold text-transparent lg:text-32'>
|
||||
<span className='rounded-full bg-white/10'>PLACEHOLDER</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className='text-28 font-semibold text-white/70 lg:text-32'>
|
||||
{artist?.name}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Type */}
|
||||
{isLoading ? (
|
||||
<div className='mt-2.5 text-24 font-medium text-transparent lg:mt-6'>
|
||||
<span className='rounded-full bg-white/10'>Artist</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className='mt-2.5 text-24 font-medium text-white/40 lg:mt-6'>
|
||||
Artist
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Counts */}
|
||||
{isLoading ? (
|
||||
<div className='mt-1 text-12 font-medium text-transparent'>
|
||||
<span className='rounded-full bg-white/10'>PLACEHOLDER12345</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className='mt-1 text-12 font-medium text-white/40'>
|
||||
{artist?.musicSize} Tracks · {artist?.albumSize} Albums ·{' '}
|
||||
{artist?.mvSize} Videos
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Description */}
|
||||
{!isMobile &&
|
||||
(isLoading || isLoadingArtistFromApple ? (
|
||||
<div
|
||||
className={cx(
|
||||
'line-clamp-5 mt-6 text-14 font-bold text-transparent',
|
||||
css`
|
||||
height: 86px;
|
||||
`
|
||||
)}
|
||||
>
|
||||
<span className='rounded-full bg-white/10'>
|
||||
PLACEHOLDER1234567890
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={cx(
|
||||
'line-clamp-5 mt-6 text-14 font-bold text-white/40',
|
||||
css`
|
||||
height: 86px;
|
||||
`
|
||||
)}
|
||||
>
|
||||
{artistFromApple?.attributes?.artistBio || artist?.briefDesc}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,16 @@ import { breakpoint as bp } from '@/web/utils/const'
|
|||
import ArtistInfo from './ArtistInfo'
|
||||
import Actions from './Actions'
|
||||
import LatestRelease from './LatestRelease'
|
||||
import Cover from "./Cover"
|
||||
const Header = ({ artist }: { artist?: Artist }) => {
|
||||
import Cover from './Cover'
|
||||
import useArtist from '@/web/api/hooks/useArtist'
|
||||
import { useParams } from 'react-router-dom'
|
||||
|
||||
const Header = () => {
|
||||
const params = useParams()
|
||||
const { data: artistRaw, isLoading } = useArtist({
|
||||
id: Number(params.id) || 0,
|
||||
})
|
||||
const artist = artistRaw?.artist
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
@ -39,8 +47,8 @@ const Header = ({ artist }: { artist?: Artist }) => {
|
|||
`
|
||||
)}
|
||||
>
|
||||
<ArtistInfo artist={artist} />
|
||||
<Actions />
|
||||
<ArtistInfo isLoading={isLoading} artist={artist} />
|
||||
<Actions isLoading={isLoading} />
|
||||
</div>
|
||||
|
||||
<LatestRelease />
|
||||
|
|
|
|||
|
|
@ -6,18 +6,11 @@ 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'
|
||||
import { motion } from 'framer-motion'
|
||||
|
||||
const Album = () => {
|
||||
const params = useParams()
|
||||
const Album = ({ album }: { album?: Album }) => {
|
||||
const navigate = useNavigate()
|
||||
|
||||
const { data: albumsRaw, isLoading: isLoadingAlbums } = useArtistAlbums({
|
||||
id: Number(params.id) || 0,
|
||||
limit: 1000,
|
||||
})
|
||||
|
||||
const album = useMemo(() => albumsRaw?.hotAlbums?.[0], [albumsRaw?.hotAlbums])
|
||||
|
||||
if (!album) {
|
||||
return <></>
|
||||
}
|
||||
|
|
@ -25,7 +18,7 @@ const Album = () => {
|
|||
return (
|
||||
<div
|
||||
onClick={() => navigate(`/album/${album.id}`)}
|
||||
className='flex rounded-24 bg-white/10 p-2.5'
|
||||
className='group flex rounded-24 bg-white/10 p-2.5 transition-colors duration-400 hover:bg-white/20'
|
||||
>
|
||||
<Image
|
||||
src={resizeImage(album.picUrl, 'sm')}
|
||||
|
|
@ -39,14 +32,14 @@ const Album = () => {
|
|||
)}
|
||||
/>
|
||||
<div className='flex-shrink-1 ml-2'>
|
||||
<div className='line-clamp-1 text-16 font-medium text-night-100'>
|
||||
<div className='line-clamp-1 text-16 font-medium text-night-100 transition-colors duration-400 group-hover:text-night-50'>
|
||||
{album.name}
|
||||
</div>
|
||||
<div className='mt-1 text-14 font-bold text-night-500'>
|
||||
<div className='mt-1 text-14 font-bold text-night-500 transition-colors duration-400 group-hover:text-night-200'>
|
||||
{album.type}
|
||||
{album.size > 1 ? `· ${album.size} Tracks` : ''}
|
||||
</div>
|
||||
<div className='mt-1.5 text-12 font-medium text-night-500'>
|
||||
<div className='mt-1.5 text-12 font-medium text-night-500 transition-colors duration-400 group-hover:text-night-200'>
|
||||
{dayjs(album?.publishTime || 0).format('MMM DD, YYYY')}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -54,17 +47,14 @@ const Album = () => {
|
|||
)
|
||||
}
|
||||
|
||||
const Video = () => {
|
||||
const params = useParams()
|
||||
const { data: videos } = useArtistMV({ id: Number(params.id) || 0 })
|
||||
const video = videos?.mvs?.[0]
|
||||
const Video = ({ video }: { video?: any }) => {
|
||||
const navigate = useNavigate()
|
||||
|
||||
return (
|
||||
<>
|
||||
{video && (
|
||||
<div
|
||||
className='mt-4 flex rounded-24 bg-white/10 p-2.5'
|
||||
className='group mt-4 flex rounded-24 bg-white/10 p-2.5 transition-colors duration-400 hover:bg-white/20'
|
||||
onClick={() => navigate(`/mv/${video.id}`)}
|
||||
>
|
||||
<img
|
||||
|
|
@ -78,11 +68,13 @@ const Video = () => {
|
|||
)}
|
||||
/>
|
||||
<div className='flex-shrink-1 ml-2'>
|
||||
<div className='line-clamp-1 text-16 font-medium text-night-100'>
|
||||
<div className='line-clamp-1 text-16 font-medium text-night-100 transition-colors duration-400 group-hover:text-night-50'>
|
||||
{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'>
|
||||
<div className='mt-1 text-14 font-bold text-night-500 transition-colors duration-400 group-hover:text-night-200'>
|
||||
MV
|
||||
</div>
|
||||
<div className='mt-1.5 text-12 font-medium text-night-500 transition-colors duration-400 group-hover:text-night-200'>
|
||||
{dayjs(video.publishTime).format('MMM DD, YYYY')}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -93,15 +85,37 @@ const Video = () => {
|
|||
}
|
||||
|
||||
const LatestRelease = () => {
|
||||
return (
|
||||
<div className='mx-2.5 lg:mx-0'>
|
||||
<div className='mb-3 mt-7 text-14 font-bold text-neutral-300'>
|
||||
Latest Releases
|
||||
</div>
|
||||
const params = useParams()
|
||||
|
||||
<Album />
|
||||
<Video />
|
||||
</div>
|
||||
const { data: albumsRaw, isLoading: isLoadingAlbums } = useArtistAlbums({
|
||||
id: Number(params.id) || 0,
|
||||
limit: 1000,
|
||||
})
|
||||
|
||||
const album = useMemo(() => albumsRaw?.hotAlbums?.[0], [albumsRaw?.hotAlbums])
|
||||
|
||||
const { data: videos, isLoading: isLoadingVideos } = useArtistMV({
|
||||
id: Number(params.id) || 0,
|
||||
})
|
||||
const video = videos?.mvs?.[0]
|
||||
|
||||
return (
|
||||
<>
|
||||
{!isLoadingVideos && !isLoadingAlbums && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
className='mx-2.5 lg:mx-0'
|
||||
>
|
||||
<div className='mb-3 mt-7 text-14 font-bold text-neutral-300'>
|
||||
Latest Releases
|
||||
</div>
|
||||
|
||||
<Album album={album} />
|
||||
<Video video={video} />
|
||||
</motion.div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import { State as PlayerState } from '@/web/utils/player'
|
|||
import useTracks from '@/web/api/hooks/useTracks'
|
||||
import { css, cx } from '@emotion/css'
|
||||
import Image from '@/web/components/New/Image'
|
||||
import useArtist from '@/web/api/hooks/useArtist'
|
||||
import { useParams } from 'react-router-dom'
|
||||
|
||||
const Track = ({
|
||||
track,
|
||||
|
|
@ -49,7 +51,13 @@ const Track = ({
|
|||
)
|
||||
}
|
||||
|
||||
const Popular = ({ tracks }: { tracks?: Track[] }) => {
|
||||
const Popular = () => {
|
||||
const params = useParams()
|
||||
const { data: artist, isLoading: isLoadingArtist } = useArtist({
|
||||
id: Number(params.id) || 0,
|
||||
})
|
||||
|
||||
const tracks = artist?.hotSongs || []
|
||||
const onPlay = (id: number) => {
|
||||
if (!tracks) return
|
||||
player.playAList(
|
||||
|
|
|
|||
5
packages/web/pages/New/Lyrics.tsx
Normal file
5
packages/web/pages/New/Lyrics.tsx
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
const Lyrics = () => {
|
||||
return <div className='text-white'>开发中</div>
|
||||
}
|
||||
|
||||
export default Lyrics
|
||||
|
|
@ -1,13 +1,19 @@
|
|||
import { css, cx } from '@emotion/css'
|
||||
import useUserArtists from '@/web/api/hooks/useUserArtists'
|
||||
import Tabs from '@/web/components/New/Tabs'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import CoverRow from '@/web/components/New/CoverRow'
|
||||
import useUserPlaylists from '@/web/api/hooks/useUserPlaylists'
|
||||
import useUserAlbums from '@/web/api/hooks/useUserAlbums'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import uiStates from '@/web/states/uiStates'
|
||||
import ArtistRow from '@/web/components/New/ArtistRow'
|
||||
import { playerWidth, topbarHeight } from '@/web/utils/const'
|
||||
import topbarBackground from '@/web/assets/images/topbar-background.png'
|
||||
import useIntersectionObserver from '@/web/hooks/useIntersectionObserver'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import { scrollToBottom } from '@/web/utils/common'
|
||||
import { throttle } from 'lodash-es'
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
|
|
@ -31,7 +37,9 @@ const tabs = [
|
|||
const Albums = () => {
|
||||
const { data: albums } = useUserAlbums()
|
||||
|
||||
return <CoverRow albums={albums?.data} />
|
||||
return (
|
||||
<CoverRow albums={albums?.data} itemTitle='name' itemSubtitle='artist' />
|
||||
)
|
||||
}
|
||||
|
||||
const Playlists = () => {
|
||||
|
|
@ -45,7 +53,7 @@ const Artists = () => {
|
|||
return <ArtistRow artists={artists?.data || []} />
|
||||
}
|
||||
|
||||
const Collections = () => {
|
||||
const CollectionTabs = ({ showBg }: { showBg: boolean }) => {
|
||||
const { librarySelectedTab: selectedTab } = useSnapshot(uiStates)
|
||||
const setSelectedTab = (
|
||||
id: 'playlists' | 'albums' | 'artists' | 'videos'
|
||||
|
|
@ -54,18 +62,73 @@ const Collections = () => {
|
|||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
{/* Topbar background */}
|
||||
<AnimatePresence>
|
||||
{showBg && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className={cx(
|
||||
'pointer-events-none fixed top-0 left-10 z-10 hidden lg:block',
|
||||
css`
|
||||
height: 230px;
|
||||
right: ${playerWidth + 32}px;
|
||||
background-image: url(${topbarBackground});
|
||||
`
|
||||
)}
|
||||
></motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
<Tabs
|
||||
tabs={tabs}
|
||||
value={selectedTab}
|
||||
onChange={(id: string) => setSelectedTab(id)}
|
||||
className='px-2.5 lg:px-0'
|
||||
onChange={(id: string) => {
|
||||
setSelectedTab(id)
|
||||
scrollToBottom(true)
|
||||
}}
|
||||
className={cx(
|
||||
'sticky z-10 -mb-10 px-2.5 lg:px-0',
|
||||
css`
|
||||
top: ${topbarHeight}px;
|
||||
`
|
||||
)}
|
||||
/>
|
||||
<div className='mt-6 px-2.5 lg:px-0'>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const Collections = () => {
|
||||
const { librarySelectedTab: selectedTab } = useSnapshot(uiStates)
|
||||
|
||||
const observePoint = useRef<HTMLDivElement | null>(null)
|
||||
const { onScreen: isScrollReachBottom } =
|
||||
useIntersectionObserver(observePoint)
|
||||
|
||||
const onScroll = throttle(() => {
|
||||
if (isScrollReachBottom) return
|
||||
scrollToBottom(true)
|
||||
}, 500)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<CollectionTabs showBg={isScrollReachBottom} />
|
||||
<div
|
||||
className={cx(
|
||||
'no-scrollbar overflow-y-auto px-2.5 pt-16 pb-16 lg:px-0',
|
||||
css`
|
||||
height: calc(100vh - ${topbarHeight}px);
|
||||
`
|
||||
)}
|
||||
onScroll={onScroll}
|
||||
>
|
||||
{selectedTab === 'albums' && <Albums />}
|
||||
{selectedTab === 'playlists' && <Playlists />}
|
||||
{selectedTab === 'artists' && <Artists />}
|
||||
</div>
|
||||
<div ref={observePoint}></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue