feat: updates (#1530)

* feat: 支持repeat mode切换

* feat: 歌单页面的播放按钮可以暂停

* fix: 专辑页面播放按钮

考虑私人FM的情况
解决按钮闪烁问题

* fix: SvgName报错

* update
This commit is contained in:
memorydream 2022-04-15 00:34:07 +08:00 committed by GitHub
parent 13281d3f08
commit 24af937e70
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 96 additions and 30 deletions

View file

@ -10,6 +10,7 @@ import { resizeImage } from '@/renderer/utils/common'
import { import {
State as PlayerState, State as PlayerState,
Mode as PlayerMode, Mode as PlayerMode,
RepeatMode as PlayerRepeatMode,
} from '@/renderer/utils/player' } from '@/renderer/utils/player'
const PlayingTrack = () => { const PlayingTrack = () => {
@ -130,25 +131,45 @@ const MediaControls = () => {
const Others = () => { const Others = () => {
const playerSnapshot = useSnapshot(player) const playerSnapshot = useSnapshot(player)
const mode = useMemo(() => playerSnapshot.mode, [playerSnapshot.mode])
const switchRepeatMode = () => {
if (playerSnapshot.repeatMode === PlayerRepeatMode.OFF) {
player.repeatMode = PlayerRepeatMode.ON
} else if (playerSnapshot.repeatMode === PlayerRepeatMode.ON) {
player.repeatMode = PlayerRepeatMode.ONE
} else {
player.repeatMode = PlayerRepeatMode.OFF
}
}
return ( return (
<div className='flex items-center justify-end gap-2 pr-2 text-black dark:text-white'> <div className='flex items-center justify-end gap-2 pr-2 text-black dark:text-white'>
<IconButton <IconButton
onClick={() => toast('Work in progress')} onClick={() => toast('Work in progress')}
disabled={mode === PlayerMode.FM} disabled={playerSnapshot.mode === PlayerMode.FM}
> >
<SvgIcon className='h-6 w-6' name='playlist' /> <SvgIcon className='h-6 w-6' name='playlist' />
</IconButton> </IconButton>
<IconButton <IconButton
onClick={() => toast('施工中...')} onClick={switchRepeatMode}
disabled={mode === PlayerMode.FM} disabled={playerSnapshot.mode === PlayerMode.FM}
> >
<SvgIcon className='h-6 w-6' name='repeat' /> <SvgIcon
className={classNames(
'h-6 w-6',
playerSnapshot.repeatMode !== PlayerRepeatMode.OFF &&
'text-brand-500'
)}
name={
playerSnapshot.repeatMode === PlayerRepeatMode.ONE
? 'repeat-1'
: 'repeat'
}
/>
</IconButton> </IconButton>
<IconButton <IconButton
onClick={() => toast('施工中...')} onClick={() => toast('施工中...')}
disabled={mode === PlayerMode.FM} disabled={playerSnapshot.mode === PlayerMode.FM}
> >
<SvgIcon className='h-6 w-6' name='shuffle' /> <SvgIcon className='h-6 w-6' name='shuffle' />
</IconButton> </IconButton>

View file

@ -1,4 +1,4 @@
type SvgName = export type SvgName =
| 'back' | 'back'
| 'dislike' | 'dislike'
| 'dj' | 'dj'
@ -35,6 +35,7 @@ type SvgName =
| 'volume-mute' | 'volume-mute'
| 'volume' | 'volume'
| 'windows-close' | 'windows-close'
| 'windows-minimize'
| 'windows-maximize' | 'windows-maximize'
| 'windows-un-maximize' | 'windows-un-maximize'
| 'x' | 'x'

View file

@ -8,7 +8,11 @@ import TracksAlbum from '@/renderer/components/TracksAlbum'
import useAlbum from '@/renderer/hooks/useAlbum' import useAlbum from '@/renderer/hooks/useAlbum'
import useArtistAlbums from '@/renderer/hooks/useArtistAlbums' import useArtistAlbums from '@/renderer/hooks/useArtistAlbums'
import { player } from '@/renderer/store' import { player } from '@/renderer/store'
import { State as PlayerState } from '@/renderer/utils/player' import {
Mode as PlayerMode,
State as PlayerState,
TrackListSourceType,
} from '@/renderer/utils/player'
import { import {
formatDate, formatDate,
formatDuration, formatDuration,
@ -27,40 +31,37 @@ const PlayButton = ({
isLoading, isLoading,
}: { }: {
album: Album | undefined album: Album | undefined
isLoading: boolean
handlePlay: () => void handlePlay: () => void
isLoading: boolean
}) => { }) => {
const playerSnapshot = useSnapshot(player) const playerSnapshot = useSnapshot(player)
const isPlaying = useMemo(
() => playerSnapshot.state === PlayerState.PLAYING,
[playerSnapshot.state]
)
const isThisAlbumPlaying = useMemo( const isThisAlbumPlaying = useMemo(
() => () =>
playerSnapshot.trackListSource?.type === 'album' && playerSnapshot.mode === PlayerMode.PLAYLIST &&
playerSnapshot.trackListSource?.type === TrackListSourceType.ALBUM &&
playerSnapshot.trackListSource?.id === album?.id, playerSnapshot.trackListSource?.id === album?.id,
[playerSnapshot.trackListSource, album?.id] [playerSnapshot.trackListSource, album?.id]
) )
const isPlaying =
isThisAlbumPlaying &&
[PlayerState.PLAYING, PlayerState.LOADING].includes(playerSnapshot.state)
const wrappedHandlePlay = () => { const wrappedHandlePlay = () => {
if (isPlaying && isThisAlbumPlaying) { if (isThisAlbumPlaying) {
player.pause() player.playOrPause()
return } else {
}
if (!isPlaying && isThisAlbumPlaying) {
player.play()
return
}
handlePlay() handlePlay()
} }
}
return ( return (
<Button onClick={wrappedHandlePlay} isSkelton={isLoading}> <Button onClick={wrappedHandlePlay} isSkelton={isLoading}>
<SvgIcon <SvgIcon
name={isPlaying && isThisAlbumPlaying ? 'pause' : 'play'} name={isPlaying ? 'pause' : 'play'}
className='mr-1 -ml-1 h-6 w-6' className='mr-1 -ml-1 h-6 w-6'
/> />
{isPlaying && isThisAlbumPlaying ? '暂停' : '播放'} {isPlaying ? '暂停' : '播放'}
</Button> </Button>
) )
} }

View file

@ -1,5 +1,5 @@
import CoverRow, { Subtitle } from '@/renderer/components/CoverRow' import CoverRow, { Subtitle } from '@/renderer/components/CoverRow'
import SvgIcon from '@/renderer/components/SvgIcon' import SvgIcon, { SvgName } from '@/renderer/components/SvgIcon'
import useUserAlbums from '@/renderer/hooks/useUserAlbums' import useUserAlbums from '@/renderer/hooks/useUserAlbums'
import useLyric from '@/renderer/hooks/useLyric' import useLyric from '@/renderer/hooks/useLyric'
import usePlaylist from '@/renderer/hooks/usePlaylist' import usePlaylist from '@/renderer/hooks/usePlaylist'
@ -105,7 +105,7 @@ const OtherCard = ({
className, className,
}: { }: {
name: string name: string
icon: string icon: SvgName
className?: string className?: string
}) => { }) => {
return ( return (

View file

@ -12,9 +12,55 @@ import useUserPlaylists, {
useMutationLikeAPlaylist, useMutationLikeAPlaylist,
} from '@/renderer/hooks/useUserPlaylists' } from '@/renderer/hooks/useUserPlaylists'
import useUser from '@/renderer/hooks/useUser' import useUser from '@/renderer/hooks/useUser'
import {
Mode as PlayerMode,
TrackListSourceType,
State as PlayerState,
} from '@/renderer/utils/player'
const enableRenderLog = true const enableRenderLog = true
const PlayButton = ({
playlist,
handlePlay,
isLoading,
}: {
playlist: Playlist | undefined
handlePlay: () => void
isLoading: boolean
}) => {
const playerSnapshot = useSnapshot(player)
const isThisPlaylistPlaying = useMemo(
() =>
playerSnapshot.mode === PlayerMode.PLAYLIST &&
playerSnapshot.trackListSource?.type === TrackListSourceType.PLAYLIST &&
playerSnapshot.trackListSource?.id === playlist?.id,
[playerSnapshot.trackListSource, playlist?.id]
)
const wrappedHandlePlay = () => {
if (isThisPlaylistPlaying) {
player.playOrPause()
} else {
handlePlay()
}
}
const isPlaying =
isThisPlaylistPlaying &&
[PlayerState.PLAYING, PlayerState.LOADING].includes(playerSnapshot.state)
return (
<Button onClick={wrappedHandlePlay} isSkelton={isLoading}>
<SvgIcon
name={isPlaying ? 'pause' : 'play'}
className='-ml-1 mr-1 h-6 w-6'
/>
{isPlaying ? '暂停' : '播放'}
</Button>
)
}
const Header = memo( const Header = memo(
({ ({
playlist, playlist,
@ -127,10 +173,7 @@ const Header = memo(
{/* <!-- Buttons --> */} {/* <!-- Buttons --> */}
<div className='mt-5 flex gap-4'> <div className='mt-5 flex gap-4'>
<Button onClick={() => handlePlay()} isSkelton={isLoading}> <PlayButton {...{ playlist, handlePlay, isLoading }} />
<SvgIcon name='play' className='-ml-1 mr-1 h-6 w-6' />
</Button>
{!isThisPlaylistCreatedByCurrentUser && ( {!isThisPlaylistCreatedByCurrentUser && (
<Button <Button