diff --git a/packages/web/components/ArtistRow.tsx b/packages/web/components/ArtistRow.tsx index c74068d..6e58431 100644 --- a/packages/web/components/ArtistRow.tsx +++ b/packages/web/components/ArtistRow.tsx @@ -77,7 +77,7 @@ const ArtistRow = ({ {/* Artists */} {artists && ( -
+
{artists.map(artist => (
diff --git a/packages/web/components/CoverRowVirtual.tsx b/packages/web/components/CoverRowVirtual.tsx index b9cb543..f86219c 100644 --- a/packages/web/components/CoverRowVirtual.tsx +++ b/packages/web/components/CoverRowVirtual.tsx @@ -6,7 +6,7 @@ import { prefetchAlbum } from '@/web/api/hooks/useAlbum' import { prefetchPlaylist } from '@/web/api/hooks/usePlaylist' import { Virtuoso } from 'react-virtuoso' import { useTranslation } from 'react-i18next' -import { +import React, { CSSProperties, FC, ReactNode, @@ -18,13 +18,13 @@ import { } from 'react' import { createPortal } from 'react-dom' import humanNumber from 'human-number' -import { useWhyDidYouUpdate } from 'ahooks' const CoverRow = ({ albums, playlists, title, className, + Footer, }: { title?: string className?: string @@ -32,6 +32,7 @@ const CoverRow = ({ playlists?: Playlist[] containerClassName?: string containerStyle?: CSSProperties + Footer?:React.FC }) => { const navigate = useNavigate() const { showTrackListName } = useSettings() @@ -219,19 +220,20 @@ const CoverRow = ({ style={{ height: 'calc(100vh - 132px)', }} + components={{ + Footer:Footer + }} data={rows} - overscan={15} + overscan={75} itemSize={el => el.getBoundingClientRect().height + 24} totalCount={rows.length} - components={{ - Header: () =>
, - // Footer: () =>
, - }} itemContent={(index, row) => (
- {row.map((item: Item) => ( + { + row.map((item: Item) => ( - ))} + )) + }
)} /> diff --git a/packages/web/components/Tabs.tsx b/packages/web/components/Tabs.tsx index 65e5bac..91aeb3b 100644 --- a/packages/web/components/Tabs.tsx +++ b/packages/web/components/Tabs.tsx @@ -28,12 +28,12 @@ function Tabs({
onChange(tab.id)} > diff --git a/packages/web/components/TrackList/Track.tsx b/packages/web/components/TrackList/Track.tsx new file mode 100644 index 0000000..2437446 --- /dev/null +++ b/packages/web/components/TrackList/Track.tsx @@ -0,0 +1,100 @@ +import Icon from '@/web/components/Icon' +import Wave from '@/web/components/Animation/Wave' +import { formatDuration, resizeImage } from '@/web/utils/common' +import { State as PlayerState } from '@/web/utils/player' +import { css, cx } from '@emotion/css' +import { Fragment, useEffect } from 'react' +import { NavLink } from 'react-router-dom' +import React from 'react' + +const Track = ({ + track, + index, + playingTrackID, + state, + handleClick, + }: { + track?: Track + index: number + playingTrackID: number + state: PlayerState + handleClick: (e: React.MouseEvent, trackID: number) => void + }) => { + return ( +
track && handleClick(e, track.id)} + onContextMenu={e => track && handleClick(e, track.id)} + > + {/* Right part */} +
+ {/* Cover */} + Cover + + {/* Track Name and Artists */} +
+
+ {track?.name} + + {[1318912, 1310848].includes(track?.mark || 0) && ( + + )} +
+
+ {track?.ar.map((a, index) => ( + + {index > 0 && ', '} + + {a.name} + + + ))} +
+
+ + {/* Wave icon */} + {playingTrackID === track?.id && ( +
+ +
+ )} +
+ + {/* Album Name */} +
+ + {track?.al?.name} + +
+ + {/* Duration */} +
+ {formatDuration(track?.dt || 0, 'en-US', 'hh:mm:ss')} +
+
+ ) + } + + export default Track \ No newline at end of file diff --git a/packages/web/components/TrackList.tsx b/packages/web/components/TrackList/TrackList.tsx similarity index 99% rename from packages/web/components/TrackList.tsx rename to packages/web/components/TrackList/TrackList.tsx index df9486e..7f736a6 100644 --- a/packages/web/components/TrackList.tsx +++ b/packages/web/components/TrackList/TrackList.tsx @@ -2,7 +2,7 @@ import { formatDuration } from '@/web/utils/common' import { css, cx } from '@emotion/css' import player from '@/web/states/player' import { useSnapshot } from 'valtio' -import Wave from './Animation/Wave' +import Wave from '../Animation/Wave' import Icon from '@/web/components/Icon' import useIsMobile from '@/web/hooks/useIsMobile' import useUserLikedTracksIDs, { useMutationLikeATrack } from '@/web/api/hooks/useUserLikedTracksIDs' diff --git a/packages/web/pages/Playlist/TrackList.tsx b/packages/web/components/TrackList/TrackListVirtual.tsx similarity index 92% rename from packages/web/pages/Playlist/TrackList.tsx rename to packages/web/components/TrackList/TrackListVirtual.tsx index 2819cd8..f601090 100644 --- a/packages/web/pages/Playlist/TrackList.tsx +++ b/packages/web/components/TrackList/TrackListVirtual.tsx @@ -8,7 +8,6 @@ import { css, cx } from '@emotion/css' import { Fragment, useEffect } from 'react' import { NavLink } from 'react-router-dom' import { useSnapshot } from 'valtio' -import react from '@vitejs/plugin-react-swc' import React from 'react' import { Virtuoso } from 'react-virtuoso' @@ -109,12 +108,14 @@ function TrackList({ onPlay, className, isLoading, + Header, }: { tracks?: Track[] onPlay: (id: number) => void className?: string isLoading?: boolean placeholderRows?: number + Header?: React.FC }) { const { trackID, state } = useSnapshot(player) let playingTrack = tracks?.find(track => track.id === trackID) @@ -136,20 +137,20 @@ function TrackList({ if (e.detail === 2) onPlay?.(trackID) } - return ( -
+
el.getBoundingClientRect().height + 24} totalCount={tracks?.length} - itemContent={(index, row) => ( -
+ itemContent={(index) => ( -
)} />
) } -const TrackListMemo = React.memo(TrackList) -TrackListMemo.displayName = "TrackList" +const TrackListVirtualMemo = React.memo(TrackList) +TrackListVirtualMemo.displayName = "TrackListVirtual" -export default TrackListMemo +export default TrackListVirtualMemo diff --git a/packages/web/hooks/useIntersectionObserver.ts b/packages/web/hooks/useIntersectionObserver.ts index c76f867..7bdbc2a 100644 --- a/packages/web/hooks/useIntersectionObserver.ts +++ b/packages/web/hooks/useIntersectionObserver.ts @@ -1,21 +1,29 @@ -import { useState, useEffect, RefObject } from 'react' +import { useState, useEffect, RefObject } from 'react'; const useIntersectionObserver = (element: RefObject): { onScreen: boolean } => { - const [onScreen, setOnScreen] = useState(false) + const [onScreen, setOnScreen] = useState(false); useEffect(() => { - if (element.current) { - const observer = new IntersectionObserver(([entry]) => setOnScreen(entry.isIntersecting)) - observer.observe(element.current) - return () => { - observer.disconnect() - } + const supportsIntersectionObserver = 'IntersectionObserver' in window; + + if (!supportsIntersectionObserver || !element?.current) { + console.warn('Intersection Observer is not supported in this browser or element is undefined.'); + return; } - }, [element, setOnScreen]) + + const observer = new IntersectionObserver(([entry]) => { + setOnScreen(entry.isIntersecting) + },{threshold:0}); + observer.observe(element.current); + + return () => { + observer.disconnect(); + }; + }, [element, setOnScreen]); return { onScreen, - } -} + }; +}; -export default useIntersectionObserver +export default useIntersectionObserver; diff --git a/packages/web/package.json b/packages/web/package.json index 5e3ce27..f3cde02 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -48,7 +48,7 @@ "react-router-dom": "^6.6.1", "react-use": "^17.4.0", "react-use-measure": "^2.1.1", - "react-virtuoso": "^2.16.6", + "react-virtuoso": "^4.6.2", "valtio": "^1.8.0", "web-audio-api": "^0.2.2" }, diff --git a/packages/web/pages/Album/Album.tsx b/packages/web/pages/Album/Album.tsx index cb8ac39..5e86364 100644 --- a/packages/web/pages/Album/Album.tsx +++ b/packages/web/pages/Album/Album.tsx @@ -2,7 +2,7 @@ import useAlbum from '@/web/api/hooks/useAlbum' import useTracks from '@/web/api/hooks/useTracks' import { useParams } from 'react-router-dom' import PageTransition from '@/web/components/PageTransition' -import TrackList from '@/web/components/TrackList' +import TrackList from '@/web/components/TrackList/TrackList' import player from '@/web/states/player' import toast from 'react-hot-toast' import { useCallback } from 'react' diff --git a/packages/web/pages/Artist/ArtistSongs.tsx b/packages/web/pages/Artist/ArtistSongs.tsx index 8fd73cb..54973e0 100644 --- a/packages/web/pages/Artist/ArtistSongs.tsx +++ b/packages/web/pages/Artist/ArtistSongs.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react' import useArtistSongs from '@/web/api/hooks/useArtistSongs' import useTracks from '@/web/api/hooks/useTracks' import { FetchArtistSongsParams } from '@/shared/api/Artist' -import TrackList from '../Playlist/TrackList' +import TrackList from '../../components/TrackList/TrackListVirtual' import player from '@/web/states/player' import { useParams } from 'react-router-dom' import ScrollPagination from '@/web/components/ScrollPage' diff --git a/packages/web/pages/Browse/Hot.tsx b/packages/web/pages/Browse/Hot.tsx index 6b053c7..9fd3d3c 100644 --- a/packages/web/pages/Browse/Hot.tsx +++ b/packages/web/pages/Browse/Hot.tsx @@ -1,40 +1,70 @@ import { fetchHQPlaylist } from '@/web/api/playlist' +import Loading from '@/web/components/Animation/Loading' import CoverRowVirtual from '@/web/components/CoverRowVirtual' -import { memo, useEffect, useState } from 'react' -import ScrollPagination from '@/web/components/ScrollPage' +import useIntersectionObserver from '@/web/hooks/useIntersectionObserver' +import { useWhyDidYouUpdate } from 'ahooks' +import { throttle } from 'lodash-es' +import { memo, useEffect, useRef, useState } from 'react' const Hot = ({ cat }: { cat: string }) => { const [dataSource, setDatasource] = useState([]) - const [hasMore, setHasMore] = useState(true) - - const getData = async (pageNo: number, pageSize: number): Promise<{ hasMore: boolean }> => { - if (hasMore === false) return { hasMore: false } + const [fetching, setFetching] = useState(false) + const [currentPage, setCurrentPage] = useState(1) + const getData = async (pageSize: number)=> { + + if (hasMore === false) return + setFetching(true) const resp = await fetchHQPlaylist({ cat: cat, limit: pageSize || 50, - before: (pageNo - 1) * pageSize || 0, + before: dataSource.length ? dataSource[dataSource.length-1].updateTime || 0 : 0, }) + setFetching(false) setHasMore(resp.more) - + if(!resp.more) return + + if(dataSource === resp.playlists) return let arrSource = [...dataSource, ...resp.playlists] setDatasource([...new Set(arrSource)]) - return { hasMore: hasMore } } - useEffect(() => { - setDatasource([]) + + const getDataThrottle = throttle((pageSize: number)=>{ + getData(pageSize) + },1000) + + useEffect(()=>{ setHasMore(true) - getData(1, 50) - }, []) - const renderItems = () => { - return - } + getDataThrottle(50) + },[]) + + useEffect(()=>{ + getDataThrottle(50) + },[currentPage]) + + const Footer = ()=>{ + const observePoint = useRef(null) + const { onScreen: isScrollReachBottom } = useIntersectionObserver(observePoint) + const [prevState,setPrevState] = useState(false) + const loadMore = ()=>{ + setCurrentPage(currentPage+1) + } + useEffect(()=>{ + if(prevState != isScrollReachBottom && isScrollReachBottom && hasMore && !fetching){ + setPrevState(isScrollReachBottom) + loadMore() + } + },[isScrollReachBottom]) + + return
{hasMore && }
+ } + return ( <> -
- +
+
) diff --git a/packages/web/pages/Browse/Top.tsx b/packages/web/pages/Browse/Top.tsx index b502288..ff4563d 100644 --- a/packages/web/pages/Browse/Top.tsx +++ b/packages/web/pages/Browse/Top.tsx @@ -2,8 +2,10 @@ import { fetchTopPlaylist } from '@/web/api/playlist' import { PlaylistApiNames } from '@/shared/api/Playlists' import { useQuery } from '@tanstack/react-query' import CoverRowVirtual from '@/web/components/CoverRowVirtual' -import { memo, useCallback, useEffect, useState } from 'react' +import { memo, useCallback, useEffect, useRef, useState } from 'react' import ScrollPagination from '@/web/components/ScrollPage' +import useIntersectionObserver from '@/web/hooks/useIntersectionObserver' +import Loading from '@/web/components/Animation/Loading' const reactQueryOptions = { refetchOnWindowFocus: false, @@ -13,20 +15,21 @@ const reactQueryOptions = { const Top = ({ cat }: { cat: string }) => { const [dataSource, setDatasource] = useState([]) - const [hasMore, setHasMore] = useState(true) + const [fetching, setFetching] = useState(false) + const [currentPage, setCurrentPage] = useState(1) - const getData = async (pageNo: number, pageSize: number): Promise<{ hasMore: boolean }> => { - console.log('top ', cat, ' ', pageNo, ' ', pageSize); - - if (hasMore === false) return { hasMore: false } + const getData = async (pageNo: number, pageSize: number) =>{ + if (hasMore === false) return + setFetching(true) const resp = await fetchTopPlaylist({ cat: cat, limit: pageSize || 50, offset: (pageNo - 1) * pageSize || 0, }) - + setFetching(false) setHasMore(resp.more) + if(!resp.more) return let arrSource = [...dataSource, ...resp.playlists] setDatasource([...new Set(arrSource)]) @@ -37,13 +40,32 @@ const Top = ({ cat }: { cat: string }) => { setHasMore(true) getData(1, 50) }, []) - const renderItems = () => { - return + + useEffect(()=>{ + getData(currentPage,50) + },[currentPage]) + + const Footer = ()=>{ + const observePoint = useRef(null) + const { onScreen: isScrollReachBottom } = useIntersectionObserver(observePoint) + const [prevState,setPrevState] = useState(false) + const loadMore = ()=>{ + setCurrentPage(currentPage+1) + } + useEffect(()=>{ + if(prevState != isScrollReachBottom && isScrollReachBottom && hasMore && !fetching){ + setPrevState(isScrollReachBottom) + loadMore() + } + },[isScrollReachBottom]) + + return
{hasMore && }
} + return ( <> -
- +
+
) diff --git a/packages/web/pages/My/Cloud.tsx b/packages/web/pages/My/Cloud.tsx index 2188e7a..582d029 100644 --- a/packages/web/pages/My/Cloud.tsx +++ b/packages/web/pages/My/Cloud.tsx @@ -1,8 +1,8 @@ -import { useEffect, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import useArtistSongs from '@/web/api/hooks/useArtistSongs' import useTracks from '@/web/api/hooks/useTracks' import { FetchArtistSongsParams } from '@/shared/api/Artist' -import TrackList from '../Playlist/TrackList' +import TrackList from '../../components/TrackList/TrackListVirtual' import player from '@/web/states/player' import { useParams } from 'react-router-dom' import ScrollPagination from '@/web/components/ScrollPage' @@ -11,6 +11,16 @@ import { fetchTracks } from '@/web/api/track' import toast from 'react-hot-toast' import { CloudDiskInfoParam } from '@/shared/api/User' import { cloudDisk } from '@/web/api/user' +import { motion } from 'framer-motion' +import Track from '@/web/components/TrackList/Track' +import { openContextMenu } from '@/web/states/contextMenus' +import { useSnapshot } from 'valtio' +import useIntersectionObserver from '@/web/hooks/useIntersectionObserver' +import { useThrottle } from 'react-use' +import { has, throttle } from 'lodash-es' +import Loading from '@/web/components/Animation/Loading' +import { cx } from '@emotion/css' + const reactQueryOptions = { refetchOnWindowFocus: false, refetchInterval: 1000 * 60 * 60, // 1 hour @@ -18,11 +28,13 @@ const reactQueryOptions = { } const Cloud = () => { + const {trackID, state} = useSnapshot(player) const [dataSource, setDatasource] = useState([]) const [songIDs, setSongIDs] = useState([]) - const params = useParams() const [hasMore, setHasMore] = useState(true) - + const [currentPage, setCurrentPage] = useState(1) + const observePoint = useRef(null) + const { onScreen: isScrollReachBottom } = useIntersectionObserver(observePoint) const getData = async (pageNo: number, pageSize: number) => { if (hasMore === false) return const cloudParams: CloudDiskInfoParam = { @@ -30,7 +42,6 @@ const Cloud = () => { offset: (pageNo - 1) * pageSize || 0, } const resp = await cloudDisk(cloudParams) - console.log('params ', cloudParams, resp) setHasMore(resp.hasMore) @@ -45,18 +56,50 @@ const Cloud = () => { return { hasMore: resp.hasMore } } + const getDataThrottle = throttle((pageNo: number, pageSize: number)=>{ + getData(pageNo,pageSize) + },1000) + + useEffect(()=>{ + if(isScrollReachBottom && hasMore){ + setCurrentPage(currentPage + 1) + getDataThrottle(currentPage,10) + } + },[isScrollReachBottom]) + const onPlay = (trackID: number | null = null) => { player.playAList(songIDs, trackID) } + + const handleClick = (e: React.MouseEvent, trackID: number) => { + if (e.type === 'contextmenu') { + e.preventDefault() + openContextMenu({ + event: e, + type: 'track', + dataSourceID: trackID, + options: { + useCursorPosition: true, + }, + }) + return + } - const renderItems = () => { - return + if (e.detail === 2) onPlay?.(trackID) } - return ( -
- -
+ + {(dataSource)?.map((track,index)=>{ + return + }) + } +
{hasMore && }
+
) } diff --git a/packages/web/pages/My/Collections.tsx b/packages/web/pages/My/Collections.tsx index 4a268d9..0c93ecb 100644 --- a/packages/web/pages/My/Collections.tsx +++ b/packages/web/pages/My/Collections.tsx @@ -1,19 +1,15 @@ import { css, cx } from '@emotion/css' import useUserArtists from '@/web/api/hooks/useUserArtists' import Tabs from '@/web/components/Tabs' -import { useMemo, useRef } from 'react' +import { useEffect, useMemo, useRef, useState } from 'react' import CoverRow from '@/web/components/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/ArtistRow' -import { playerWidth, topbarHeight } from '@/web/utils/const' -import topbarBackground from '@/web/assets/images/topbar-background.png' -import useIntersectionObserver from '@/web/hooks/useIntersectionObserver' +import { topbarHeight } from '@/web/utils/const' import { AnimatePresence, motion } from 'framer-motion' -import { scrollToBottom } from '@/web/utils/common' -import { sampleSize, throttle } from 'lodash-es' import { useTranslation } from 'react-i18next' import VideoRow from '@/web/components/VideoRow' import useUserVideos from '@/web/api/hooks/useUserVideos' @@ -30,15 +26,8 @@ import { IconNames } from '@/web/components/Icon/iconNamesType' const collections = ['daily', 'playlists', 'albums', 'artists', 'videos', 'cloud'] as const type Collection = typeof collections[number] -interface DiscoverPlayList { - id: number - coverUrl: string - large: boolean -} - const Albums = () => { const { data: albums } = useUserAlbums() - return } @@ -88,7 +77,7 @@ const Videos = () => { return } -const CollectionTabs = ({ showBg }: { showBg: boolean }) => { +const CollectionTabs = ({ className }: { className:string }) => { const { t } = useTranslation() const { displayPlaylistsFromNeteaseMusic } = useSnapshot(settings) @@ -96,65 +85,43 @@ const CollectionTabs = ({ showBg }: { showBg: boolean }) => { { id: 'daily', name: t`common.daily`, - iconName: 'netease', + // iconName: 'netease', }, { id: 'albums', name: t`common.album_other`, - iconName: 'album', + // iconName: 'album', }, { id: 'playlists', name: t`common.playlist_other`, - iconName: 'playlist', + // iconName: 'playlist', }, { id: 'artists', name: t`common.artist_other`, - iconName: 'artist', + // iconName: 'artist', }, { id: 'videos', name: t`common.video_other`, - iconName: 'video', + // iconName: 'video', }, { id: 'cloud', name: t`common.cloud`, - iconName: 'cloud', + // iconName: 'cloud', }, ] const { librarySelectedTab: selectedTab } = useSnapshot(persistedUiStates) - const { minimizePlayer } = useSnapshot(persistedUiStates) const setSelectedTab = (id: Collection) => { persistedUiStates.librarySelectedTab = id } return ( -
- {/* Topbar background */} - - {showBg && ( - - )} - -
+
+
{ if (!displayPlaylistsFromNeteaseMusic && tab.id === 'playlists') { @@ -169,12 +136,11 @@ const CollectionTabs = ({ showBg }: { showBg: boolean }) => { className={cx( 'sticky', 'z-10', - // '-mb-10', 'px-2.5 lg:px-0' )} - style={{ - top: `${topbarHeight}px`, - }} + // style={{ + // top: `${topbarHeight}px`, + // }} />
{/* {selectedTab == 'cloud' && } */}
@@ -184,30 +150,17 @@ const CollectionTabs = ({ showBg }: { showBg: boolean }) => { const Collections = () => { const { librarySelectedTab: selectedTab } = useSnapshot(persistedUiStates) - - const observePoint = useRef(null) - const { onScreen: isScrollReachBottom } = useIntersectionObserver(observePoint) - - const onScroll = throttle(() => { - if (isScrollReachBottom) return - scrollToBottom(true) - }, 500) - return ( - -
+ +
{selectedTab === 'daily' && } {selectedTab === 'albums' && } {selectedTab === 'playlists' && } {selectedTab === 'artists' && } {selectedTab === 'videos' && } - {selectedTab === 'cloud' && } + {selectedTab === 'cloud' && }
-
) } diff --git a/packages/web/pages/My/Daily.tsx b/packages/web/pages/My/Daily.tsx index 68e4bfd..6b169d1 100644 --- a/packages/web/pages/My/Daily.tsx +++ b/packages/web/pages/My/Daily.tsx @@ -5,9 +5,21 @@ import { } from '@/web/api/playlist' import { PlaylistApiNames } from '@/shared/api/Playlists' import { useQuery } from '@tanstack/react-query' -import TrackList from '../Playlist/TrackList' +import TrackList from '@/web/components/TrackList/TrackListVirtual' import player from '@/web/states/player' -import Loading from '@/web/components/Animation/Loading' +import Icon from '@/web/components/Icon' +import Wave from '@/web/components/Animation/Wave' +import { formatDuration, resizeImage } from '@/web/utils/common' +import { State as PlayerState } from '@/web/utils/player' +import { css, cx } from '@emotion/css' +import { Fragment, useEffect } from 'react' +import { NavLink } from 'react-router-dom' +import React from 'react' +import { useSnapshot } from 'valtio' +import { openContextMenu } from '@/web/states/contextMenus' +import { motion } from 'framer-motion' +import { map } from 'lodash-es' +import Track from '@/web/components/TrackList/Track' const reactQueryOptions = { refetchOnWindowFocus: false, @@ -15,32 +27,54 @@ const reactQueryOptions = { refetchOnMount: false, } -const Daily = () => { +const Daily = ({ className }: { className?: string }) => { + const { data: dailyRecommendSongs, isLoading: isLoadingDaily } = useQuery( [PlaylistApiNames.FetchDailyRecommendSongs], () => fetchDailyRecommendSongs(), reactQueryOptions ) + const { trackID, state } = useSnapshot(player) + const tracks = dailyRecommendSongs?.data?.dailySongs || [] - const songIDs = - dailyRecommendSongs?.data?.dailySongs?.map(track => { - return track.id - }) ?? [] + let playingTrack = tracks?.find(track => track.id === trackID) + const songIDs = tracks.map(track => { + return track.id + }) ?? [] const onPlay = (trackID: number | null = null) => { player.playAList(songIDs, trackID) } + const handleClick = (e: React.MouseEvent, trackID: number) => { + if (isLoadingDaily) return + if (e.type === 'contextmenu') { + e.preventDefault() + openContextMenu({ + event: e, + type: 'track', + dataSourceID: trackID, + options: { + useCursorPosition: true, + }, + }) + return + } + + if (e.detail === 2) onPlay?.(trackID) + } + return ( - <> - {isLoadingDaily ? ( -
- -
- ) : ( - - )} - + + {(isLoadingDaily ? [] : tracks)?.map((track, index) => { + return + })} + ) } diff --git a/packages/web/pages/Playlist/Header.tsx b/packages/web/pages/Playlist/Header.tsx index 608c57b..e18ea28 100644 --- a/packages/web/pages/Playlist/Header.tsx +++ b/packages/web/pages/Playlist/Header.tsx @@ -4,6 +4,7 @@ import useUserPlaylists, { useMutationLikeAPlaylist } from '@/web/api/hooks/useU import TrackListHeader from '@/web/components/TrackListHeader' import player from '@/web/states/player' import { formatDate } from '@/web/utils/common' +import { useWhyDidYouUpdate } from 'ahooks' import { useMemo } from 'react' import { useParams } from 'react-router-dom' @@ -50,9 +51,8 @@ const Header = () => { const onLike = async () => { likeAPlaylist.mutateAsync(playlist?.id || Number(params.id)) } - return ( - { return ( -
-
+
=10'} - peerDependencies: - react: '>=16' - dependencies: - '@virtuoso.dev/urx': 0.2.13 - react: 18.2.0 - dev: false - - /@virtuoso.dev/urx@0.2.13: - resolution: {integrity: sha512-iirJNv92A1ZWxoOHHDYW/1KPoi83939o83iUBQHIim0i3tMeSKEh+bxhJdTHQ86Mr4uXx9xGUTq69cp52ZP8Xw==} - dev: false - /@vitejs/plugin-react-swc@3.2.0(vite@4.5.0): resolution: {integrity: sha512-IcBoXL/mcH7JdQr/nfDlDwTdIaH8Rg7LpfQDF4nAht+juHWIuv6WhpKPCSfY4+zztAaB07qdBoFz1XCZsgo3pQ==} peerDependencies: @@ -10192,15 +10178,13 @@ packages: tslib: 2.4.0 dev: false - /react-virtuoso@2.16.6(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-xpm/edDfrX2auYjnTmzdzpyxppsIvkEWtBgy1In/qMmJHMuXwhTSszCX2zsUs5SbwNCiEAbXjV7Qg1iXVa/QsA==} + /react-virtuoso@4.6.2(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-vvlqvzPif+MvBrJ09+hJJrVY0xJK9yran+A+/1iwY78k0YCVKsyoNPqoLxOxzYPggspNBNXqUXEcvckN29OxyQ==} engines: {node: '>=10'} peerDependencies: react: '>=16 || >=17 || >= 18' react-dom: '>=16 || >=17 || >= 18' dependencies: - '@virtuoso.dev/react-urx': 0.2.13(react@18.2.0) - '@virtuoso.dev/urx': 0.2.13 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false