From 133881d287524e82c8c73fc8397b573c72817cc2 Mon Sep 17 00:00:00 2001 From: qier222 Date: Wed, 8 Jun 2022 00:07:04 +0800 Subject: [PATCH] feat: updates --- packages/electron/main/index.ts | 4 +- packages/web/AppNew.tsx | 6 +- packages/web/api/personalFM.ts | 2 +- packages/web/assets/icons/hide-list.svg | 3 + packages/web/components/New/ArtistRow.tsx | 100 ++++++++++++------ packages/web/components/New/CoverRow.tsx | 2 +- packages/web/components/New/CoverWall.tsx | 10 +- packages/web/components/New/Devtool.tsx | 6 +- packages/web/components/New/Layout.tsx | 14 +-- packages/web/components/New/LayoutMobile.tsx | 37 +++++++ packages/web/components/New/Main.tsx | 5 +- .../New/{Sidebar.tsx => MenuBar.tsx} | 74 +++++++++---- packages/web/components/New/NowPlaying.tsx | 4 +- .../web/components/New/PlayLikedSongsCard.tsx | 2 +- packages/web/components/New/Player.tsx | 2 +- packages/web/components/New/PlayerMobile.tsx | 95 +++++++++++++++++ packages/web/components/New/PlayingNext.tsx | 36 +++++-- .../web/components/New/Sidebar.stories.tsx | 2 +- packages/web/components/New/Tabs.tsx | 3 +- packages/web/components/New/Topbar.tsx | 8 +- packages/web/components/New/TrackList.tsx | 31 ++++-- .../web/components/New/TrackListHeader.tsx | 4 +- packages/web/components/New/TrafficLight.tsx | 13 +++ packages/web/hooks/useIsMobile.ts | 8 ++ packages/web/pages/New/My.tsx | 20 ++-- packages/web/styles/global.css | 4 + packages/web/tailwind.config.js | 1 + packages/web/utils/player.ts | 8 +- 28 files changed, 389 insertions(+), 115 deletions(-) create mode 100644 packages/web/assets/icons/hide-list.svg create mode 100644 packages/web/components/New/LayoutMobile.tsx rename packages/web/components/New/{Sidebar.tsx => MenuBar.tsx} (53%) create mode 100644 packages/web/components/New/PlayerMobile.tsx create mode 100644 packages/web/components/New/TrafficLight.tsx create mode 100644 packages/web/hooks/useIsMobile.ts diff --git a/packages/electron/main/index.ts b/packages/electron/main/index.ts index f04900c..d0a4345 100644 --- a/packages/electron/main/index.ts +++ b/packages/electron/main/index.ts @@ -107,10 +107,8 @@ class Main { height: this.store.get('window.height'), minWidth: 1240, minHeight: 848, - // vibrancy: 'fullscreen-ui', - titleBarStyle: 'customButtonsOnHover', + titleBarStyle: isMac ? 'customButtonsOnHover' : 'hidden', trafficLightPosition: { x: 24, y: 24 }, - // frame: !(isWindows || isLinux), // TODO: 适用于linux下独立的启用开关 frame: false, transparent: true, } diff --git a/packages/web/AppNew.tsx b/packages/web/AppNew.tsx index fe118aa..b9cc2b1 100644 --- a/packages/web/AppNew.tsx +++ b/packages/web/AppNew.tsx @@ -4,13 +4,17 @@ import IpcRendererReact from '@/web/IpcRendererReact' import Layout from '@/web/components/New/Layout' import Devtool from '@/web/components/New/Devtool' import ErrorBoundary from '@/web/components/New/ErrorBoundary' +import useIsMobile from '@/web/hooks/useIsMobile' +import LayoutMobile from '@/web/components/New/LayoutMobile' const App = () => { + const isMobile = useIsMobile() + return (
{window.env?.isEnableTitlebar && } - + {isMobile ? : } diff --git a/packages/web/api/personalFM.ts b/packages/web/api/personalFM.ts index a74493a..5fde9e4 100644 --- a/packages/web/api/personalFM.ts +++ b/packages/web/api/personalFM.ts @@ -93,7 +93,7 @@ export interface FetchPersonalFMResponse { } export function fetchPersonalFM(): Promise { return request({ - url: window.ipcRenderer ? '/personal/fm' : '/personal_fm', + url: window.env?.isElectron ? '/personal/fm' : '/personal_fm', method: 'get', params: { timestamp: Date.now(), diff --git a/packages/web/assets/icons/hide-list.svg b/packages/web/assets/icons/hide-list.svg new file mode 100644 index 0000000..c462f49 --- /dev/null +++ b/packages/web/assets/icons/hide-list.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/web/components/New/ArtistRow.tsx b/packages/web/components/New/ArtistRow.tsx index b8bc830..f0f4420 100644 --- a/packages/web/components/New/ArtistRow.tsx +++ b/packages/web/components/New/ArtistRow.tsx @@ -2,41 +2,79 @@ import { resizeImage } from '@/web/utils/common' import { css, cx } from '@emotion/css' import Image from './Image' -const ArtistRow = ({ - artists, - title, - className, -}: { - artists: Artist[] | undefined - title?: string - className?: string -}) => { +const Artist = ({ artist }: { artist: Artist }) => { return ( -
- {/* Title */} - {title && ( -

- {title} -

- )} - - {/* Artists */} -
- {artists?.map(artist => ( -
- {artist.name} -
- {artist.name} -
-
- ))} +
+ {artist.name} +
+ {artist.name}
) } +const ArtistRow = ({ + artists, + title, + className, + placeholderRow, +}: { + artists: Artist[] | undefined + title?: string + className?: string + placeholderRow?: number +}) => { + return ( +
+ {/* Title */} + {title && ( +

+ {title} +

+ )} + + {/* Artists */} + {artists && ( +
+ {artists.map(artist => ( +
+ +
+ ))} +
+ )} + + {/* Placeholder */} + {placeholderRow && !artists && ( +
+ {[...new Array(placeholderRow * 5).keys()].map(i => ( +
+
+
+ PLACE +
+
+ ))} +
+ )} +
+ ) +} + export default ArtistRow diff --git a/packages/web/components/New/CoverRow.tsx b/packages/web/components/New/CoverRow.tsx index 4b334d8..0de43b1 100644 --- a/packages/web/components/New/CoverRow.tsx +++ b/packages/web/components/New/CoverRow.tsx @@ -38,7 +38,7 @@ const CoverRow = ({ )} {/* Items */} -
+
{albums?.map(album => ( goTo(album.id)} diff --git a/packages/web/components/New/CoverWall.tsx b/packages/web/components/New/CoverWall.tsx index c793fa6..8be7a39 100644 --- a/packages/web/components/New/CoverWall.tsx +++ b/packages/web/components/New/CoverWall.tsx @@ -14,14 +14,14 @@ const CoverWall = ({ const breakpoint = useBreakpoint() const sizes = { small: { - sm: 'xs', - md: 'xs', + sm: 'sm', + md: 'sm', lg: 'sm', xl: 'sm', '2xl': 'md', }, large: { - sm: 'xs', + sm: 'sm', md: 'sm', lg: 'md', xl: 'md', @@ -32,7 +32,7 @@ const CoverWall = ({ return (
navigate(`/album/${album.id}`)} diff --git a/packages/web/components/New/Devtool.tsx b/packages/web/components/New/Devtool.tsx index 43995f0..4f06127 100644 --- a/packages/web/components/New/Devtool.tsx +++ b/packages/web/components/New/Devtool.tsx @@ -1,14 +1,18 @@ +import useBreakpoint from '@/web/hooks/useBreakpoint' import { ReactQueryDevtools } from 'react-query/devtools' const Devtool = () => { + const breakpoint = useBreakpoint() + const isMobile = ['sm', 'md'].includes(breakpoint) return ( { id='layout' className={cx( 'relative grid h-screen select-none overflow-hidden bg-white dark:bg-black', - window.ipcRenderer && 'rounded-24', + window.env?.isElectron && 'rounded-24', css` grid-template-columns: 6.5rem auto 358px; grid-template-rows: 132px auto; @@ -24,17 +24,17 @@ const Layout = () => { showPlayer ? css` grid-template-areas: - 'sidebar main -' - 'sidebar main player'; + 'menubar main -' + 'menubar main player'; ` : css` grid-template-areas: - 'sidebar main main' - 'sidebar main main'; + 'menubar main main' + 'menubar main main'; ` )} > - +
{showPlayer && } diff --git a/packages/web/components/New/LayoutMobile.tsx b/packages/web/components/New/LayoutMobile.tsx new file mode 100644 index 0000000..79615ff --- /dev/null +++ b/packages/web/components/New/LayoutMobile.tsx @@ -0,0 +1,37 @@ +import Player from '@/web/components/New/PlayerMobile' +import { css, cx } from '@emotion/css' +import { useMemo } from 'react' +import { player } from '@/web/store' +import { useSnapshot } from 'valtio' +import Router from '@/web/components/New/Router' +import MenuBar from './MenuBar' + +const LayoutMobile = () => { + const playerSnapshot = useSnapshot(player) + const showPlayer = useMemo(() => !!playerSnapshot.track, [playerSnapshot]) + + return ( +
+
+ +
+ {showPlayer && ( +
+ +
+ )} +
+ +
+
+ ) +} + +export default LayoutMobile diff --git a/packages/web/components/New/Main.tsx b/packages/web/components/New/Main.tsx index f78dbb8..e106a3f 100644 --- a/packages/web/components/New/Main.tsx +++ b/packages/web/components/New/Main.tsx @@ -5,13 +5,10 @@ const Main = () => { return (
diff --git a/packages/web/components/New/Sidebar.tsx b/packages/web/components/New/MenuBar.tsx similarity index 53% rename from packages/web/components/New/Sidebar.tsx rename to packages/web/components/New/MenuBar.tsx index 2128409..566160f 100644 --- a/packages/web/components/New/Sidebar.tsx +++ b/packages/web/components/New/MenuBar.tsx @@ -1,9 +1,11 @@ import React, { useEffect, useState } from 'react' import { css, cx } from '@emotion/css' import Icon from '../Icon' -import { NavLink, useLocation } from 'react-router-dom' +import { useLocation, useNavigate } from 'react-router-dom' import { useAnimation, motion } from 'framer-motion' import { ease } from '@/web/utils/const' +import TrafficLight from './TrafficLight' +import useIsMobile from '@/web/hooks/useIsMobile' const tabs = [ { @@ -75,36 +77,68 @@ const TabName = () => { ) } -const Sidebar = () => { +const Tabs = () => { + const navigate = useNavigate() const location = useLocation() + const controls = useAnimation() + const animate = async (path: string) => { + await controls.start((p: string) => + p === path && location.pathname !== path ? 'scale' : 'reset' + ) + await controls.start('reset') + } + + return ( +
+ {tabs.map(tab => ( + animate(tab.path)} + onClick={() => navigate(tab.path)} + custom={tab.path} + variants={{ + scale: { scale: 0.8 }, + reset: { scale: 1 }, + }} + > + + + ))} +
+ ) +} + +const MenuBar = () => { + const isMobile = useIsMobile() return (
-
- {tabs.map(tab => ( - - - - ))} -
- + {window.env?.isMac && ( +
+ +
+ )} + + {!isMobile && }
) } -export default Sidebar +export default MenuBar diff --git a/packages/web/components/New/NowPlaying.tsx b/packages/web/components/New/NowPlaying.tsx index 17c5b55..fa6c1b8 100644 --- a/packages/web/components/New/NowPlaying.tsx +++ b/packages/web/components/New/NowPlaying.tsx @@ -122,8 +122,8 @@ const NowPlaying = () => {
diff --git a/packages/web/components/New/PlayLikedSongsCard.tsx b/packages/web/components/New/PlayLikedSongsCard.tsx index 0d6901d..f127eb2 100644 --- a/packages/web/components/New/PlayLikedSongsCard.tsx +++ b/packages/web/components/New/PlayLikedSongsCard.tsx @@ -73,7 +73,7 @@ const PlayLikedSongsCard = () => { ` )} > -
+
{lyricLines.map((line, index) => (
{line}
))} diff --git a/packages/web/components/New/Player.tsx b/packages/web/components/New/Player.tsx index 7fb429e..c9dedf0 100644 --- a/packages/web/components/New/Player.tsx +++ b/packages/web/components/New/Player.tsx @@ -13,7 +13,7 @@ const Player = () => { )} > -
+
diff --git a/packages/web/components/New/PlayerMobile.tsx b/packages/web/components/New/PlayerMobile.tsx new file mode 100644 index 0000000..533ca62 --- /dev/null +++ b/packages/web/components/New/PlayerMobile.tsx @@ -0,0 +1,95 @@ +import { player } from '@/web/store' +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' + +const PlayerMobile = () => { + const playerSnapshot = useSnapshot(player) + const bgColor = useCoverColor(playerSnapshot.track?.al?.picUrl ?? '') + + const x = useMotionValue(0) + const onDragEnd = ( + event: MouseEvent | TouchEvent | PointerEvent, + info: PanInfo + ) => { + const x = info.offset.x + const offset = 100 + if (x > offset) player.nextTrack() + if (x < -offset) player.prevTrack() + } + + return ( +
+
+ + Cover + +
+ + {playerSnapshot.track?.name} + + +
+
+
+ + + + +
+ ) +} + +export default PlayerMobile diff --git a/packages/web/components/New/PlayingNext.tsx b/packages/web/components/New/PlayingNext.tsx index 0f88fc8..0263a4d 100644 --- a/packages/web/components/New/PlayingNext.tsx +++ b/packages/web/components/New/PlayingNext.tsx @@ -6,6 +6,7 @@ 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' const PlayingNext = ({ className }: { className?: string }) => { const playerSnapshot = useSnapshot(player) @@ -15,22 +16,30 @@ const PlayingNext = ({ className }: { className?: string }) => { <>
- PLAYING NEXT +
+
+ PLAYING NEXT +
+
+
+ +
+
+ +
+
+
@@ -60,10 +69,17 @@ const PlayingNext = ({ className }: { className?: string }) => { {/* Track info */}
-
+
{track.name}
-
+
{track.ar.map(a => a.name).join(', ')}
@@ -72,7 +88,7 @@ const PlayingNext = ({ className }: { className?: string }) => { {playerSnapshot.trackIndex === index ? ( ) : ( -
+
{String(index + 1).padStart(2, '0')}
)} diff --git a/packages/web/components/New/Sidebar.stories.tsx b/packages/web/components/New/Sidebar.stories.tsx index 18ba7a7..5d51208 100644 --- a/packages/web/components/New/Sidebar.stories.tsx +++ b/packages/web/components/New/Sidebar.stories.tsx @@ -1,6 +1,6 @@ import React from 'react' import { ComponentStory, ComponentMeta } from '@storybook/react' -import Sidebar from './Sidebar' +import Sidebar from './MenuBar' export default { title: 'Components/Sidebar', diff --git a/packages/web/components/New/Tabs.tsx b/packages/web/components/New/Tabs.tsx index 7e17520..1daa150 100644 --- a/packages/web/components/New/Tabs.tsx +++ b/packages/web/components/New/Tabs.tsx @@ -13,7 +13,7 @@ const Tabs = ({ onChange: (id: string) => void }) => { return ( -
+
{tabs.map(tab => (
onChange(tab.id)} > {tab.name}
diff --git a/packages/web/components/New/Topbar.tsx b/packages/web/components/New/Topbar.tsx index e747dd7..eb2e9fa 100644 --- a/packages/web/components/New/Topbar.tsx +++ b/packages/web/components/New/Topbar.tsx @@ -10,13 +10,13 @@ const NavigationButtons = () => { const navigate = useNavigate() const controlsBack = useAnimation() const controlsForward = useAnimation() - const transition = { duration: 0.2, ease } + const transition = { duration: 0.18, ease } return ( <>
diff --git a/packages/web/components/New/TrackListHeader.tsx b/packages/web/components/New/TrackListHeader.tsx index e9eeda7..3c1fc06 100644 --- a/packages/web/components/New/TrackListHeader.tsx +++ b/packages/web/components/New/TrackListHeader.tsx @@ -86,8 +86,8 @@ const TrackListHeader = ({ onClick={() => onPlay()} className='h-[72px] w-[170px] rounded-full dark:bg-brand-700' > - - + +
diff --git a/packages/web/components/New/TrafficLight.tsx b/packages/web/components/New/TrafficLight.tsx new file mode 100644 index 0000000..7fefc4e --- /dev/null +++ b/packages/web/components/New/TrafficLight.tsx @@ -0,0 +1,13 @@ +const TrafficLight = () => { + const className = + 'mr-2 h-3 w-3 rounded-full last-of-type:mr-0 dark:bg-white/20' + return ( +
+
+
+
+
+ ) +} + +export default TrafficLight diff --git a/packages/web/hooks/useIsMobile.ts b/packages/web/hooks/useIsMobile.ts new file mode 100644 index 0000000..9055e5f --- /dev/null +++ b/packages/web/hooks/useIsMobile.ts @@ -0,0 +1,8 @@ +import useBreakpoint from './useBreakpoint' + +const useIsMobile = () => { + const breakpoint = useBreakpoint() + return ['sm', 'md'].includes(breakpoint) +} + +export default useIsMobile diff --git a/packages/web/pages/New/My.tsx b/packages/web/pages/New/My.tsx index 5dd9fd4..cfbf52f 100644 --- a/packages/web/pages/New/My.tsx +++ b/packages/web/pages/New/My.tsx @@ -66,12 +66,12 @@ const My = () => {
-
- a.artist)} - title='RECENTLY LISTENED' - /> -
+ + a.artist)} + placeholderRow={1} + title='RECENTLY LISTENED' + />
{ value={selectedTab} onChange={(id: string) => setSelectedTab(id)} /> - +
diff --git a/packages/web/styles/global.css b/packages/web/styles/global.css index d7622e5..afd2c50 100644 --- a/packages/web/styles/global.css +++ b/packages/web/styles/global.css @@ -130,3 +130,7 @@ a { * { -webkit-font-smoothing: antialiased; } + +.no-scrollbar::-webkit-scrollbar { + display: none; +} diff --git a/packages/web/tailwind.config.js b/packages/web/tailwind.config.js index 34b2e25..a567e86 100644 --- a/packages/web/tailwind.config.js +++ b/packages/web/tailwind.config.js @@ -48,6 +48,7 @@ module.exports = { 600: '#0E0E0E', 700: '#060606', 800: '#020202', + 900: '#313131', }, neutral: { 100: '#E3E3E3', diff --git a/packages/web/utils/player.ts b/packages/web/utils/player.ts index e3865cd..21b5dd0 100644 --- a/packages/web/utils/player.ts +++ b/packages/web/utils/player.ts @@ -250,7 +250,9 @@ export class Player { } if (this.trackID !== id) return Howler.unload() - const url = audio.includes('?') ? `${audio}&id=${id}` : `${audio}?id=${id}` + const url = audio.includes('?') + ? `${audio}&dash-id=${id}` + : `${audio}?dash-id=${id}` const howler = new Howl({ src: [url], format: ['mp3', 'flac', 'webm'], @@ -285,7 +287,7 @@ export class Player { private _cacheAudio(audio: string) { if (audio.includes('yesplaymusic') || !window.ipcRenderer) return - const id = Number(new URL(audio).searchParams.get('id')) + const id = Number(new URL(audio).searchParams.get('dash-id')) if (isNaN(id) || !id) return cacheAudio(id, audio) } @@ -490,7 +492,7 @@ export class Player { async playTrack(trackID: TrackID) { this._setStateToLoading() const index = this.trackList.findIndex(t => t === trackID) - if (!index) toast('播放失败,歌曲不在列表内') + if (index === -1) toast('播放失败,歌曲不在列表内') this._trackIndex = index this._playTrack() }