feat: updates

This commit is contained in:
qier222 2022-04-02 02:12:15 +08:00
parent 719a3a60d4
commit 86f088e5c9
No known key found for this signature in database
GPG key ID: 9C85007ED905F14D
9 changed files with 87 additions and 69 deletions

View file

@ -14,7 +14,8 @@
"dev:renderer": "vite dev", "dev:renderer": "vite dev",
"build:main": "node scripts/build.main.mjs", "build:main": "node scripts/build.main.mjs",
"build:renderer": "vite build", "build:renderer": "vite build",
"build": "npm run typecheck && npm run build:renderer && npm run build:main && electron-builder --config .electron-builder.config.js --dir", "build": "npm run typecheck && cross-env-shell IS_ELECTRON=true npm run build:renderer && npm run build:main && electron-builder --config .electron-builder.config.js",
"build-dir": "npm run typecheck && cross-env-shell IS_ELECTRON=true npm run build:renderer && npm run build:main && electron-builder --config .electron-builder.config.js --dir",
"typecheck": "tsc --noEmit --project src/renderer/tsconfig.json", "typecheck": "tsc --noEmit --project src/renderer/tsconfig.json",
"debug": "cross-env-shell NODE_ENV=debug \"npm run typecheck && node scripts/build.mjs && vite ./src/renderer\"", "debug": "cross-env-shell NODE_ENV=debug \"npm run typecheck && node scripts/build.mjs && vite ./src/renderer\"",
"eslint": "eslint --ext .ts,.js ./", "eslint": "eslint --ext .ts,.js ./",

View file

@ -57,7 +57,7 @@ const PlayingTrack = () => {
</div> </div>
</div> </div>
<IconButton onClick={() => toast('Work in progress')}> <IconButton onClick={() => toast('施工中...')}>
<SvgIcon <SvgIcon
className='h-6 w-6 text-black dark:text-white' className='h-6 w-6 text-black dark:text-white'
name='heart-outline' name='heart-outline'
@ -115,23 +115,30 @@ const Others = () => {
const playerSnapshot = useSnapshot(player) const playerSnapshot = useSnapshot(player)
const mode = useMemo(() => playerSnapshot.mode, [playerSnapshot.mode]) const mode = useMemo(() => playerSnapshot.mode, [playerSnapshot.mode])
const isFM = () => mode === PlayerMode.FM
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 onClick={() => toast('Work in progress')} disabled={isFM()}> <IconButton
onClick={() => toast('Work in progress')}
disabled={mode === PlayerMode.FM}
>
<SvgIcon className='h-6 w-6' name='playlist' /> <SvgIcon className='h-6 w-6' name='playlist' />
</IconButton> </IconButton>
<IconButton onClick={() => toast('Work in progress')} disabled={isFM()}> <IconButton
onClick={() => toast('施工中...')}
disabled={mode === PlayerMode.FM}
>
<SvgIcon className='h-6 w-6' name='repeat' /> <SvgIcon className='h-6 w-6' name='repeat' />
</IconButton> </IconButton>
<IconButton onClick={() => toast('Work in progress')} disabled={isFM()}> <IconButton
onClick={() => toast('施工中...')}
disabled={mode === PlayerMode.FM}
>
<SvgIcon className='h-6 w-6' name='shuffle' /> <SvgIcon className='h-6 w-6' name='shuffle' />
</IconButton> </IconButton>
<IconButton onClick={() => toast('Work in progress')}> <IconButton onClick={() => toast('施工中...')}>
<SvgIcon className='h-6 w-6' name='volume' /> <SvgIcon className='h-6 w-6' name='volume' />
</IconButton> </IconButton>
<IconButton onClick={() => toast('Work in progress')}> <IconButton onClick={() => toast('施工中...')}>
<SvgIcon className='h-6 w-6' name='lyrics' /> <SvgIcon className='h-6 w-6' name='lyrics' />
</IconButton> </IconButton>
</div> </div>

View file

@ -68,7 +68,10 @@ const SearchBox = () => {
const Settings = () => { const Settings = () => {
return ( return (
<div className='app-region-no-drag btn-hover-animation rounded-lg p-2.5 text-gray-500 transition duration-300 after:rounded-full after:bg-black/[.06] hover:text-gray-900 dark:text-gray-300 dark:after:bg-white/10 dark:hover:text-gray-200'> <div
onClick={() => toast('施工中...')}
className='app-region-no-drag btn-hover-animation rounded-lg p-2.5 text-gray-500 transition duration-300 after:rounded-full after:bg-black/[.06] hover:text-gray-900 dark:text-gray-300 dark:after:bg-white/10 dark:hover:text-gray-200'
>
<SvgIcon className='h-[1.125rem] w-[1.125rem]' name='settings' /> <SvgIcon className='h-[1.125rem] w-[1.125rem]' name='settings' />
</div> </div>
) )
@ -78,20 +81,27 @@ const Avatar = () => {
const navigate = useNavigate() const navigate = useNavigate()
const { data: user } = useUser() const { data: user } = useUser()
const avatarUrl = resizeImage(user?.profile?.avatarUrl ?? '', 'sm') const avatarUrl = user?.profile?.avatarUrl
? resizeImage(user?.profile?.avatarUrl ?? '', 'sm')
: ''
return ( return (
<img <>
src={avatarUrl} {avatarUrl ? (
onClick={() => navigate('/login')} <img
className='app-region-no-drag h-9 w-9 rounded-full bg-gray-100 dark:bg-gray-700' src={avatarUrl}
/> onClick={() => navigate('/login')}
// <div onClick={() => navigate('/login')}> className='app-region-no-drag h-9 w-9 rounded-full bg-gray-100 dark:bg-gray-700'
// <SvgIcon />
// name='user' ) : (
// className='h-9 w-9 rounded-full bg-gray-400/10 p-1 text-gray-400' <div onClick={() => navigate('/login')}>
// /> <SvgIcon
// </div> name='user'
className='h-9 w-9 rounded-full bg-black/[.06] p-1 text-gray-500'
/>
</div>
)}
</>
) )
} }

View file

@ -191,7 +191,7 @@ const Header = ({
color={ButtonColor.Gray} color={ButtonColor.Gray}
iconColor={ButtonColor.Gray} iconColor={ButtonColor.Gray}
isSkelton={isLoading} isSkelton={isLoading}
onClick={() => toast('Work in progress')} onClick={() => toast('施工中...')}
> >
<SvgIcon name='heart-outline' className='h-6 w-6' /> <SvgIcon name='heart-outline' className='h-6 w-6' />
</Button> </Button>
@ -200,7 +200,7 @@ const Header = ({
color={ButtonColor.Gray} color={ButtonColor.Gray}
iconColor={ButtonColor.Gray} iconColor={ButtonColor.Gray}
isSkelton={isLoading} isSkelton={isLoading}
onClick={() => toast('Work in progress')} onClick={() => toast('施工中...')}
> >
<SvgIcon name='more' className='h-6 w-6' /> <SvgIcon name='more' className='h-6 w-6' />
</Button> </Button>

View file

@ -8,6 +8,21 @@ import DailyTracksCard from '@/components/DailyTracksCard'
import FMCard from '@/components/FMCard' import FMCard from '@/components/FMCard'
export default function Home() { export default function Home() {
const {
data: dailyRecommendPlaylists,
isLoading: isLoadingDailyRecommendPlaylists,
} = useQuery(
PlaylistApiNames.FETCH_DAILY_RECOMMEND_PLAYLISTS,
fetchDailyRecommendPlaylists,
{
retry: false,
placeholderData: () =>
window.ipcRenderer?.sendSync('getApiCacheSync', {
api: 'recommend/resource',
}),
}
)
const { const {
data: recommendedPlaylists, data: recommendedPlaylists,
isLoading: isLoadingRecommendedPlaylists, isLoading: isLoadingRecommendedPlaylists,
@ -24,20 +39,6 @@ export default function Home() {
} }
) )
const {
data: dailyRecommendPlaylists,
isLoading: isLoadingDailyRecommendPlaylists,
} = useQuery(
PlaylistApiNames.FETCH_DAILY_RECOMMEND_PLAYLISTS,
fetchDailyRecommendPlaylists,
{
placeholderData: () =>
window.ipcRenderer?.sendSync('getApiCacheSync', {
api: 'recommend/resource',
}),
}
)
const playlists = [ const playlists = [
...(dailyRecommendPlaylists?.recommend ?? []), ...(dailyRecommendPlaylists?.recommend ?? []),
...(recommendedPlaylists?.result ?? []), ...(recommendedPlaylists?.result ?? []),

View file

@ -28,7 +28,7 @@ const EmailInput = ({
return ( return (
<div className='w-full'> <div className='w-full'>
<div className='mb-1 text-sm font-medium text-gray-700 dark:text-gray-300'> <div className='mb-1 text-sm font-medium text-gray-700 dark:text-gray-300'>
Email
</div> </div>
<input <input
value={email} value={email}
@ -54,7 +54,7 @@ const PhoneInput = ({
return ( return (
<div className='w-full'> <div className='w-full'>
<div className='mb-1 text-sm font-medium text-gray-700 dark:text-gray-300'> <div className='mb-1 text-sm font-medium text-gray-700 dark:text-gray-300'>
Phone
</div> </div>
<div className='flex w-full'> <div className='flex w-full'>
<input <input
@ -91,7 +91,7 @@ const PasswordInput = ({
return ( return (
<div className='mt-3 flex w-full flex-col'> <div className='mt-3 flex w-full flex-col'>
<div className='mb-1 text-sm font-medium text-gray-700 dark:text-gray-300'> <div className='mb-1 text-sm font-medium text-gray-700 dark:text-gray-300'>
Password
</div> </div>
<div className='flex w-full'> <div className='flex w-full'>
<input <input
@ -135,7 +135,7 @@ const LoginButton = ({
'bg-brand-100 text-brand-300 dark:bg-brand-700 dark:text-white/50' 'bg-brand-100 text-brand-300 dark:bg-brand-700 dark:text-white/50'
)} )}
> >
Login
</button> </button>
) )
} }
@ -153,15 +153,15 @@ const OtherLoginMethods = ({
}[] = [ }[] = [
{ {
id: Method.QRCODE, id: Method.QRCODE,
name: 'QR Code', name: '二维码',
}, },
{ {
id: Method.EMAIL, id: Method.EMAIL,
name: 'Email', name: '邮箱',
}, },
{ {
id: Method.PHONE, id: Method.PHONE,
name: 'Phone', name: '手机',
}, },
] ]
return ( return (
@ -178,7 +178,7 @@ const OtherLoginMethods = ({
<button <button
key={id} key={id}
onClick={() => setMethod(id)} onClick={() => setMethod(id)}
className='flex w-full cursor-default items-center justify-center rounded-lg bg-gray-100 py-2 font-medium text-gray-600 transition duration-300 hover:bg-gray-200 hover:text-gray-800 dark:bg-gray-600 dark:text-gray-200 dark:hover:bg-gray-500 dark:hover:text-gray-100' className='flex w-full cursor-default items-center justify-center rounded-lg bg-gray-100 py-2 text-gray-600 transition duration-300 hover:bg-gray-200 hover:text-gray-800 dark:bg-gray-600 dark:text-gray-200 dark:hover:bg-gray-500 dark:hover:text-gray-100'
> >
<SvgIcon className='mr-2 h-5 w-5' name={id} /> <SvgIcon className='mr-2 h-5 w-5' name={id} />
<span>{name}</span> <span>{name}</span>
@ -312,7 +312,7 @@ const LoginWithPhone = () => {
// Login with QRCode // Login with QRCode
const LoginWithQRCode = () => { const LoginWithQRCode = () => {
const [qrCodeMessage, setQrCodeMessage] = useState('扫码登录') const [qrCodeMessage, setQrCodeMessage] = useState('打开网易云音乐,扫码登录')
const [qrCodeImage, setQrCodeImage] = useState('') const [qrCodeImage, setQrCodeImage] = useState('')
const navigate = useNavigate() const navigate = useNavigate()
@ -344,7 +344,7 @@ const LoginWithQRCode = () => {
refetchKey() refetchKey()
break break
case 801: case 801:
setQrCodeMessage('等待扫码') setQrCodeMessage('打开网易云音乐,扫码登录')
break break
case 802: case 802:
setQrCodeMessage('等待确认') setQrCodeMessage('等待确认')

View file

@ -118,7 +118,7 @@ const Header = memo(
color={ButtonColor.Gray} color={ButtonColor.Gray}
iconColor={ButtonColor.Gray} iconColor={ButtonColor.Gray}
isSkelton={isLoading} isSkelton={isLoading}
onClick={() => toast('Work in progress')} onClick={() => toast('施工中...')}
> >
<SvgIcon name='heart-outline' className='h-6 w-6' /> <SvgIcon name='heart-outline' className='h-6 w-6' />
</Button> </Button>
@ -127,7 +127,7 @@ const Header = memo(
color={ButtonColor.Gray} color={ButtonColor.Gray}
iconColor={ButtonColor.Gray} iconColor={ButtonColor.Gray}
isSkelton={isLoading} isSkelton={isLoading}
onClick={() => toast('Work in progress')} onClick={() => toast('施工中...')}
> >
<SvgIcon name='more' className='h-6 w-6' /> <SvgIcon name='more' className='h-6 w-6' />
</Button> </Button>

View file

@ -157,7 +157,10 @@ export class Player {
*/ */
private async _fetchAudioSource(trackID: TrackID) { private async _fetchAudioSource(trackID: TrackID) {
const response = await fetchAudioSourceWithReactQuery({ id: trackID }) const response = await fetchAudioSourceWithReactQuery({ id: trackID })
if (response.data?.[0]?.url) return response.data[0].url return {
audio: response.data?.[0]?.url,
id: trackID,
}
} }
/** /**
@ -174,14 +177,14 @@ export class Player {
* Play audio via howler * Play audio via howler
*/ */
private async _playAudio() { private async _playAudio() {
const audio = await this._fetchAudioSource(this.trackID) const { audio, id } = await this._fetchAudioSource(this.trackID)
if (!audio) { if (!audio) {
toast('Failed to load audio source') toast('Failed to load audio source')
return return
} }
Howler.unload() Howler.unload()
const howler = new Howl({ const howler = new Howl({
src: [audio], src: [`${audio}?id=${id}`],
format: ['mp3', 'flac'], format: ['mp3', 'flac'],
html5: true, html5: true,
autoplay: true, autoplay: true,
@ -191,8 +194,9 @@ export class Player {
_howler = howler _howler = howler
this.play() this.play()
this.state = State.PLAYING this.state = State.PLAYING
_howler.once('load', () => {
this._cacheAudio(this.trackID, audio) this._cacheAudio(_howler._src)
})
if (!this._progressInterval) { if (!this._progressInterval) {
this._setupProgressInterval() this._setupProgressInterval()
@ -209,8 +213,10 @@ export class Player {
} }
} }
private _cacheAudio(id: number, audio: string) { private _cacheAudio(audio: string) {
if (audio.includes('yesplaymusic')) return if (audio.includes('yesplaymusic')) return
const id = Number(audio.split('?id=')[1])
if (isNaN(id) || !id) return
cacheAudio(id, audio) cacheAudio(id, audio)
} }
@ -242,18 +248,13 @@ export class Player {
play(fade: boolean = false) { play(fade: boolean = false) {
if (_howler.playing()) return if (_howler.playing()) return
const setPlayState = () => {
this.state = State.PLAYING
}
_howler.play() _howler.play()
if (fade) { if (fade) {
_howler.once('play', () => { _howler.once('play', () => {
_howler.fade(0, this._volume, PLAY_PAUSE_FADE_DURATION) _howler.fade(0, this._volume, PLAY_PAUSE_FADE_DURATION)
setPlayState()
}) })
} else { } else {
setPlayState() this.state = State.PLAYING
} }
} }
@ -262,18 +263,15 @@ export class Player {
* @param {boolean} fade fade out * @param {boolean} fade fade out
*/ */
pause(fade: boolean = false) { pause(fade: boolean = false) {
const setPauseState = () => {
_howler.pause()
this.state = State.PAUSED
}
if (fade) { if (fade) {
_howler.fade(this._volume, 0, PLAY_PAUSE_FADE_DURATION) _howler.fade(this._volume, 0, PLAY_PAUSE_FADE_DURATION)
this.state = State.PAUSED
_howler.once('fade', () => { _howler.once('fade', () => {
setPauseState() _howler.pause()
}) })
} else { } else {
setPauseState() this.state = State.PAUSED
_howler.pause()
} }
} }

View file

@ -42,6 +42,7 @@ export default defineConfig({
], ],
base: './', base: './',
build: { build: {
target: process.env.IS_ELECTRON ? 'esnext' : 'modules',
sourcemap: process.env.NODE_ENV === 'debug', sourcemap: process.env.NODE_ENV === 'debug',
outDir: '../../dist/renderer', outDir: '../../dist/renderer',
emptyOutDir: true, emptyOutDir: true,