From 222fb02355ee0d5307ca3364f0750138518ead98 Mon Sep 17 00:00:00 2001 From: qier222 Date: Mon, 11 Jul 2022 11:06:41 +0800 Subject: [PATCH] feat: updates --- packages/electron/main/appleMusic.ts | 112 ++++++--------- packages/electron/main/cache.ts | 1 + packages/electron/main/index.ts | 5 +- packages/electron/main/ipcMain.ts | 20 ++- packages/shared/CacheAPIs.ts | 4 + packages/shared/IpcChannels.ts | 5 +- packages/shared/api/User.ts | 14 ++ packages/shared/store.ts | 54 ------- packages/web/AppNew.tsx | 2 +- packages/web/IpcRendererReact.tsx | 2 +- .../web/api/hooks/useUserListenedRecords.ts | 15 +- packages/web/api/hooks/useUserPlaylists.ts | 1 + packages/web/api/user.ts | 15 +- packages/web/assets/icons/player-handler.svg | 3 + packages/web/components/ArtistsInline.tsx | 2 +- packages/web/components/FMCard.tsx | 2 +- packages/web/components/Lyric/Lyric.tsx | 2 +- packages/web/components/Lyric/Lyric2.tsx | 2 +- packages/web/components/Lyric/LyricPanel.tsx | 10 +- packages/web/components/Lyric/Player.tsx | 5 +- .../web/components/New/BlurBackground.tsx | 38 +++++ packages/web/components/New/CoverWall.tsx | 2 - packages/web/components/New/Layout.tsx | 9 +- packages/web/components/New/LayoutMobile.tsx | 8 +- packages/web/components/New/Login/Login.tsx | 18 +-- .../New/Login/LoginWithPhoneOrEmail.tsx | 25 ++-- .../components/New/Login/LoginWithQRCode.tsx | 18 +-- packages/web/components/New/Main.tsx | 23 ++- packages/web/components/New/MenuBar.tsx | 5 - packages/web/components/New/NowPlaying.tsx | 20 +-- .../web/components/New/PageTransition.tsx | 2 +- packages/web/components/New/Player.tsx | 2 +- packages/web/components/New/PlayerMobile.tsx | 49 +++++-- packages/web/components/New/PlayingNext.tsx | 133 ++++++++---------- .../web/components/New/PlayingNextMobile.tsx | 113 ++++++++------- .../web/components/New/ScrollRestoration.tsx | 2 +- packages/web/components/New/Topbar/Avatar.tsx | 6 +- .../components/New/Topbar/TopbarDesktop.tsx | 51 +++++-- packages/web/components/New/TrackList.tsx | 2 +- .../web/components/New/TrackListHeader.tsx | 22 +-- packages/web/components/Player.tsx | 8 +- packages/web/components/Sidebar.tsx | 2 +- packages/web/components/TitleBar.tsx | 2 +- packages/web/components/TracksAlbum.tsx | 2 +- packages/web/components/TracksGrid.tsx | 2 +- packages/web/components/TracksList.tsx | 2 +- packages/web/hooks/useIntersectionObserver.ts | 25 ++++ packages/web/hooks/useVideoCover.ts | 12 +- packages/web/ipcRenderer.ts | 2 +- packages/web/main.tsx | 16 +-- packages/web/package.json | 7 +- packages/web/pages/Album.tsx | 2 +- packages/web/pages/Artist.tsx | 2 +- packages/web/pages/Library.tsx | 2 +- packages/web/pages/New/Album.tsx | 10 +- .../pages/New/Artist/Header/ArtistInfo.tsx | 2 +- .../New/Artist/Header/BlurBackground.tsx | 26 ---- .../web/pages/New/Artist/Header/Header.tsx | 3 +- .../pages/New/Artist/Header/LatestRelease.tsx | 12 +- packages/web/pages/New/Artist/Popular.tsx | 2 +- packages/web/pages/New/Browse.tsx | 2 +- packages/web/pages/New/Discover.tsx | 2 +- packages/web/pages/New/My/Collections.tsx | 9 +- .../web/pages/New/My/PlayLikedSongsCard.tsx | 2 +- packages/web/pages/New/Playlist.tsx | 10 +- packages/web/pages/Playlist.tsx | 2 +- packages/web/pages/Search/Search.tsx | 2 +- packages/web/pages/Settings/Appearance.tsx | 6 +- packages/web/states/persistedUiStates.ts | 19 +++ packages/web/states/player.ts | 18 +++ .../web/{store => states}/scrollPositions.ts | 0 packages/web/states/settings.ts | 53 +++++++ packages/web/states/uiStates.ts | 19 +++ packages/web/store/index.ts | 35 ----- packages/web/utils/theme.ts | 15 +- packages/web/vite.config.ts | 4 +- pnpm-lock.yaml | 42 ++++-- 77 files changed, 654 insertions(+), 551 deletions(-) delete mode 100644 packages/shared/store.ts create mode 100644 packages/web/assets/icons/player-handler.svg create mode 100644 packages/web/components/New/BlurBackground.tsx create mode 100644 packages/web/hooks/useIntersectionObserver.ts delete mode 100644 packages/web/pages/New/Artist/Header/BlurBackground.tsx create mode 100644 packages/web/states/persistedUiStates.ts create mode 100644 packages/web/states/player.ts rename packages/web/{store => states}/scrollPositions.ts (100%) create mode 100644 packages/web/states/settings.ts create mode 100644 packages/web/states/uiStates.ts delete mode 100644 packages/web/store/index.ts diff --git a/packages/electron/main/appleMusic.ts b/packages/electron/main/appleMusic.ts index 73d2585..dfdfcd5 100644 --- a/packages/electron/main/appleMusic.ts +++ b/packages/electron/main/appleMusic.ts @@ -1,84 +1,56 @@ -import { logger } from '@sentry/utils' +import log from './log' import axios from 'axios' -// 'https://mvod.itunes.apple.com/itunes-assets/HLSMusic116/v4/de/52/95/de52957b-fcf1-ae96-b114-0445cb8c41d4/P359420813_default.m3u8' - -const searchAlbum = async ( - keyword: string -): Promise< - | { - id: string - href: string - attributes: { - artistName: string - url: string - name: string - editorialNotes?: { - standard: string - short: string - } - } - } - | undefined -> => { - const r = await axios.get( - `https://amp-api.music.apple.com/v1/catalog/cn/search`, - { - params: { - term: keyword, - l: 'zh-cn', - platform: 'web', - types: 'albums', - limit: 1, - }, - headers: { - authorization: 'Bearer xxxxxx', // required token - }, - } - ) - - return r.data?.results?.albums?.data?.[0] -} +const token = + 'Bearer eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IldlYlBsYXlLaWQifQ.eyJpc3MiOiJBTVBXZWJQbGF5IiwiaWF0IjoxNjQ2NjU1MDgwLCJleHAiOjE2NjIyMDcwODB9.pyOrt2FmP0cHkzYtO8KiEzQL2t1qpRszzxIYbLH7faXSddo6PQei771Ja3aGwGVU4hD99lZAw7bwat60iBcGiQ' export const getCoverVideo = async ({ name, - artists, + artist, }: { name: string - artists: string[] + artist: string }): Promise => { - const keyword = `${artists.join(' ')} ${name}` - logger.debug(`[appleMusic] getCoverVideo: ${keyword}`) - const album = await searchAlbum(keyword).catch(e => { - console.log(e) - logger.debug('[appleMusic] Search album error', e) + const keyword = `${artist} ${name}` + log.debug(`[appleMusic] getCoverVideo: ${keyword}`) + const searchResult = await axios({ + method: 'GET', + url: 'https://amp-api.music.apple.com/v1/catalog/us/search', + params: { + term: keyword, + types: 'albums', + 'fields[albums]': 'artistName,name,editorialVideo', + platform: 'web', + limit: '1', + }, + headers: { + Authority: 'amp-api.music.apple.com', + Accept: '*/*', + Authorization: token, + Referer: 'http://localhost:9000/', + 'Sec-Fetch-Dest': 'empty', + 'Sec-Fetch-Mode': 'cors', + 'Sec-Fetch-Site': 'cross-site', + 'User-Agent': + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Cider/1.5.1 Chrome/100.0.4896.160 Electron/18.3.3 Safari/537.36', + 'Accept-Encoding': 'gzip', + }, + }).catch(e => { + log.debug('[appleMusic] Search album error', e) }) - const url = album?.attributes.url - - if (!url) { - logger.info('[appleMusic] no url') + const album = searchResult?.data?.results?.albums?.data?.[0] + if (!album) { + log.debug('[appleMusic] No album found on apple music') return } + log.debug( + `[appleMusic] Got ${album?.id}: ${album?.attributes?.name} by ${album?.attributes?.artistName}` + ) - let { data: html } = await axios.get(url) - if (!html) return - - const regex = - /', '') - html = JSON.parse(html) - const data = JSON.parse(html[Object.keys(html)[1]]) - const m3u8 = - data?.d?.[0]?.attributes?.editorialVideo?.motionSquareVideo1x1?.video - - logger.debug(`[appleMusic] ${m3u8}`) - - return m3u8 + const url = album?.attributes?.editorialVideo?.motionSquareVideo1x1?.video + if (!url) { + log.debug('[appleMusic] Album does not have video cover') + } + return url } diff --git a/packages/electron/main/cache.ts b/packages/electron/main/cache.ts index d1156aa..eba7ae1 100644 --- a/packages/electron/main/cache.ts +++ b/packages/electron/main/cache.ts @@ -21,6 +21,7 @@ class Cache { case APIs.RecommendResource: case APIs.UserAlbums: case APIs.UserArtists: + case APIs.ListenedRecords: case APIs.Likelist: { if (!data) return db.upsert(Tables.AccountData, { diff --git a/packages/electron/main/index.ts b/packages/electron/main/index.ts index d0a4345..5c073f4 100644 --- a/packages/electron/main/index.ts +++ b/packages/electron/main/index.ts @@ -16,7 +16,6 @@ import { createTray, YPMTray } from './tray' import { IpcChannels } from '@/shared/IpcChannels' import { createTaskbar, Thumbar } from './windowsTaskbar' import { createMenu } from './menu' -import { Store as State, initialState } from '@/shared/store' import { isDev, isWindows, isLinux, isMac } from './utils' export interface TypedElectronStore { @@ -26,7 +25,7 @@ export interface TypedElectronStore { x?: number y?: number } - settings: State['settings'] + // settings: State['settings'] } class Main { @@ -39,7 +38,7 @@ class Main { width: 1440, height: 1024, }, - settings: initialState.settings, + // settings: initialState.settings, }, }) diff --git a/packages/electron/main/ipcMain.ts b/packages/electron/main/ipcMain.ts index dc191f4..1ab04f3 100644 --- a/packages/electron/main/ipcMain.ts +++ b/packages/electron/main/ipcMain.ts @@ -155,19 +155,17 @@ function initOtherIpcMain() { }) /** - * 缓存动态专辑封面 + * 获取动态封面 */ - on(IpcChannels.SetVideoCover, (event, args) => { - const { id, url } = args - cache.set(APIs.VideoCover, { id, url }) - }) + handle(IpcChannels.GetVideoCover, async (event, { id, name, artist }) => { + const fromCache = cache.get(APIs.VideoCover, { id }) + if (fromCache) { + return fromCache === 'no' ? undefined : fromCache + } - /** - * 获取动态专辑封面 - */ - on(IpcChannels.GetVideoCover, (event, args) => { - const { id } = args - event.returnValue = cache.get(APIs.VideoCover, { id }) + const fromApple = await getCoverVideo({ name, artist }) + cache.set(APIs.VideoCover, { id, url: fromApple || 'no' }) + return fromApple }) /** diff --git a/packages/shared/CacheAPIs.ts b/packages/shared/CacheAPIs.ts index 43fd46c..ec27932 100644 --- a/packages/shared/CacheAPIs.ts +++ b/packages/shared/CacheAPIs.ts @@ -5,6 +5,7 @@ import { } from './api/Artist' import { FetchAlbumResponse } from './api/Album' import { + FetchListenedRecordsResponse, FetchUserAccountResponse, FetchUserAlbumsResponse, FetchUserArtistsResponse, @@ -37,6 +38,7 @@ export const enum APIs { UserArtists = 'artist/sublist', UserPlaylist = 'user/playlist', SimilarArtist = 'simi/artist', + ListenedRecords = 'user/record', // not netease api CoverColor = 'cover_color', @@ -61,6 +63,7 @@ export interface APIsParams { [APIs.SimilarArtist]: { id: number } [APIs.CoverColor]: { id: number } [APIs.VideoCover]: { id: number } + [APIs.ListenedRecords]: { id: number; type: number } } export interface APIsResponse { @@ -81,4 +84,5 @@ export interface APIsResponse { [APIs.SimilarArtist]: FetchSimilarArtistsResponse [APIs.CoverColor]: string | undefined [APIs.VideoCover]: string | undefined + [APIs.ListenedRecords]: FetchListenedRecordsResponse } diff --git a/packages/shared/IpcChannels.ts b/packages/shared/IpcChannels.ts index d6130bb..9f82468 100644 --- a/packages/shared/IpcChannels.ts +++ b/packages/shared/IpcChannels.ts @@ -1,6 +1,5 @@ import { APIs } from './CacheAPIs' import { RepeatMode } from './playerDataTypes' -import { Store } from '@/shared/store' export const enum IpcChannels { ClearAPICache = 'ClearAPICache', @@ -57,10 +56,10 @@ export interface IpcChannelsParams { [IpcChannels.Repeat]: { mode: RepeatMode } - [IpcChannels.SyncSettings]: Store['settings'] + [IpcChannels.SyncSettings]: any [IpcChannels.GetAudioCacheSize]: void [IpcChannels.ResetWindowSize]: void - [IpcChannels.GetVideoCover]: { id: number } + [IpcChannels.GetVideoCover]: { id: number; name: string; artist: string } [IpcChannels.SetVideoCover]: { id: number; url: string } } diff --git a/packages/shared/api/User.ts b/packages/shared/api/User.ts index ef996e0..ad3c29c 100644 --- a/packages/shared/api/User.ts +++ b/packages/shared/api/User.ts @@ -107,3 +107,17 @@ export interface FetchUserArtistsResponse { count: number data: Artist[] } + +// 听歌排名 +export interface FetchListenedRecordsParams { + uid: number // 用户id + type: number // type=1 时只返回 weekData, type=0 时返回 allData +} +export interface FetchListenedRecordsResponse { + code: number + weekData: { + playCount: number + score: number + song: Track + }[] +} diff --git a/packages/shared/store.ts b/packages/shared/store.ts deleted file mode 100644 index a3aa387..0000000 --- a/packages/shared/store.ts +++ /dev/null @@ -1,54 +0,0 @@ -export interface Store { - uiStates: { - showLyricPanel: boolean - showLoginPanel: boolean - } - persistedUiStates: { - loginPhoneCountryCode: string - loginType: 'phone' | 'email' | 'qrCode' - } - settings: { - showSidebar: boolean - accentColor: string - unm: { - enabled: boolean - sources: Array< - 'migu' | 'kuwo' | 'kugou' | 'ytdl' | 'qq' | 'bilibili' | 'joox' - > - searchMode: 'order-first' | 'fast-first' - proxy: null | { - protocol: 'http' | 'https' | 'socks5' - host: string - port: number - username?: string - password?: string - } - cookies: { - qq?: string - joox?: string - } - } - } -} - -export const initialState: Store = { - uiStates: { - showLyricPanel: false, - showLoginPanel: false, - }, - persistedUiStates: { - loginPhoneCountryCode: '+86', - loginType: 'qrCode', - }, - settings: { - showSidebar: true, - accentColor: 'blue', - unm: { - enabled: true, - sources: ['migu'], - searchMode: 'order-first', - proxy: null, - cookies: {}, - }, - }, -} diff --git a/packages/web/AppNew.tsx b/packages/web/AppNew.tsx index 96ed082..601b51e 100644 --- a/packages/web/AppNew.tsx +++ b/packages/web/AppNew.tsx @@ -13,7 +13,7 @@ const App = () => { return ( -
+
{window.env?.isEnableTitlebar && } {isMobile ? : } diff --git a/packages/web/IpcRendererReact.tsx b/packages/web/IpcRendererReact.tsx index 1226b13..779ccc5 100644 --- a/packages/web/IpcRendererReact.tsx +++ b/packages/web/IpcRendererReact.tsx @@ -2,7 +2,7 @@ import { IpcChannels } from '@/shared/IpcChannels' import useUserLikedTracksIDs, { useMutationLikeATrack, } from '@/web/api/hooks/useUserLikedTracksIDs' -import { player } from '@/web/store' +import player from '@/web/states/player' import useIpcRenderer from '@/web/hooks/useIpcRenderer' import { State as PlayerState } from '@/web/utils/player' import { useEffect, useRef, useState } from 'react' diff --git a/packages/web/api/hooks/useUserListenedRecords.ts b/packages/web/api/hooks/useUserListenedRecords.ts index d2a93eb..5b57e5d 100644 --- a/packages/web/api/hooks/useUserListenedRecords.ts +++ b/packages/web/api/hooks/useUserListenedRecords.ts @@ -1,8 +1,5 @@ -import { - fetchListenedRecords, - FetchListenedRecordsParams, -} from '@/web/api/user' -import { UserApiNames } from '@/shared/api/User' +import { fetchListenedRecords } from '@/web/api/user' +import { UserApiNames, FetchListenedRecordsResponse } from '@/shared/api/User' import { APIs } from '@/shared/CacheAPIs' import { IpcChannels } from '@/shared/IpcChannels' import { useQuery } from 'react-query' @@ -24,10 +21,10 @@ export default function useUserListenedRecords(params: { { refetchOnWindowFocus: false, enabled: !!uid, - // placeholderData: (): FetchUserArtistsResponse => - // window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, { - // api: APIs.UserArtists, - // }), + placeholderData: (): FetchListenedRecordsResponse => + window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, { + api: APIs.UserArtists, + }), } ) } diff --git a/packages/web/api/hooks/useUserPlaylists.ts b/packages/web/api/hooks/useUserPlaylists.ts index ffa8345..8408d8d 100644 --- a/packages/web/api/hooks/useUserPlaylists.ts +++ b/packages/web/api/hooks/useUserPlaylists.ts @@ -5,6 +5,7 @@ import { IpcChannels } from '@/shared/IpcChannels' import { APIs } from '@/shared/CacheAPIs' import { fetchUserPlaylists } from '@/web/api/user' import { FetchUserPlaylistsResponse, UserApiNames } from '@/shared/api/User' +import toast from 'react-hot-toast' export default function useUserPlaylists() { const { data: user } = useUser() diff --git a/packages/web/api/user.ts b/packages/web/api/user.ts index 6092bd6..0896288 100644 --- a/packages/web/api/user.ts +++ b/packages/web/api/user.ts @@ -8,6 +8,8 @@ import { FetchUserAlbumsParams, FetchUserAlbumsResponse, FetchUserArtistsResponse, + FetchListenedRecordsParams, + FetchListenedRecordsResponse, } from '@/shared/api/User' /** @@ -78,18 +80,7 @@ export function scrobble(params: { }) } -export interface FetchListenedRecordsParams { - uid: number // 用户id - type: number // type=1 时只返回 weekData, type=0 时返回 allData -} -export interface FetchListenedRecordsResponse { - code: number - weekData: { - playCount: number - score: number - song: Track - }[] -} +// 用户最近听歌排名 export function fetchListenedRecords( params: FetchListenedRecordsParams ): Promise { diff --git a/packages/web/assets/icons/player-handler.svg b/packages/web/assets/icons/player-handler.svg new file mode 100644 index 0000000..5079344 --- /dev/null +++ b/packages/web/assets/icons/player-handler.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/web/components/ArtistsInline.tsx b/packages/web/components/ArtistsInline.tsx index 1a29af5..259535b 100644 --- a/packages/web/components/ArtistsInline.tsx +++ b/packages/web/components/ArtistsInline.tsx @@ -1,5 +1,5 @@ import { useNavigate } from 'react-router-dom' -import {cx} from '@emotion/css' +import { cx } from '@emotion/css' const ArtistInline = ({ artists, diff --git a/packages/web/components/FMCard.tsx b/packages/web/components/FMCard.tsx index fc0bff8..c490ba2 100644 --- a/packages/web/components/FMCard.tsx +++ b/packages/web/components/FMCard.tsx @@ -1,4 +1,4 @@ -import { player } from '@/web/store' +import player from '@/web/states/player' import { resizeImage } from '@/web/utils/common' import Icon from './Icon' import ArtistInline from './ArtistsInline' diff --git a/packages/web/components/Lyric/Lyric.tsx b/packages/web/components/Lyric/Lyric.tsx index 0b2cdbe..346f450 100644 --- a/packages/web/components/Lyric/Lyric.tsx +++ b/packages/web/components/Lyric/Lyric.tsx @@ -1,5 +1,5 @@ import useLyric from '@/web/api/hooks/useLyric' -import { player } from '@/web/store' +import player from '@/web/states/player' import { motion } from 'framer-motion' import { lyricParser } from '@/web/utils/lyric' import { useMemo } from 'react' diff --git a/packages/web/components/Lyric/Lyric2.tsx b/packages/web/components/Lyric/Lyric2.tsx index 5f5c628..6176d84 100644 --- a/packages/web/components/Lyric/Lyric2.tsx +++ b/packages/web/components/Lyric/Lyric2.tsx @@ -1,5 +1,5 @@ import useLyric from '@/web/api/hooks/useLyric' -import { player } from '@/web/store' +import player from '@/web/states/player' import { motion, useMotionValue } from 'framer-motion' import { lyricParser } from '@/web/utils/lyric' import { useWindowSize } from 'react-use' diff --git a/packages/web/components/Lyric/LyricPanel.tsx b/packages/web/components/Lyric/LyricPanel.tsx index f178b19..22c6c99 100644 --- a/packages/web/components/Lyric/LyricPanel.tsx +++ b/packages/web/components/Lyric/LyricPanel.tsx @@ -1,5 +1,5 @@ import Player from './Player' -import { player, state } from '@/web/store' +import player from '@/web/states/player' import { getCoverColor } from '@/web/utils/common' import { colord } from 'colord' import IconButton from '../IconButton' @@ -13,7 +13,7 @@ import { useMemo } from 'react' import { useSnapshot } from 'valtio' const LyricPanel = () => { - const stateSnapshot = useSnapshot(state) + const stateSnapshot = useSnapshot(player) const playerSnapshot = useSnapshot(player) const track = useMemo(() => playerSnapshot.track, [playerSnapshot.track]) @@ -55,7 +55,11 @@ const LyricPanel = () => {
- (state.uiStates.showLyricPanel = false)}> + { + // + }} + >
diff --git a/packages/web/components/Lyric/Player.tsx b/packages/web/components/Lyric/Player.tsx index f9978cc..3d7d529 100644 --- a/packages/web/components/Lyric/Player.tsx +++ b/packages/web/components/Lyric/Player.tsx @@ -1,7 +1,7 @@ import useUserLikedTracksIDs, { useMutationLikeATrack, } from '@/web/api/hooks/useUserLikedTracksIDs' -import { player, state } from '@/web/store' +import player from '@/web/states/player' import { resizeImage } from '@/web/utils/common' import ArtistInline from '../ArtistsInline' @@ -23,7 +23,6 @@ const PlayingTrack = () => { const id = track?.al?.id if (!id) return navigate(`/album/${id}`) - state.uiStates.showLyricPanel = false } const trackListSource = useMemo( @@ -38,12 +37,10 @@ const PlayingTrack = () => { if (!hasListSource) return navigate(`/${trackListSource.type}/${trackListSource.id}`) - state.uiStates.showLyricPanel = false } const toArtist = (id: number) => { navigate(`/artist/${id}`) - state.uiStates.showLyricPanel = false } return ( diff --git a/packages/web/components/New/BlurBackground.tsx b/packages/web/components/New/BlurBackground.tsx new file mode 100644 index 0000000..c75beab --- /dev/null +++ b/packages/web/components/New/BlurBackground.tsx @@ -0,0 +1,38 @@ +import { resizeImage } from '@/web/utils/common' +import { cx, css } from '@emotion/css' +import useIsMobile from '@/web/hooks/useIsMobile' +import { useSnapshot } from 'valtio' +import uiStates from '@/web/states/uiStates' +import { AnimatePresence, motion } from 'framer-motion' +import { ease } from '@/web/utils/const' + +const BlurBackground = ({ cover }: { cover?: string }) => { + const isMobile = useIsMobile() + const { hideTopbarBackground } = useSnapshot(uiStates) + + return ( + + {!isMobile && cover && hideTopbarBackground && ( + + )} + + ) +} + +export default BlurBackground diff --git a/packages/web/components/New/CoverWall.tsx b/packages/web/components/New/CoverWall.tsx index 4a3032a..1e440aa 100644 --- a/packages/web/components/New/CoverWall.tsx +++ b/packages/web/components/New/CoverWall.tsx @@ -46,8 +46,6 @@ const CoverWall = ({ sizes[album.large ? 'large' : 'small'][breakpoint] )} key={album.id} - alt='Album Cover' - placeholder={null} className={cx( 'aspect-square h-full w-full rounded-20 lg:rounded-24', album.large && 'col-span-2 row-span-2' diff --git a/packages/web/components/New/Layout.tsx b/packages/web/components/New/Layout.tsx index 9fce077..b48a282 100644 --- a/packages/web/components/New/Layout.tsx +++ b/packages/web/components/New/Layout.tsx @@ -3,9 +3,10 @@ import Player from '@/web/components/New/Player' import MenuBar from '@/web/components/New/MenuBar' import Topbar from '@/web/components/New/Topbar/TopbarDesktop' import { css, cx } from '@emotion/css' -import { player } from '@/web/store' +import player from '@/web/states/player' import { useSnapshot } from 'valtio' import Login from './Login' +import TrafficLight from './TrafficLight' const Layout = () => { const playerSnapshot = useSnapshot(player) @@ -39,6 +40,12 @@ const Layout = () => {
{showPlayer && } + + {window.env?.isMac && ( +
+ +
+ )}
) } diff --git a/packages/web/components/New/LayoutMobile.tsx b/packages/web/components/New/LayoutMobile.tsx index e98272c..4595a25 100644 --- a/packages/web/components/New/LayoutMobile.tsx +++ b/packages/web/components/New/LayoutMobile.tsx @@ -1,7 +1,7 @@ import Player from '@/web/components/New/PlayerMobile' import { css, cx } from '@emotion/css' import { useMemo } from 'react' -import { player } from '@/web/store' +import player from '@/web/states/player' import { useSnapshot } from 'valtio' import Router from '@/web/components/New/Router' import MenuBar from './MenuBar' @@ -17,8 +17,8 @@ const LayoutMobile = () => { const location = useLocation() return ( -
-
+
+
{location.pathname === '/' && }
@@ -48,7 +48,7 @@ const LayoutMobile = () => { )} - {/* */} +
diff --git a/packages/web/components/New/Login/Login.tsx b/packages/web/components/New/Login/Login.tsx index 103f719..17e5ae2 100644 --- a/packages/web/components/New/Login/Login.tsx +++ b/packages/web/components/New/Login/Login.tsx @@ -1,13 +1,13 @@ import { cx, css } from '@emotion/css' import { useEffect, useState } from 'react' -import { state } from '@/web/store' import { useSnapshot } from 'valtio' - +import uiStates from '@/web/states/uiStates' import { AnimatePresence, motion, useAnimation } from 'framer-motion' import { ease } from '@/web/utils/const' import Icon from '@/web/components/Icon' import LoginWithPhoneOrEmail from './LoginWithPhoneOrEmail' import LoginWithQRCode from './LoginWithQRCode' +import persistedUiStates from '@/web/states/persistedUiStates' const OR = ({ children, @@ -37,9 +37,10 @@ const OR = ({ } const Login = () => { - const { uiStates, persistedUiStates } = useSnapshot(state) + const { loginType } = useSnapshot(persistedUiStates) + const { showLoginPanel } = useSnapshot(uiStates) const [cardType, setCardType] = useState<'qrCode' | 'phone/email'>( - persistedUiStates.loginType === 'qrCode' ? 'qrCode' : 'phone/email' + loginType === 'qrCode' ? 'qrCode' : 'phone/email' ) const animateCard = useAnimation() @@ -52,8 +53,7 @@ const Login = () => { }) setCardType(cardType === 'qrCode' ? 'phone/email' : 'qrCode') - state.persistedUiStates.loginType = - cardType === 'qrCode' ? 'phone' : 'qrCode' + persistedUiStates.loginType = cardType === 'qrCode' ? 'phone' : 'qrCode' await animateCard.start({ rotateY: 0, @@ -66,7 +66,7 @@ const Login = () => { <> {/* Blur bg */} - {uiStates.showLoginPanel && ( + {showLoginPanel && ( { {/* Content */} - {uiStates.showLoginPanel && ( + {showLoginPanel && (
{ (state.uiStates.showLoginPanel = false)} + onClick={() => (uiStates.showLoginPanel = false)} className='mt-10 flex h-14 w-14 items-center justify-center rounded-full bg-white/10' > diff --git a/packages/web/components/New/Login/LoginWithPhoneOrEmail.tsx b/packages/web/components/New/Login/LoginWithPhoneOrEmail.tsx index c17774d..15ee430 100644 --- a/packages/web/components/New/Login/LoginWithPhoneOrEmail.tsx +++ b/packages/web/components/New/Login/LoginWithPhoneOrEmail.tsx @@ -1,6 +1,5 @@ import { cx, css } from '@emotion/css' import { useState } from 'react' -import { state } from '@/web/store' import { useMutation } from 'react-query' import { loginWithEmail, loginWithPhone } from '@/web/api/auth' import md5 from 'md5' @@ -9,17 +8,20 @@ import { setCookies } from '@/web/utils/cookie' import { AnimatePresence, motion } from 'framer-motion' import { ease } from '@/web/utils/const' import { useSnapshot } from 'valtio' +import uiStates from '@/web/states/uiStates' +import persistedUiStates from '@/web/states/persistedUiStates' const LoginWithPhoneOrEmail = () => { - const { persistedUiStates } = useSnapshot(state) + const { loginPhoneCountryCode, loginType: persistedLoginType } = + useSnapshot(persistedUiStates) const [email, setEmail] = useState('') const [countryCode, setCountryCode] = useState( - persistedUiStates.loginPhoneCountryCode || '+86' + loginPhoneCountryCode || '+86' ) const [phone, setPhone] = useState('') const [password, setPassword] = useState('') const [loginType, setLoginType] = useState<'phone' | 'email'>( - persistedUiStates.loginType === 'email' ? 'email' : 'phone' + persistedLoginType === 'email' ? 'email' : 'phone' ) const doEmailLogin = useMutation( @@ -36,7 +38,7 @@ const LoginWithPhoneOrEmail = () => { } setCookies(result.cookie) - state.uiStates.showLoginPanel = false + uiStates.showLoginPanel = false }, onError: error => { toast(`Login failed: ${error}`) @@ -80,7 +82,7 @@ const LoginWithPhoneOrEmail = () => { return } setCookies(result.cookie) - state.uiStates.showLoginPanel = false + uiStates.showLoginPanel = false }, onError: error => { toast(`Login failed: ${error}`) @@ -132,7 +134,7 @@ const LoginWithPhoneOrEmail = () => { onClick={() => { const type = loginType === 'phone' ? 'email' : 'phone' setLoginType(type) - state.persistedUiStates.loginType = type + persistedUiStates.loginType = type }} > Phone @@ -165,7 +167,7 @@ const LoginWithPhoneOrEmail = () => { { setCountryCode(e.target.value) - state.persistedUiStates.loginPhoneCountryCode = e.target.value + persistedUiStates.loginPhoneCountryCode = e.target.value }} className={cx( 'my-3.5 flex-shrink-0 bg-transparent', @@ -181,7 +183,7 @@ const LoginWithPhoneOrEmail = () => { onChange={e => setPhone(e.target.value)} className='my-3.5 flex-grow appearance-none bg-transparent' placeholder='Phone' - type='number' + type='tel' value={phone} /> @@ -198,10 +200,11 @@ const LoginWithPhoneOrEmail = () => { initial='hidden' animate='show' exit='hidden' + className='w-full' > setEmail(e.target.value)} - className='flex-grow appearance-none bg-transparent' + className='w-full flex-grow appearance-none bg-transparent' placeholder='Email' type='email' value={email} @@ -215,7 +218,7 @@ const LoginWithPhoneOrEmail = () => {
setPassword(e.target.value)} - className='bg-transparent' + className='w-full bg-transparent' placeholder='Password' type='password' value={password} diff --git a/packages/web/components/New/Login/LoginWithQRCode.tsx b/packages/web/components/New/Login/LoginWithQRCode.tsx index ffcbecc..31648d5 100644 --- a/packages/web/components/New/Login/LoginWithQRCode.tsx +++ b/packages/web/components/New/Login/LoginWithQRCode.tsx @@ -1,21 +1,11 @@ import { cx, css } from '@emotion/css' import { useEffect, useState, useMemo } from 'react' import qrCode from 'qrcode' -import { state } from '@/web/store' -import { useSnapshot } from 'valtio' -import { useMutation, useQuery } from 'react-query' -import { - checkLoginQrCodeStatus, - fetchLoginQrCodeKey, - loginWithEmail, - loginWithPhone, -} from '@/web/api/auth' -import md5 from 'md5' +import { useQuery } from 'react-query' +import { checkLoginQrCodeStatus, fetchLoginQrCodeKey } from '@/web/api/auth' import toast from 'react-hot-toast' import { setCookies } from '@/web/utils/cookie' -import { AnimatePresence, motion } from 'framer-motion' -import { ease } from '@/web/utils/const' -import Icon from '@/web/components/Icon' +import uiStates from '@/web/states/uiStates' const QRCode = ({ className, text }: { className?: string; text: string }) => { const [image, setImage] = useState('') @@ -90,7 +80,7 @@ const LoginWithQRCode = () => { break } setCookies(status.cookie) - state.uiStates.showLoginPanel = false + uiStates.showLoginPanel = false break } }, diff --git a/packages/web/components/New/Main.tsx b/packages/web/components/New/Main.tsx index e106a3f..0e0b74d 100644 --- a/packages/web/components/New/Main.tsx +++ b/packages/web/components/New/Main.tsx @@ -1,18 +1,37 @@ import { css, cx } from '@emotion/css' import Router from './Router' +import useIntersectionObserver from '@/web/hooks/useIntersectionObserver' +import uiStates from '@/web/states/uiStates' +import { useEffect, useRef } from 'react' const Main = () => { + // Show/hide topbar background + const observePoint = useRef(null) + const { onScreen } = useIntersectionObserver(observePoint) + useEffect(() => { + uiStates.hideTopbarBackground = onScreen + return () => { + uiStates.hideTopbarBackground = false + } + }, [onScreen]) + return (
- +
+
+ +
) } diff --git a/packages/web/components/New/MenuBar.tsx b/packages/web/components/New/MenuBar.tsx index 6926453..a26662c 100644 --- a/packages/web/components/New/MenuBar.tsx +++ b/packages/web/components/New/MenuBar.tsx @@ -144,11 +144,6 @@ const MenuBar = ({ className }: { className?: string }) => { ` )} > - {window.env?.isMac && ( -
- -
- )} {!isMobile && }
diff --git a/packages/web/components/New/NowPlaying.tsx b/packages/web/components/New/NowPlaying.tsx index 925ca88..53ef3b6 100644 --- a/packages/web/components/New/NowPlaying.tsx +++ b/packages/web/components/New/NowPlaying.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useMemo, useRef, useState } from 'react' import { css, cx } from '@emotion/css' import Icon from '../Icon' import { formatDuration, resizeImage } from '@/web/utils/common' -import { player } from '@/web/store' +import player from '@/web/states/player' import { useSnapshot } from 'valtio' import { State as PlayerState, Mode as PlayerMode } from '@/web/utils/player' import Slider from './Slider' @@ -17,7 +17,7 @@ const Progress = () => { const { track, progress } = useSnapshot(player) return ( -
+
{ onlyCallOnChangeAfterDragEnded={true} /> -
+
{formatDuration(progress * 1000, 'en', 'hh:mm:ss')} {formatDuration(track?.dt || 0, 'en', 'hh:mm:ss')}
@@ -117,9 +117,9 @@ const NowPlaying = () => { {/* Info & Controls */} -
+
{/* Track Info */} -
+
{track?.name}
{ /> {/* Dividing line */} -
+
{/* Progress */} {/* Controls */} -
+
diff --git a/packages/web/components/New/PageTransition.tsx b/packages/web/components/New/PageTransition.tsx index e8010d0..c4dafca 100644 --- a/packages/web/components/New/PageTransition.tsx +++ b/packages/web/components/New/PageTransition.tsx @@ -1,7 +1,7 @@ import { motion } from 'framer-motion' import { ease } from '@/web/utils/const' import useIsMobile from '@/web/hooks/useIsMobile' -import scrollPositions from '@/web/store/scrollPositions' +import scrollPositions from '@/web/states/scrollPositions' import { useLayoutEffect } from 'react' const PageTransition = ({ diff --git a/packages/web/components/New/Player.tsx b/packages/web/components/New/Player.tsx index c9dedf0..575aace 100644 --- a/packages/web/components/New/Player.tsx +++ b/packages/web/components/New/Player.tsx @@ -12,7 +12,7 @@ const Player = () => { ` )} > - +
diff --git a/packages/web/components/New/PlayerMobile.tsx b/packages/web/components/New/PlayerMobile.tsx index c52573a..5ae9084 100644 --- a/packages/web/components/New/PlayerMobile.tsx +++ b/packages/web/components/New/PlayerMobile.tsx @@ -1,17 +1,18 @@ -import { player } from '@/web/store' +import player from '@/web/states/player' import { css, cx } from '@emotion/css' import { useSnapshot } from 'valtio' import Image from '@/web/components/New/Image' import Icon from '@/web/components/Icon' import useCoverColor from '@/web/hooks/useCoverColor' import { resizeImage } from '@/web/utils/common' -import { motion, PanInfo, useMotionValue } from 'framer-motion' +import { motion, PanInfo } from 'framer-motion' import { useLockBodyScroll } from 'react-use' import { useState } from 'react' import useUserLikedTracksIDs, { useMutationLikeATrack, } from '@/web/api/hooks/useUserLikedTracksIDs' -import PlayingNextMobile from './PlayingNextMobile' +import uiStates from '@/web/states/uiStates' +import { ease } from '@/web/utils/const' const LikeButton = () => { const { track } = useSnapshot(player) @@ -23,7 +24,7 @@ const LikeButton = () => { return (
diff --git a/packages/web/components/New/PlayingNext.tsx b/packages/web/components/New/PlayingNext.tsx index c7f005d..9d419ec 100644 --- a/packages/web/components/New/PlayingNext.tsx +++ b/packages/web/components/New/PlayingNext.tsx @@ -1,27 +1,25 @@ -import { resizeImage } from '@/web/utils/common' -import { player } from '@/web/store' +import { isIosPwa, resizeImage } from '@/web/utils/common' +import player from '@/web/states/player' import { State as PlayerState } from '@/web/utils/player' import { useSnapshot } from 'valtio' import useTracks from '@/web/api/hooks/useTracks' import { css, cx } from '@emotion/css' -import { AnimatePresence, motion } from 'framer-motion' -import Image from './Image' import Wave from './Wave' import Icon from '@/web/components/Icon' -import { useVirtualizer } from '@tanstack/react-virtual' -import { useRef } from 'react' import { useWindowSize } from 'react-use' import { playerWidth, topbarHeight } from '@/web/utils/const' +import useIsMobile from '@/web/hooks/useIsMobile' +import { Virtuoso } from 'react-virtuoso' const Header = () => { return (
-
+
PLAYING NEXT
@@ -48,30 +46,21 @@ const Track = ({ state: PlayerState }) => { return ( - { if (e.detail === 2 && track?.id) player.playTrack(track.id) }} > {/* Cover */} - Cover {/* Track info */} -
+
{track?.name}
-
+
{track?.ar.map(a => a.name).join(', ')}
@@ -91,11 +80,11 @@ const Track = ({ {playingTrackIndex === index ? ( ) : ( -
+
{String(index + 1).padStart(2, '0')}
)} - +
) } @@ -103,69 +92,57 @@ const TrackList = ({ className }: { className?: string }) => { const { trackList, trackIndex, state } = useSnapshot(player) const { data: tracksRaw } = useTracks({ ids: trackList }) const tracks = tracksRaw?.songs || [] - const parentRef = useRef(null) const { height } = useWindowSize() + const isMobile = useIsMobile() - const listHeight = height - topbarHeight - playerWidth - 24 - 20 // 24是封面与底部间距,20是list与封面间距 - - const rowVirtualizer = useVirtualizer({ - count: tracks.length, - getScrollElement: () => parentRef.current, - estimateSize: () => 76, - overscan: 10, - }) + const listHeight = height - topbarHeight - playerWidth - 24 // 24是封面与底部间距 + const listHeightMobile = height - 154 - 110 - (isIosPwa ? 34 : 0) // 154是列表距离底部的距离,110是顶部的距离 return ( <>
-
- {rowVirtualizer.getVirtualItems().map((row: any) => ( -
- -
- ))} -
+ totalCount={tracks.length} + className={cx( + 'no-scrollbar relative z-10 w-full overflow-auto', + className, + css` + mask-image: linear-gradient( + to top, + transparent 8px, + black 42px + ); // 底部渐变遮罩 + ` + )} + fixedItemHeight={76} + data={tracks} + overscan={10} + components={{ + Header: () =>
, + Footer: () =>
, + }} + itemContent={(index, track) => ( + + )} + >
- - {/* 底部渐变遮罩 */} -
) } diff --git a/packages/web/components/New/PlayingNextMobile.tsx b/packages/web/components/New/PlayingNextMobile.tsx index 673a22d..7ba5cd3 100644 --- a/packages/web/components/New/PlayingNextMobile.tsx +++ b/packages/web/components/New/PlayingNextMobile.tsx @@ -10,72 +10,81 @@ import { useLockBodyScroll } from 'react-use' import { isIosPwa } from '@/web/utils/common' import PlayingNext from './PlayingNext' import { ease } from '@/web/utils/const' +import { useSnapshot } from 'valtio' +import uiStates from '@/web/states/uiStates' +import Icon from '@/web/components/Icon' const PlayingNextMobile = () => { - const [display, setDisplay] = useState(false) + const { mobileShowPlayingNext: display } = useSnapshot(uiStates) const [isDragging, setIsDragging] = useState(false) - useLockBodyScroll(isDragging || display) + useLockBodyScroll(isDragging) const dragControls = useDragControls() - const y = useMotionValue('82%') return ( - console.log(info.point.y)} - > - {/* Indictor */} + {display && ( { - setIsDragging(true) - dragControls.start(e) + className='fixed inset-0 bg-black/80 backdrop-blur-3xl' + exit={{ + y: '100%', + borderRadius: '24px', + transition: { + ease: 'easeOut', + duration: 0.4, + }, + }} + animate={{ y: 0, borderRadius: 0 }} + initial={{ y: '100%', borderRadius: '24px' }} + transition={{ duration: 0.6, ease }} + dragControls={dragControls} + dragListener={false} + whileDrag={{ + borderRadius: '24px', + transition: { + duration: 0.2, + ease: 'linear', + }, }} - onDragEnd={() => setIsDragging(false)} dragConstraints={{ top: 0, bottom: 0 }} - className={cx( - 'mx-7 flex justify-center', - css` - --height: 30px; - bottom: calc( - 70px + 64px + - ${isIosPwa ? '24px' : 'env(safe-area-inset-bottom)'} - ); // 拖动条到导航栏的距离 + 导航栏高度 + safe-area-inset-bottom - height: var(--height); - ` - )} - layout + dragDirectionLock={true} + onDragEnd={(event, info) => { + setIsDragging(false) + const offset = info.offset.y + if (offset > 150) { + uiStates.mobileShowPlayingNext = false + } + }} + drag='y' > + {/* Indictor */} - + onPointerDown={e => { + setIsDragging(true) + dragControls.start(e) + }} + onClick={() => { + uiStates.mobileShowPlayingNext = false + }} + className={cx( + 'flex flex-col justify-end', + css` + height: 108px; + ` + )} + > + + - {/* List */} -
- -
- + {/* List */} +
+ +
+ + )}
) } diff --git a/packages/web/components/New/ScrollRestoration.tsx b/packages/web/components/New/ScrollRestoration.tsx index 9de17fd..b80dd8f 100644 --- a/packages/web/components/New/ScrollRestoration.tsx +++ b/packages/web/components/New/ScrollRestoration.tsx @@ -1,5 +1,5 @@ import { useLayoutEffect } from 'react' -import scrollPositions from '@/web/store/scrollPositions' +import scrollPositions from '@/web/states/scrollPositions' import { throttle } from 'lodash-es' const ScrollRestoration = () => { diff --git a/packages/web/components/New/Topbar/Avatar.tsx b/packages/web/components/New/Topbar/Avatar.tsx index f0744db..84728ab 100644 --- a/packages/web/components/New/Topbar/Avatar.tsx +++ b/packages/web/components/New/Topbar/Avatar.tsx @@ -2,7 +2,7 @@ import { css, cx } from '@emotion/css' import Icon from '../../Icon' import { resizeImage } from '@/web/utils/common' import useUser from '@/web/api/hooks/useUser' -import { state } from '@/web/store' +import uiStates from '@/web/states/uiStates' const Avatar = ({ className }: { className?: string }) => { const { data: user } = useUser() @@ -16,7 +16,7 @@ const Avatar = ({ className }: { className?: string }) => { {avatarUrl ? ( (state.uiStates.showLoginPanel = true)} + onClick={() => (uiStates.showLoginPanel = true)} className={cx( 'app-region-no-drag rounded-full', className || 'h-12 w-12' @@ -24,7 +24,7 @@ const Avatar = ({ className }: { className?: string }) => { /> ) : (
(state.uiStates.showLoginPanel = true)} + onClick={() => (uiStates.showLoginPanel = true)} className={cx( 'rounded-full bg-day-600 p-2.5 dark:bg-night-600', className || 'h-12 w-12' diff --git a/packages/web/components/New/Topbar/TopbarDesktop.tsx b/packages/web/components/New/Topbar/TopbarDesktop.tsx index 41255af..4b20671 100644 --- a/packages/web/components/New/Topbar/TopbarDesktop.tsx +++ b/packages/web/components/New/Topbar/TopbarDesktop.tsx @@ -1,32 +1,55 @@ import { css, cx } from '@emotion/css' -import { useLocation } from 'react-router-dom' import Avatar from './Avatar' import SearchBox from './SearchBox' import SettingsButton from './SettingsButton' import NavigationButtons from './NavigationButtons' import topbarBackground from '@/web/assets/images/topbar-background.png' +import uiStates from '@/web/states/uiStates' +import { useSnapshot } from 'valtio' +import { AnimatePresence, motion } from 'framer-motion' +import { ease } from '@/web/utils/const' + +const Background = () => { + const { hideTopbarBackground } = useSnapshot(uiStates) + + return ( + <> + + {!hideTopbarBackground && ( + + )} + + + ) +} const TopbarDesktop = () => { - const location = useLocation() - return (
+ {/* Background */} + + {/* Left Part */} -
+
{/* Dividing line */} @@ -36,7 +59,7 @@ const TopbarDesktop = () => {
{/* Right Part */} -
+
diff --git a/packages/web/components/New/TrackList.tsx b/packages/web/components/New/TrackList.tsx index a1c6e06..73c05ea 100644 --- a/packages/web/components/New/TrackList.tsx +++ b/packages/web/components/New/TrackList.tsx @@ -1,7 +1,7 @@ import { formatDuration } from '@/web/utils/common' import { css, cx } from '@emotion/css' import { useMemo } from 'react' -import { player } from '@/web/store' +import player from '@/web/states/player' import { useSnapshot } from 'valtio' import Wave from './Wave' import Icon from '@/web/components/Icon' diff --git a/packages/web/components/New/TrackListHeader.tsx b/packages/web/components/New/TrackListHeader.tsx index 98a0b67..dc72372 100644 --- a/packages/web/components/New/TrackListHeader.tsx +++ b/packages/web/components/New/TrackListHeader.tsx @@ -17,6 +17,7 @@ import { motion } from 'framer-motion' import { ease } from '@/web/utils/const' import { injectGlobal } from '@emotion/css' import { useNavigate } from 'react-router-dom' +import BlurBackground from '@/web/components/New/BlurBackground' injectGlobal` .plyr__video-wrapper, @@ -86,21 +87,7 @@ const Cover = memo(
{/* Blur bg */} - {!isMobile && ( - - )} + ) } @@ -111,10 +98,12 @@ const TrackListHeader = ({ album, playlist, onPlay, + className, }: { album?: Album playlist?: Playlist onPlay: () => void + className?: string }) => { const navigate = useNavigate() const isMobile = useIsMobile() @@ -126,6 +115,7 @@ const TrackListHeader = ({ return (
+
{album?.description || playlist?.description}
)} diff --git a/packages/web/components/Player.tsx b/packages/web/components/Player.tsx index 31f6a96..418ad1b 100644 --- a/packages/web/components/Player.tsx +++ b/packages/web/components/Player.tsx @@ -5,7 +5,7 @@ import Icon from './Icon' import useUserLikedTracksIDs, { useMutationLikeATrack, } from '@/web/api/hooks/useUserLikedTracksIDs' -import { player, state } from '@/web/store' +import player from '@/web/states/player' import { resizeImage } from '@/web/utils/common' import { State as PlayerState, Mode as PlayerMode } from '@/web/utils/player' import { RepeatMode as PlayerRepeatMode } from '@/shared/playerDataTypes' @@ -180,7 +180,11 @@ const Others = () => { {/* Lyric */} - (state.uiStates.showLyricPanel = true)}> + { + // + }} + >
diff --git a/packages/web/components/Sidebar.tsx b/packages/web/components/Sidebar.tsx index a5456db..a9a2f8c 100644 --- a/packages/web/components/Sidebar.tsx +++ b/packages/web/components/Sidebar.tsx @@ -3,7 +3,7 @@ import Icon from './Icon' import useUserPlaylists from '@/web/api/hooks/useUserPlaylists' import { scrollToTop } from '@/web/utils/common' import { prefetchPlaylist } from '@/web/api/hooks/usePlaylist' -import { player } from '@/web/store' +import player from '@/web/states/player' import { Mode, TrackListSourceType } from '@/web/utils/player' import { cx } from '@emotion/css' import { useMemo } from 'react' diff --git a/packages/web/components/TitleBar.tsx b/packages/web/components/TitleBar.tsx index 7d8ee8e..f04880c 100644 --- a/packages/web/components/TitleBar.tsx +++ b/packages/web/components/TitleBar.tsx @@ -1,4 +1,4 @@ -import { player } from '@/web/store' +import player from '@/web/states/player' import Icon from './Icon' import { IpcChannels } from '@/shared/IpcChannels' import useIpcRenderer from '@/web/hooks/useIpcRenderer' diff --git a/packages/web/components/TracksAlbum.tsx b/packages/web/components/TracksAlbum.tsx index 9616dcd..2d7d7b4 100644 --- a/packages/web/components/TracksAlbum.tsx +++ b/packages/web/components/TracksAlbum.tsx @@ -5,7 +5,7 @@ import Icon from '@/web/components/Icon' import useUserLikedTracksIDs, { useMutationLikeATrack, } from '@/web/api/hooks/useUserLikedTracksIDs' -import { player } from '@/web/store' +import player from '@/web/states/player' import { formatDuration } from '@/web/utils/common' import { State as PlayerState } from '@/web/utils/player' import { cx } from '@emotion/css' diff --git a/packages/web/components/TracksGrid.tsx b/packages/web/components/TracksGrid.tsx index 3cf38e4..e392042 100644 --- a/packages/web/components/TracksGrid.tsx +++ b/packages/web/components/TracksGrid.tsx @@ -1,6 +1,6 @@ import ArtistInline from '@/web/components/ArtistsInline' import Skeleton from '@/web/components/Skeleton' -import { player } from '@/web/store' +import player from '@/web/states/player' import { resizeImage } from '@/web/utils/common' import Icon from './Icon' import { cx } from '@emotion/css' diff --git a/packages/web/components/TracksList.tsx b/packages/web/components/TracksList.tsx index 1158bd2..cae7e16 100644 --- a/packages/web/components/TracksList.tsx +++ b/packages/web/components/TracksList.tsx @@ -7,7 +7,7 @@ import useUserLikedTracksIDs, { useMutationLikeATrack, } from '@/web/api/hooks/useUserLikedTracksIDs' import { formatDuration, resizeImage } from '@/web/utils/common' -import { player } from '@/web/store' +import player from '@/web/states/player' import { cx } from '@emotion/css' import { useSnapshot } from 'valtio' diff --git a/packages/web/hooks/useIntersectionObserver.ts b/packages/web/hooks/useIntersectionObserver.ts new file mode 100644 index 0000000..f0d8f03 --- /dev/null +++ b/packages/web/hooks/useIntersectionObserver.ts @@ -0,0 +1,25 @@ +import { useState, useEffect, RefObject } from 'react' + +const useIntersectionObserver = ( + element: RefObject +): { onScreen: boolean } => { + const [onScreen, setOnScreen] = useState(false) + + useEffect(() => { + if (element.current) { + const observer = new IntersectionObserver(([entry]) => + setOnScreen(entry.isIntersecting) + ) + observer.observe(element.current) + return () => { + observer.disconnect() + } + } + }, [element, setOnScreen]) + + return { + onScreen, + } +} + +export default useIntersectionObserver diff --git a/packages/web/hooks/useVideoCover.ts b/packages/web/hooks/useVideoCover.ts index 6c2c2a8..b323ff0 100644 --- a/packages/web/hooks/useVideoCover.ts +++ b/packages/web/hooks/useVideoCover.ts @@ -13,23 +13,21 @@ export default function useVideoCover(props: { async () => { if (!id || !name || !artist) return - const fromCache = window.ipcRenderer?.sendSync( + const fromMainProcess = await window.ipcRenderer?.invoke( IpcChannels.GetVideoCover, { id, + name, + artist, } ) - if (fromCache) { - return fromCache === 'no' ? undefined : fromCache + if (fromMainProcess) { + return fromMainProcess } const fromRemote = await axios.get('/yesplaymusic/video-cover', { params: props, }) - window.ipcRenderer?.send(IpcChannels.SetVideoCover, { - id, - url: fromRemote.data.url || '', - }) if (fromRemote?.data?.url) { return fromRemote.data.url } diff --git a/packages/web/ipcRenderer.ts b/packages/web/ipcRenderer.ts index 67f3968..b08f813 100644 --- a/packages/web/ipcRenderer.ts +++ b/packages/web/ipcRenderer.ts @@ -1,4 +1,4 @@ -import { player } from '@/web/store' +import player from '@/web/states/player' import { IpcChannels, IpcChannelsReturns, diff --git a/packages/web/main.tsx b/packages/web/main.tsx index 2f33c31..52ad3ab 100644 --- a/packages/web/main.tsx +++ b/packages/web/main.tsx @@ -14,7 +14,6 @@ import ReactGA from 'react-ga4' import { ipcRenderer } from './ipcRenderer' import { QueryClientProvider } from 'react-query' import reactQueryClient from '@/web/utils/reactQueryClient' -import ReactDOM from 'react-dom' ReactGA.initialize('G-KMJJCFZDKF') @@ -35,23 +34,12 @@ ipcRenderer() const container = document.getElementById('root') as HTMLElement const root = ReactDOMClient.createRoot(container) -// root.render( -// -// -// -// -// -// -// -// ) - -ReactDOM.render( +root.render( - , - document.getElementById('root') + ) diff --git a/packages/web/package.json b/packages/web/package.json index 4bf06b2..097e2f8 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -9,14 +9,15 @@ "test": "vitest", "test:ui": "vitest --ui", "test:coverage": "vitest run --coverage", - "test:types": "tsc --noEmit --project src/renderer/tsconfig.json", + "test:types": "tsc --noEmit --project ./tsconfig.json", "lint": "eslint --ext .ts,.js,.tsx,.jsx ./", "analyze:css": "npx windicss-analysis", "analyze:js": "npm run build && open-cli bundle-stats-renderer.html", "storybook": "start-storybook -p 6006", "storybook:build": "build-storybook", "generate:accent-color-css": "node ./scripts/generate.accent.color.css.js", - "api:netease": "npx NeteaseCloudMusicApi@latest" + "api:netease": "npx NeteaseCloudMusicApi@latest", + "format": "prettier --config ../../prettier.config.js --write './**/*.{ts,tsx,js,jsx,css}' --ignore-path ../../.prettierignore" }, "engines": { "node": "^14.13.1 || >=16.0.0" @@ -25,7 +26,6 @@ "@emotion/css": "^11.9.0", "@sentry/react": "^6.19.7", "@sentry/tracing": "^6.19.7", - "@tanstack/react-virtual": "3.0.0-beta.2", "ahooks": "^3.4.1", "axios": "^0.27.2", "color.js": "^1.2.0", @@ -48,6 +48,7 @@ "react-query": "^3.38.0", "react-router-dom": "^6.3.0", "react-use": "^17.4.0", + "react-virtuoso": "^2.16.1", "valtio": "^1.6.1" }, "devDependencies": { diff --git a/packages/web/pages/Album.tsx b/packages/web/pages/Album.tsx index f6070d4..10ae011 100644 --- a/packages/web/pages/Album.tsx +++ b/packages/web/pages/Album.tsx @@ -7,7 +7,7 @@ import Icon from '@/web/components/Icon' import TracksAlbum from '@/web/components/TracksAlbum' import useAlbum from '@/web/api/hooks/useAlbum' import useArtistAlbums from '@/web/api/hooks/useArtistAlbums' -import { player } from '@/web/store' +import player from '@/web/states/player' import { Mode as PlayerMode, State as PlayerState, diff --git a/packages/web/pages/Artist.tsx b/packages/web/pages/Artist.tsx index a92bd6e..32a693f 100644 --- a/packages/web/pages/Artist.tsx +++ b/packages/web/pages/Artist.tsx @@ -9,7 +9,7 @@ import TracksGrid from '@/web/components/TracksGrid' import CoverRow, { Subtitle } from '@/web/components/CoverRow' import Skeleton from '@/web/components/Skeleton' import useTracks from '@/web/api/hooks/useTracks' -import { player } from '@/web/store' +import player from '@/web/states/player' import { cx } from '@emotion/css' import { useCallback, useMemo } from 'react' import toast from 'react-hot-toast' diff --git a/packages/web/pages/Library.tsx b/packages/web/pages/Library.tsx index 13c650c..659b3f3 100644 --- a/packages/web/pages/Library.tsx +++ b/packages/web/pages/Library.tsx @@ -5,7 +5,7 @@ import useLyric from '@/web/api/hooks/useLyric' import usePlaylist from '@/web/api/hooks/usePlaylist' import useUser from '@/web/api/hooks/useUser' import useUserPlaylists from '@/web/api/hooks/useUserPlaylists' -import { player } from '@/web/store' +import player from '@/web/states/player' import { resizeImage } from '@/web/utils/common' import { sample, chunk } from 'lodash-es' import useUserArtists from '@/web/api/hooks/useUserArtists' diff --git a/packages/web/pages/New/Album.tsx b/packages/web/pages/New/Album.tsx index 815832e..259fa2a 100644 --- a/packages/web/pages/New/Album.tsx +++ b/packages/web/pages/New/Album.tsx @@ -4,7 +4,7 @@ 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/store' +import player from '@/web/states/player' import toast from 'react-hot-toast' import { useSnapshot } from 'valtio' import useArtistAlbums from '@/web/api/hooks/useArtistAlbums' @@ -113,9 +113,13 @@ const Album = () => { return ( - + diff --git a/packages/web/pages/New/Artist/Header/ArtistInfo.tsx b/packages/web/pages/New/Artist/Header/ArtistInfo.tsx index 5d9d39a..4a9529d 100644 --- a/packages/web/pages/New/Artist/Header/ArtistInfo.tsx +++ b/packages/web/pages/New/Artist/Header/ArtistInfo.tsx @@ -7,7 +7,7 @@ const ArtistInfo = ({ artist }: { artist?: Artist }) => {
{artist?.name}
-
+
Artist
diff --git a/packages/web/pages/New/Artist/Header/BlurBackground.tsx b/packages/web/pages/New/Artist/Header/BlurBackground.tsx deleted file mode 100644 index bbe2395..0000000 --- a/packages/web/pages/New/Artist/Header/BlurBackground.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { resizeImage } from '@/web/utils/common' -import { cx, css } from '@emotion/css' -import useIsMobile from '@/web/hooks/useIsMobile' - -const BlurBackground = ({ cover }: { cover?: string }) => { - const isMobile = useIsMobile() - return isMobile || !cover ? ( - <> - ) : ( - - ) -} - -export default BlurBackground diff --git a/packages/web/pages/New/Artist/Header/Header.tsx b/packages/web/pages/New/Artist/Header/Header.tsx index 870846a..51bb885 100644 --- a/packages/web/pages/New/Artist/Header/Header.tsx +++ b/packages/web/pages/New/Artist/Header/Header.tsx @@ -1,9 +1,8 @@ import { resizeImage } from '@/web/utils/common' import { cx, css } from '@emotion/css' import Image from '@/web/components/New/Image' -import { useMemo } from 'react' import { breakpoint as bp } from '@/web/utils/const' -import BlurBackground from './BlurBackground' +import BlurBackground from '@/web/components/New/BlurBackground' import ArtistInfo from './ArtistInfo' import Actions from './Actions' import LatestRelease from './LatestRelease' diff --git a/packages/web/pages/New/Artist/Header/LatestRelease.tsx b/packages/web/pages/New/Artist/Header/LatestRelease.tsx index 6a12ecf..f0aeac2 100644 --- a/packages/web/pages/New/Artist/Header/LatestRelease.tsx +++ b/packages/web/pages/New/Artist/Header/LatestRelease.tsx @@ -37,11 +37,11 @@ const Album = () => { ` )} /> -
-
+
+
{album.name}
-
+
{album.type} {album.size > 1 ? `· ${album.size} Tracks` : ''}
@@ -69,8 +69,8 @@ const Video = () => { ` )} /> -
-
+
+
Swedish House Mafia & The Weeknd Live at C...
@@ -84,7 +84,7 @@ const Video = () => { const LatestRelease = () => { return (
-
+
Latest Releases
diff --git a/packages/web/pages/New/Artist/Popular.tsx b/packages/web/pages/New/Artist/Popular.tsx index e9a476d..8389466 100644 --- a/packages/web/pages/New/Artist/Popular.tsx +++ b/packages/web/pages/New/Artist/Popular.tsx @@ -1,5 +1,5 @@ import { resizeImage } from '@/web/utils/common' -import { player } from '@/web/store' +import player from '@/web/states/player' import { State as PlayerState } from '@/web/utils/player' import useTracks from '@/web/api/hooks/useTracks' import { css, cx } from '@emotion/css' diff --git a/packages/web/pages/New/Browse.tsx b/packages/web/pages/New/Browse.tsx index 67a47c0..ce13b1d 100644 --- a/packages/web/pages/New/Browse.tsx +++ b/packages/web/pages/New/Browse.tsx @@ -82,7 +82,7 @@ const Browse = () => { tabs={categories} value={active} onChange={category => setActive(category)} - className='sticky top-0 z-10 px-2.5 lg:px-0' + className='sticky top-0 z-10 mt-2.5 px-2.5 lg:mt-0 lg:px-0' />
diff --git a/packages/web/pages/New/Discover.tsx b/packages/web/pages/New/Discover.tsx index 90c1748..631d96b 100644 --- a/packages/web/pages/New/Discover.tsx +++ b/packages/web/pages/New/Discover.tsx @@ -106,7 +106,7 @@ const Discover = () => { return ( -
+
diff --git a/packages/web/pages/New/My/Collections.tsx b/packages/web/pages/New/My/Collections.tsx index 71c40ef..ab941f5 100644 --- a/packages/web/pages/New/My/Collections.tsx +++ b/packages/web/pages/New/My/Collections.tsx @@ -5,6 +5,8 @@ import { useMemo, 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' const tabs = [ { @@ -39,7 +41,12 @@ const Playlists = () => { const Collections = () => { // const { data: artists } = useUserArtists() - const [selectedTab, setSelectedTab] = useState(tabs[0].id) + const { librarySelectedTab: selectedTab } = useSnapshot(uiStates) + const setSelectedTab = ( + id: 'playlists' | 'albums' | 'artists' | 'videos' + ) => { + uiStates.librarySelectedTab = id + } return (
diff --git a/packages/web/pages/New/My/PlayLikedSongsCard.tsx b/packages/web/pages/New/My/PlayLikedSongsCard.tsx index 0f7fd6a..1302c7c 100644 --- a/packages/web/pages/New/My/PlayLikedSongsCard.tsx +++ b/packages/web/pages/New/My/PlayLikedSongsCard.tsx @@ -1,7 +1,7 @@ import useLyric from '@/web/api/hooks/useLyric' import usePlaylist from '@/web/api/hooks/usePlaylist' import useUserPlaylists from '@/web/api/hooks/useUserPlaylists' -import { player } from '@/web/store' +import player from '@/web/states/player' import { sample, chunk, sampleSize } from 'lodash-es' import { css, cx } from '@emotion/css' import { useState, useEffect, useMemo, useCallback, memo } from 'react' diff --git a/packages/web/pages/New/Playlist.tsx b/packages/web/pages/New/Playlist.tsx index 56c4a70..5305c1a 100644 --- a/packages/web/pages/New/Playlist.tsx +++ b/packages/web/pages/New/Playlist.tsx @@ -2,13 +2,13 @@ import TrackListHeader from '@/web/components/New/TrackListHeader' 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/store' +import player from '@/web/states/player' import toast from 'react-hot-toast' import { useSnapshot } from 'valtio' import { memo, useEffect, useMemo } from 'react' import usePlaylist from '@/web/api/hooks/usePlaylist' import useTracksInfinite from '@/web/api/hooks/useTracksInfinite' -import useScroll from '@/web/hooks/useScroll' + const Playlist = () => { const params = useParams() const { data: playlist, isLoading } = usePlaylist({ @@ -35,7 +35,11 @@ const Playlist = () => { return ( - + { } const changeColor = (color: string) => { - state.settings.accentColor = color + settings.accentColor = color changeAccentColor(color) } - const accentColor = useSnapshot(state).settings.accentColor + const accentColor = useSnapshot(settings).accentColor return (
强调色
diff --git a/packages/web/states/persistedUiStates.ts b/packages/web/states/persistedUiStates.ts new file mode 100644 index 0000000..123ba83 --- /dev/null +++ b/packages/web/states/persistedUiStates.ts @@ -0,0 +1,19 @@ +import { proxy, subscribe } from 'valtio' + +interface PersistedUiStates { + loginPhoneCountryCode: string + loginType: 'phone' | 'email' | 'qrCode' +} + +const initPersistedUiStates: PersistedUiStates = { + loginPhoneCountryCode: '+86', + loginType: 'qrCode', +} + +const persistedUiStates = proxy(initPersistedUiStates) + +subscribe(persistedUiStates, () => { + localStorage.setItem('persistedUiStates', JSON.stringify(persistedUiStates)) +}) + +export default persistedUiStates diff --git a/packages/web/states/player.ts b/packages/web/states/player.ts new file mode 100644 index 0000000..9890202 --- /dev/null +++ b/packages/web/states/player.ts @@ -0,0 +1,18 @@ +import { Player } from '@/web/utils/player' +import { proxy, subscribe } from 'valtio' + +const playerInLocalStorage = localStorage.getItem('player') +const player = proxy(new Player()) + +player.init((playerInLocalStorage && JSON.parse(playerInLocalStorage)) || {}) + +subscribe(player, () => { + localStorage.setItem('player', JSON.stringify(player)) +}) + +if (import.meta.env.DEV) { + // eslint-disable-next-line @typescript-eslint/no-extra-semi + ;(window as any).player = player +} + +export default player diff --git a/packages/web/store/scrollPositions.ts b/packages/web/states/scrollPositions.ts similarity index 100% rename from packages/web/store/scrollPositions.ts rename to packages/web/states/scrollPositions.ts diff --git a/packages/web/states/settings.ts b/packages/web/states/settings.ts new file mode 100644 index 0000000..bc2ee42 --- /dev/null +++ b/packages/web/states/settings.ts @@ -0,0 +1,53 @@ +import { IpcChannels } from '@/shared/IpcChannels' +import { merge } from 'lodash-es' +import { proxy, subscribe } from 'valtio' + +interface Settings { + accentColor: string + unm: { + enabled: boolean + sources: Array< + 'migu' | 'kuwo' | 'kugou' | 'ytdl' | 'qq' | 'bilibili' | 'joox' + > + searchMode: 'order-first' | 'fast-first' + proxy: null | { + protocol: 'http' | 'https' | 'socks5' + host: string + port: number + username?: string + password?: string + } + cookies: { + qq?: string + joox?: string + } + } +} + +const initSettings: Settings = { + accentColor: 'blue', + unm: { + enabled: true, + sources: ['migu'], + searchMode: 'order-first', + proxy: null, + cookies: {}, + }, +} + +const settingsInLocalStorage = localStorage.getItem('settings') +const settings = proxy( + merge( + initSettings, + settingsInLocalStorage ? JSON.parse(settingsInLocalStorage) : {} + ) +) + +subscribe(settings, () => { + localStorage.setItem('settings', JSON.stringify(settings)) +}) +subscribe(settings, () => { + window.ipcRenderer?.send(IpcChannels.SyncSettings, settings) +}) + +export default settings diff --git a/packages/web/states/uiStates.ts b/packages/web/states/uiStates.ts new file mode 100644 index 0000000..eb5b843 --- /dev/null +++ b/packages/web/states/uiStates.ts @@ -0,0 +1,19 @@ +import { proxy } from 'valtio' + +interface UIStates { + showLyricPanel: boolean + showLoginPanel: boolean + hideTopbarBackground: boolean + librarySelectedTab: 'playlists' | 'albums' | 'artists' | 'videos' + mobileShowPlayingNext: boolean +} + +const initUIStates: UIStates = { + showLyricPanel: false, + showLoginPanel: false, + hideTopbarBackground: false, + librarySelectedTab: 'playlists', + mobileShowPlayingNext: false, +} + +export default proxy(initUIStates) diff --git a/packages/web/store/index.ts b/packages/web/store/index.ts deleted file mode 100644 index db2ca0e..0000000 --- a/packages/web/store/index.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { proxy, subscribe } from 'valtio' -import { Player } from '@/web/utils/player' -import { merge } from 'lodash-es' -import { IpcChannels } from '@/shared/IpcChannels' -import { Store, initialState } from '@/shared/store' - -const stateInLocalStorage = localStorage.getItem('state') -export const state = proxy( - merge( - initialState, - stateInLocalStorage ? JSON.parse(stateInLocalStorage) : {}, - { - uiStates: initialState.uiStates, - } - ) -) -subscribe(state, () => { - localStorage.setItem('state', JSON.stringify(state)) -}) -subscribe(state.settings, () => { - window.ipcRenderer?.send(IpcChannels.SyncSettings, { ...state.settings }) -}) - -// player -const playerInLocalStorage = localStorage.getItem('player') -export const player = proxy(new Player()) -player.init((playerInLocalStorage && JSON.parse(playerInLocalStorage)) || {}) -subscribe(player, () => { - localStorage.setItem('player', JSON.stringify(player)) -}) - -if (import.meta.env.DEV) { - // eslint-disable-next-line @typescript-eslint/no-extra-semi - ;(window as any).player = player -} diff --git a/packages/web/utils/theme.ts b/packages/web/utils/theme.ts index 6a851aa..96b204a 100644 --- a/packages/web/utils/theme.ts +++ b/packages/web/utils/theme.ts @@ -1,8 +1,17 @@ +export const changeTheme = (theme: 'light' | 'dark') => { + document.body.setAttribute('class', theme) + if (!window.env?.isElectron) { + document.documentElement.style.background = + theme === 'dark' ? '#000' : '#fff' + } +} + export const changeAccentColor = (color: string) => { document.body.setAttribute('data-accent-color', color) } -const stateString = localStorage.getItem('state') -const stateInLocalStorage = stateString ? JSON.parse(stateString) : {} +const settingsInStorage = localStorage.getItem('settings') +const settings = settingsInStorage ? JSON.parse(settingsInStorage) : {} -changeAccentColor(stateInLocalStorage?.settings?.accentColor || 'blue') +changeTheme(settings.theme || 'dark') +changeAccentColor(settings?.accentColor || 'green') diff --git a/packages/web/vite.config.ts b/packages/web/vite.config.ts index 7dd9297..d21f58d 100644 --- a/packages/web/vite.config.ts +++ b/packages/web/vite.config.ts @@ -85,7 +85,8 @@ export default defineConfig({ strictPort: IS_ELECTRON ? true : false, proxy: { '/netease/': { - target: `http://192.168.2.111:${ + target: `http://192.168.50.111:${ + // target: `http://127.0.0.1:${ process.env.ELECTRON_DEV_NETEASE_API_PORT || 3000 }`, changeOrigin: true, @@ -93,7 +94,6 @@ export default defineConfig({ }, '/yesplaymusic/video-cover': { target: `http://168.138.40.199:51324`, - // target: `http://127.0.0.1:51324`, changeOrigin: true, }, '/yesplaymusic/': { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0bd3194..1f3f0b5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -117,7 +117,6 @@ importers: '@storybook/builder-vite': ^0.1.35 '@storybook/react': ^6.5.5 '@storybook/testing-library': ^0.0.11 - '@tanstack/react-virtual': 3.0.0-beta.2 '@testing-library/react': ^13.3.0 '@types/howler': ^2.2.7 '@types/js-cookie': ^3.0.2 @@ -163,6 +162,7 @@ importers: react-query: ^3.38.0 react-router-dom: ^6.3.0 react-use: ^17.4.0 + react-virtuoso: ^2.16.1 rollup-plugin-visualizer: ^5.6.0 storybook-tailwind-dark-mode: ^1.0.12 tailwindcss: ^3.0.24 @@ -176,7 +176,6 @@ importers: '@emotion/css': 11.9.0 '@sentry/react': 6.19.7_react@18.1.0 '@sentry/tracing': 6.19.7 - '@tanstack/react-virtual': 3.0.0-beta.2 ahooks: 3.4.1_react@18.1.0 axios: 0.27.2 color.js: 1.2.0 @@ -199,6 +198,7 @@ importers: react-query: 3.39.1_ef5jwxihqo6n7gxfmzogljlgcm react-router-dom: 6.3.0_ef5jwxihqo6n7gxfmzogljlgcm react-use: 17.4.0_ef5jwxihqo6n7gxfmzogljlgcm + react-virtuoso: 2.16.1_ef5jwxihqo6n7gxfmzogljlgcm valtio: 1.6.1_react@18.1.0+vite@2.9.9 devDependencies: '@storybook/addon-actions': 6.5.5_ef5jwxihqo6n7gxfmzogljlgcm @@ -4539,10 +4539,6 @@ packages: resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==} dev: true - /@reach/observe-rect/1.2.0: - resolution: {integrity: sha512-Ba7HmkFgfQxZqqaeIWWkNK0rEhpxVQHIoVyW1YDSkGsGIXzcaW4deC8B0pZrNSSyLTdIk7y+5olKt5+g0GmFIQ==} - dev: false - /@rollup/plugin-babel/5.3.1_4kojsos35jimftt7mhjohcqk6y: resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==} engines: {node: '>= 10.0.0'} @@ -6402,13 +6398,6 @@ packages: defer-to-connect: 2.0.1 dev: true - /@tanstack/react-virtual/3.0.0-beta.2: - resolution: {integrity: sha512-pwA9URTHYXX/2PgIISoMcf1P77hxf5oI3L/IDQ19Q1xuAc76o2R2CwHv6vvl5fDhwVj5klOfBxJvuT61Lhy9/w==} - engines: {node: '>=12'} - dependencies: - '@reach/observe-rect': 1.2.0 - dev: false - /@testing-library/dom/8.13.0: resolution: {integrity: sha512-9VHgfIatKNXQNaZTtLnalIy0jNZzY35a4S3oi08YAt9Hv1VsfZ/DfA45lM8D/UhtHBGJ4/lGwp0PZkVndRkoOQ==} engines: {node: '>=12'} @@ -7151,6 +7140,20 @@ packages: url-toolkit: 2.2.5 dev: false + /@virtuoso.dev/react-urx/0.2.13_react@18.1.0: + resolution: {integrity: sha512-MY0ugBDjFb5Xt8v2HY7MKcRGqw/3gTpMlLXId2EwQvYJoC8sP7nnXjAxcBtTB50KTZhO0SbzsFimaZ7pSdApwA==} + engines: {node: '>=10'} + peerDependencies: + react: '>=16' + dependencies: + '@virtuoso.dev/urx': 0.2.13 + react: 18.1.0 + dev: false + + /@virtuoso.dev/urx/0.2.13: + resolution: {integrity: sha512-iirJNv92A1ZWxoOHHDYW/1KPoi83939o83iUBQHIim0i3tMeSKEh+bxhJdTHQ86Mr4uXx9xGUTq69cp52ZP8Xw==} + dev: false + /@vitejs/plugin-react/1.3.2: resolution: {integrity: sha512-aurBNmMo0kz1O4qRoY+FM4epSA39y3ShWGuqfLRA/3z0oEJAdtoSfgA3aO98/PCCHAqMaduLxIxErWrVKIFzXA==} engines: {node: '>=12.0.0'} @@ -16373,6 +16376,19 @@ packages: tslib: 2.4.0 dev: false + /react-virtuoso/2.16.1_ef5jwxihqo6n7gxfmzogljlgcm: + resolution: {integrity: sha512-WpcHZedUe00XYSQ56KcdYmWy/oaiPPuweTYemC9gl8CbjchLTKqLPCJa51Yv32U9oj1XPAEMfxuaXM7NTtjOiw==} + engines: {node: '>=10'} + peerDependencies: + react: '>=16 || >=17 || >= 18' + react-dom: '>=16 || >=17 || >= 18' + dependencies: + '@virtuoso.dev/react-urx': 0.2.13_react@18.1.0 + '@virtuoso.dev/urx': 0.2.13 + react: 18.1.0 + react-dom: 18.1.0_react@18.1.0 + dev: false + /react/18.1.0: resolution: {integrity: sha512-4oL8ivCz5ZEPyclFQXaNksK3adutVS8l2xzZU0cqEFrE9Sb7fC0EFK5uEk74wIreL1DERyjvsU915j1pcT2uEQ==} engines: {node: '>=0.10.0'}