feat: updates

This commit is contained in:
qier222 2022-03-24 14:23:04 +08:00
parent 3b9d728410
commit 46349e8314
No known key found for this signature in database
GPG key ID: 9C85007ED905F14D
10 changed files with 117 additions and 39 deletions

View file

@ -80,7 +80,7 @@ export function getCache(
break break
} }
case 'user/playlist': { case 'user/playlist': {
if (!query.uid) return if (isNaN(Number(query.uid))) return
const userPlaylists = db.get( const userPlaylists = db.get(
ModelNames.USER_PLAYLISTS, ModelNames.USER_PLAYLISTS,
Number(query?.uid) Number(query?.uid)
@ -90,7 +90,16 @@ export function getCache(
} }
case 'song/detail': { case 'song/detail': {
const ids: string[] = query?.ids.split(',') const ids: string[] = query?.ids.split(',')
if (ids.length === 0) return
let isIDsValid = true
ids.forEach(id => {
if (id === '' || isNaN(Number(id))) isIDsValid = false
})
if (!isIDsValid) return
const idsQuery = ids.map(id => `id = ${id}`).join(' OR ') const idsQuery = ids.map(id => `id = ${id}`).join(' OR ')
console.log(idsQuery)
const tracksRaw = realm const tracksRaw = realm
.objects(ModelNames.TRACK) .objects(ModelNames.TRACK)
.filtered(`(${idsQuery})`) .filtered(`(${idsQuery})`)
@ -109,28 +118,28 @@ export function getCache(
} }
} }
case 'album': { case 'album': {
if (!query?.id) return if (isNaN(Number(query?.id))) return
const album = db.get(ModelNames.ALBUM, Number(query?.id)) as any const album = db.get(ModelNames.ALBUM, Number(query?.id)) as any
if (checkIsExpired && isCacheExpired(album?.updateAt, 24 * 60)) return if (checkIsExpired && isCacheExpired(album?.updateAt, 24 * 60)) return
if (album?.json) return JSON.parse(album.json) if (album?.json) return JSON.parse(album.json)
break break
} }
case 'playlist/detail': { case 'playlist/detail': {
if (!query?.id) return if (isNaN(Number(query?.id))) return
const playlist = db.get(ModelNames.PLAYLIST, Number(query?.id)) as any const playlist = db.get(ModelNames.PLAYLIST, Number(query?.id)) as any
if (checkIsExpired && isCacheExpired(playlist?.updateAt, 10)) return if (checkIsExpired && isCacheExpired(playlist?.updateAt, 10)) return
if (playlist?.json) return JSON.parse(playlist.json) if (playlist?.json) return JSON.parse(playlist.json)
break break
} }
case 'artists': { case 'artists': {
if (!query?.id) return if (isNaN(Number(query?.id))) return
const artist = db.get(ModelNames.ARTIST, Number(query?.id)) as any const artist = db.get(ModelNames.ARTIST, Number(query?.id)) as any
if (checkIsExpired && isCacheExpired(artist?.updateAt, 30)) return if (checkIsExpired && isCacheExpired(artist?.updateAt, 30)) return
if (artist?.json) return JSON.parse(artist.json) if (artist?.json) return JSON.parse(artist.json)
break break
} }
case 'artist/album': { case 'artist/album': {
if (!query?.id) return if (isNaN(Number(query?.id))) return
const artistAlbums = db.get( const artistAlbums = db.get(
ModelNames.ARTIST_ALBUMS, ModelNames.ARTIST_ALBUMS,
Number(query?.id) Number(query?.id)

View file

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 2C3.34315 2 2 3.34315 2 5V19C2 20.6569 3.34315 22 5 22H19C20.6569 22 22 20.6569 22 19V5C22 3.34315 20.6569 2 19 2H5ZM9 6C8.44772 6 8 6.44772 8 7V12V17C8 17.5523 8.44772 18 9 18H15C15.5523 18 16 17.5523 16 17C16 16.4477 15.5523 16 15 16H10V13H15C15.5523 13 16 12.5523 16 12C16 11.4477 15.5523 11 15 11H10V8H15C15.5523 8 16 7.55228 16 7C16 6.44772 15.5523 6 15 6H9Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 542 B

View file

@ -170,6 +170,16 @@ const CoverRow = ({
className='mr-1 mb-1 inline-block h-3 w-3 text-gray-300' className='mr-1 mb-1 inline-block h-3 w-3 text-gray-300'
/> />
)} )}
{/* Explicit icon */}
{(item as Album)?.mark === 1056768 && (
<SvgIcon
name='explicit'
className='float-right mt-[2px] h-4 w-4 text-gray-300'
/>
)}
{/* Name */}
<span <span
onClick={() => goTo(item.id)} onClick={() => goTo(item.id)}
className='decoration-gray-600 decoration-2 hover:underline dark:text-white dark:decoration-gray-200' className='decoration-gray-600 decoration-2 hover:underline dark:text-white dark:decoration-gray-200'

View file

@ -16,17 +16,17 @@ interface PrimaryTab extends Tab {
const primaryTabs: PrimaryTab[] = [ const primaryTabs: PrimaryTab[] = [
{ {
name: 'Home', name: '主页',
icon: 'home', icon: 'home',
route: '/', route: '/',
}, },
{ {
name: 'Podcast', name: '播客',
icon: 'podcast', icon: 'podcast',
route: '/podcast', route: '/podcast',
}, },
{ {
name: 'Library', name: '音乐库',
icon: 'music-library', icon: 'music-library',
route: '/library', route: '/library',
}, },

View file

@ -115,7 +115,15 @@ const Track = memo(
isHighlight ? 'text-brand-500' : 'text-black dark:text-white' isHighlight ? 'text-brand-500' : 'text-black dark:text-white'
)} )}
> >
<span>{track.name}</span> <span className='flex items-center'>
{track.name}
{track.mark === 1318912 && (
<SvgIcon
name='explicit'
className='ml-1.5 mt-[2px] h-4 w-4 text-gray-300 dark:text-gray-500'
/>
)}
</span>
{subtitle && ( {subtitle && (
<span <span
title={subtitle} title={subtitle}
@ -227,10 +235,10 @@ const TracksAlbum = ({
<div className='mx-4 mt-10 mb-2 grid grid-cols-12 border-b border-gray-100 py-2.5 text-sm text-gray-400 dark:border-gray-800 dark:text-gray-500'> <div className='mx-4 mt-10 mb-2 grid grid-cols-12 border-b border-gray-100 py-2.5 text-sm text-gray-400 dark:border-gray-800 dark:text-gray-500'>
<div className='col-span-6 grid grid-cols-[2rem_auto]'> <div className='col-span-6 grid grid-cols-[2rem_auto]'>
<div>#</div> <div>#</div>
<div>TITLE</div> <div></div>
</div> </div>
<div className='col-span-4'>ARTIST</div> <div className='col-span-4'></div>
<div className='col-span-2 justify-self-end'>TIME</div> <div className='col-span-2 justify-self-end'></div>
</div> </div>
{/* Tracks */} {/* Tracks */}

View file

@ -1,6 +1,8 @@
import ArtistInline from '@/components/ArtistsInline' import ArtistInline from '@/components/ArtistsInline'
import Skeleton from '@/components/Skeleton' import Skeleton from '@/components/Skeleton'
import { resizeImage } from '@/utils/common' import { resizeImage } from '@/utils/common'
import { Fragment } from 'react'
import SvgIcon from './SvgIcon'
const Track = ({ const Track = ({
track, track,
@ -52,11 +54,18 @@ const Track = ({
)} )}
<div className='text-xs text-gray-500 dark:text-gray-400'> <div className='text-xs text-gray-500 dark:text-gray-400'>
{!isSkeleton && ( {isSkeleton ? (
<ArtistInline artists={track.ar} disableLink={true} />
)}
{isSkeleton && (
<Skeleton className='w-2/3 translate-y-px'>PLACE</Skeleton> <Skeleton className='w-2/3 translate-y-px'>PLACE</Skeleton>
) : (
<span className='flex items-center'>
{track.mark === 1318912 && (
<SvgIcon
name='explicit'
className='mr-1 h-3 w-3 text-gray-300 dark:text-gray-500'
/>
)}
<ArtistInline artists={track.ar} disableLink={true} />
</span>
)} )}
</div> </div>
</div> </div>

View file

@ -87,7 +87,15 @@ const Track = memo(
{isSkeleton ? ( {isSkeleton ? (
<Skeleton className='w-2/3 translate-y-px'>PLACE</Skeleton> <Skeleton className='w-2/3 translate-y-px'>PLACE</Skeleton>
) : ( ) : (
<ArtistInline artists={track.ar} /> <span className='inline-flex items-center'>
{track.mark === 1318912 && (
<SvgIcon
name='explicit'
className='mr-1 h-3.5 w-3.5 text-gray-300 dark:text-gray-500'
/>
)}
<ArtistInline artists={track.ar} />
</span>
)} )}
</div> </div>
</div> </div>
@ -191,10 +199,10 @@ const TracksList = memo(
<div className='ml-2 mr-4 mt-10 mb-2 grid grid-cols-12 border-b border-gray-100 py-2.5 text-sm text-gray-400 dark:border-gray-800 dark:text-gray-500'> <div className='ml-2 mr-4 mt-10 mb-2 grid grid-cols-12 border-b border-gray-100 py-2.5 text-sm text-gray-400 dark:border-gray-800 dark:text-gray-500'>
<div className='col-span-6 grid grid-cols-[4.2rem_auto]'> <div className='col-span-6 grid grid-cols-[4.2rem_auto]'>
<div></div> <div></div>
<div>TITLE</div> <div></div>
</div> </div>
<div className='col-span-4'>ALBUM</div> <div className='col-span-4'></div>
<div className='col-span-2 justify-self-end'>TIME</div> <div className='col-span-2 justify-self-end'></div>
</div> </div>
<div className='grid w-full'> <div className='grid w-full'>

View file

@ -16,6 +16,7 @@ import {
resizeImage, resizeImage,
scrollToTop, scrollToTop,
} from '@/utils/common' } from '@/utils/common'
import useTracks from '@/hooks/useTracks'
const PlayButton = ({ const PlayButton = ({
album, album,
@ -95,7 +96,7 @@ const Header = ({
/> />
</Fragment> </Fragment>
)} )}
<div className='absolute top-0 h-full w-full bg-gradient-to-b from-white/[.84] to-white dark:from-black/[.5] dark:to-[#1d1d1d]'></div> <div className='absolute top-0 h-full w-full bg-gradient-to-b from-white/75 to-white dark:from-black/50 dark:to-[#1d1d1d]'></div>
</div> </div>
<div className='grid grid-cols-[17rem_auto] items-center gap-9'> <div className='grid grid-cols-[17rem_auto] items-center gap-9'>
@ -285,6 +286,10 @@ const Album = () => {
id: Number(params.id) || 0, id: Number(params.id) || 0,
}) })
const { data: tracks } = useTracks({
ids: album?.songs?.map(track => track.id) ?? [],
})
const handlePlay = async (trackID: number | null = null) => { const handlePlay = async (trackID: number | null = null) => {
const realAlbum = album?.album const realAlbum = album?.album
if (!realAlbum) { if (!realAlbum) {
@ -302,7 +307,7 @@ const Album = () => {
handlePlay={handlePlay} handlePlay={handlePlay}
/> />
<TracksAlbum <TracksAlbum
tracks={album?.album.songs ?? []} tracks={tracks?.songs ?? album?.album.songs ?? []}
onTrackDoubleClick={handlePlay} onTrackDoubleClick={handlePlay}
isSkeleton={isLoading} isSkeleton={isLoading}
/> />

View file

@ -9,6 +9,7 @@ import TracksGrid from '@/components/TracksGrid'
import CoverRow, { Subtitle } from '@/components/CoverRow' import CoverRow, { Subtitle } from '@/components/CoverRow'
import Skeleton from '@/components/Skeleton' import Skeleton from '@/components/Skeleton'
import { Fragment } from 'react' import { Fragment } from 'react'
import useTracks from '@/hooks/useTracks'
const Artist = () => { const Artist = () => {
const params = useParams() const params = useParams()
@ -22,25 +23,48 @@ const Artist = () => {
limit: 1000, limit: 1000,
}) })
const { data: tracks, isLoading: isLoadingTracks } = useTracks({
ids: artist?.hotSongs?.slice(0, 10)?.map(t => t.id) ?? [],
})
const albums = useMemo(() => { const albums = useMemo(() => {
if (!albumsRaw?.hotAlbums) return [] if (!albumsRaw?.hotAlbums) return []
return albumsRaw.hotAlbums.filter( const albums: Album[] = []
album => albumsRaw.hotAlbums.forEach(album => {
album.type === '专辑' && if (album.type !== '专辑') return false
['混音版', '精选集', 'Remix'].includes(album.subType) === false && if (['混音版', '精选集', 'Remix'].includes(album.subType)) return false
album.size > 1
) // No singles
if (album.size <= 1) return false
// No remixes
if (
/(\(|\[)(.*)(Remix|remix)(.*)(\)|\])/.test(
album.name.toLocaleLowerCase()
)
) {
return false
}
// If have same name album only keep the Explicit version
const sameNameAlbumIndex = albums.findIndex(a => a.name === album.name)
if (sameNameAlbumIndex !== -1) {
if (album.mark === 1056768) albums[sameNameAlbumIndex] = album
return
}
albums.push(album)
})
return albums
}, [albumsRaw?.hotAlbums]) }, [albumsRaw?.hotAlbums])
const singles = useMemo(() => { const singles = useMemo(() => {
if (!albumsRaw?.hotAlbums) return [] if (!albumsRaw?.hotAlbums) return []
const albumsIds = albums.map(album => album.id)
return albumsRaw.hotAlbums.filter( return albumsRaw.hotAlbums.filter(
album => album => albumsIds.includes(album.id) === false
album.type !== '专辑' ||
['混音版', '精选集', 'Remix'].includes(album.subType) ||
album.size === 1
) )
}, [albumsRaw?.hotAlbums]) }, [albums, albumsRaw?.hotAlbums])
const latestAlbum = useMemo(() => { const latestAlbum = useMemo(() => {
if (!albumsRaw || !albumsRaw.hotAlbums) return if (!albumsRaw || !albumsRaw.hotAlbums) return
@ -89,7 +113,7 @@ const Artist = () => {
<div className='mt-12 grid h-[20rem] grid-cols-[14rem,_auto] grid-rows-1 gap-16 px-2'> <div className='mt-12 grid h-[20rem] grid-cols-[14rem,_auto] grid-rows-1 gap-16 px-2'>
{/* Latest release */} {/* Latest release */}
<div> <div>
<div className='mb-6 text-2xl font-semibold dark:text-white'> <div className='mb-6 text-2xl font-semibold text-gray-800 dark:text-white'>
</div> </div>
<div className='flex-grow rounded-xl '> <div className='flex-grow rounded-xl '>
@ -110,12 +134,12 @@ const Artist = () => {
{/* Popular tracks */} {/* Popular tracks */}
<div> <div>
<div className='mb-6 text-2xl font-semibold dark:text-white'> <div className='mb-6 text-2xl font-semibold text-gray-800 dark:text-white'>
</div> </div>
<div className='rounded-xl'> <div className='rounded-xl'>
<TracksGrid <TracksGrid
tracks={artist?.hotSongs.slice(0, 10) ?? []} tracks={tracks?.songs ?? artist?.hotSongs.slice(0, 10) ?? []}
isSkeleton={isLoading} isSkeleton={isLoading}
/> />
</div> </div>
@ -125,7 +149,7 @@ const Artist = () => {
{/* Albums */} {/* Albums */}
{albums.length !== 0 && ( {albums.length !== 0 && (
<div className='mt-20 px-2'> <div className='mt-20 px-2'>
<div className='mb-6 text-2xl font-semibold dark:text-white'> <div className='mb-6 text-2xl font-semibold text-gray-800 dark:text-white'>
</div> </div>
<CoverRow <CoverRow
@ -137,7 +161,7 @@ const Artist = () => {
{/* Singles/EP */} {/* Singles/EP */}
<div className='mt-16 px-2'> <div className='mt-16 px-2'>
<div className='mb-6 text-2xl font-semibold dark:text-white'> <div className='mb-6 text-2xl font-semibold text-gray-800 dark:text-white'>
EP EP
</div> </div>
<CoverRow <CoverRow

View file

@ -178,7 +178,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))
const id = this.trackID
_howler.once('load', () => this._cacheAudio(id, audio))
if (!this._progressInterval) { if (!this._progressInterval) {
this._setupProgressInterval() this._setupProgressInterval()