feat: 音乐库增加查看收藏的专辑/歌单/歌手功能

This commit is contained in:
qier222 2022-04-05 02:31:10 +08:00
parent bbd5299341
commit 2e41001d02
No known key found for this signature in database
GPG Key ID: 9C85007ED905F14D
6 changed files with 203 additions and 44 deletions

View File

@ -4,6 +4,8 @@ export enum UserApiNames {
FETCH_USER_ACCOUNT = 'fetchUserAccount',
FETCH_USER_LIKED_TRACKS_IDS = 'fetchUserLikedTracksIDs',
FETCH_USER_PLAYLISTS = 'fetchUserPlaylists',
FETCH_USER_ALBUMS = 'fetchUserAlbums',
FETCH_USER_ARTIST = 'fetchUserArtists',
}
/**
@ -99,7 +101,7 @@ export interface FetchUserPlaylistsParams {
}
export interface FetchUserPlaylistsResponse {
code: number
more: false
more: boolean
version: string
playlist: Playlist[]
}
@ -151,40 +153,44 @@ export function dailySignin(type = 0) {
})
}
/**
*
* 说明 : 调用此接口可获取到用户收藏的专辑
* - limit : 返回数量 , 25
* - offset : 偏移数量 , :( -1)*25, 25 limit , 0
* @param {Object} params
* @param {number} params.limit
* @param {number=} params.offset
*/
// export function likedAlbums(params) {
// return request({
// url: '/album/sublist',
// method: 'get',
// params: {
// limit: params.limit,
// timestamp: new Date().getTime(),
// },
// })
// }
export interface FetchUserAlbumsParams {
offset?: number // default 0
limit?: number // default 25
}
export interface FetchUserAlbumsResponse {
code: number
hasMore: boolean
paidCount: number
count: number
data: Album[]
}
export function fetchUserAlbums(params: FetchUserAlbumsParams) {
return request({
url: '/album/sublist',
method: 'get',
params: {
...params,
timestamp: new Date().getTime(),
},
})
}
/**
*
* 说明 : 调用此接口可获取到用户收藏的歌手
*/
// export function likedArtists(params) {
// return request({
// url: '/artist/sublist',
// method: 'get',
// params: {
// limit: params.limit,
// timestamp: new Date().getTime(),
// },
// })
// }
// 获取收藏的歌手
export interface FetchUserArtistsResponse {
code: number
hasMore: boolean
count: number
data: Artist[]
}
export function fetchUserArtists(): Promise<FetchUserArtistsResponse> {
return request({
url: '/artist/sublist',
method: 'get',
params: {
timestamp: new Date().getTime(),
},
})
}
/**
* MV

View File

@ -21,7 +21,7 @@ const Cover = ({
{showHover && (
<div
className={classNames(
'absolute top-2 z-[-1] h-full w-full scale-x-[.92] scale-y-[.96] rounded-xl bg-cover opacity-0 blur-lg filter transition duration-300 group-hover:opacity-60',
'absolute top-2 z-[-1] h-full w-full scale-x-[.92] scale-y-[.96] bg-cover opacity-0 blur-lg filter transition duration-300 group-hover:opacity-60',
roundedClass
)}
style={{

View File

@ -38,7 +38,12 @@ const getSubtitleText = (
subtitle: Subtitle
) => {
const nickname = 'creator' in item ? item.creator.nickname : 'someone'
const artist = 'artist' in item ? item.artist.name : 'unknown'
const artist =
'artist' in item
? item.artist.name
: 'artists' in item
? item.artists?.[0]?.name
: 'unknown'
const copywriter = 'copywriter' in item ? item.copywriter : 'unknown'
const releaseYear =
('publishTime' in item &&
@ -125,10 +130,10 @@ const CoverRow = ({
<div
className={classNames(
'grid gap-x-6 gap-y-7',
'grid',
className,
!className &&
'grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6'
'grid-cols-3 gap-x-6 gap-y-7 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6'
)}
>
{renderItems.map((item, index) => (
@ -146,6 +151,7 @@ const CoverRow = ({
onClick={() => goTo(item.id)}
imageUrl={getImageUrl(item)}
showPlayButton={true}
roundedClass={artists ? 'rounded-full' : 'rounded-xl'}
/>
)}
@ -163,9 +169,14 @@ const CoverRow = ({
</Skeleton>
</div>
) : (
<span className='line-clamp-2 leading-tight '>
<span
className={classNames(
'line-clamp-2 leading-tight',
artists && 'mt-3 text-center'
)}
>
{/* Playlist private icon */}
{(item as Playlist).privacy && (
{(item as Playlist).privacy === 10 && (
<SvgIcon
name='lock'
className='mr-1 mb-1 inline-block h-3 w-3 text-gray-300'
@ -197,9 +208,11 @@ const CoverRow = ({
PLACEHOLDER
</Skeleton>
) : (
<div className='flex text-[12px] text-gray-500 dark:text-gray-400'>
<span>{getSubtitleText(item, subtitle)}</span>
</div>
!artists && (
<div className='flex text-[12px] text-gray-500 dark:text-gray-400'>
<span>{getSubtitleText(item, subtitle)}</span>
</div>
)
)}
</div>
</div>

View File

@ -0,0 +1,16 @@
import type { FetchUserAlbumsParams, FetchUserAlbumsResponse } from '@/api/user'
import { UserApiNames, fetchUserAlbums } from '@/api/user'
export default function useUserAlbums(params: FetchUserAlbumsParams) {
return useQuery(
[UserApiNames.FETCH_USER_ALBUMS, params],
() => fetchUserAlbums(params),
{
placeholderData: (): FetchUserAlbumsResponse =>
window.ipcRenderer?.sendSync('getApiCacheSync', {
api: 'album/sublist',
query: params,
}),
}
)
}

View File

@ -0,0 +1,11 @@
import type { FetchUserArtistsResponse } from '@/api/user'
import { UserApiNames, fetchUserArtists } from '@/api/user'
export default function useUserArtists() {
return useQuery([UserApiNames.FETCH_USER_ARTIST], fetchUserArtists, {
placeholderData: (): FetchUserArtistsResponse =>
window.ipcRenderer?.sendSync('getApiCacheSync', {
api: 'album/sublist',
}),
})
}

View File

@ -1,4 +1,6 @@
import CoverRow, { Subtitle } from '@/components/CoverRow'
import SvgIcon from '@/components/SvgIcon'
import useUserAlbums from '@/hooks/useUserAlbums'
import useLyric from '@/hooks/useLyric'
import usePlaylist from '@/hooks/usePlaylist'
import useUser from '@/hooks/useUser'
@ -6,6 +8,7 @@ import useUserPlaylists from '@/hooks/useUserPlaylists'
import { player } from '@/store'
import { resizeImage } from '@/utils/common'
import { sample, chunk } from 'lodash-es'
import useUserArtists from '@/hooks/useUserArtists'
const LikedTracksCard = ({ className }: { className?: string }) => {
const navigate = useNavigate()
@ -122,8 +125,116 @@ const OtherCard = ({
)
}
const Playlists = () => {
const { data: user } = useUser()
const { data: playlists } = useUserPlaylists({
uid: user?.account?.id ?? 0,
offset: 0,
})
return (
<div>
<CoverRow
playlists={playlists?.playlist?.slice(1) ?? []}
subtitle={Subtitle.CREATOR}
/>
</div>
)
}
const Albums = () => {
const { data: albums } = useUserAlbums({
limit: 1000,
})
return (
<div>
<CoverRow albums={albums?.data ?? []} subtitle={Subtitle.ARTIST} />
</div>
)
}
const Artists = () => {
const { data: artists } = useUserArtists()
return (
<div>
<CoverRow artists={artists?.data ?? []} subtitle={Subtitle.ARTIST} />
</div>
)
}
const MVs = () => {
return <div></div>
}
const Podcasts = () => {
return <div></div>
}
interface TabsType {
playlist: string
album: string
artist: string
mv: string
podcast: string
}
const TabHeader = ({
activeTab,
tabs,
setActiveTab,
}: {
activeTab: keyof TabsType
tabs: TabsType
setActiveTab: (tab: keyof TabsType) => void
}) => {
return (
<div className='mt-10 flex text-lg'>
{Object.entries(tabs).map(([id, name]) => (
<div
key={id}
onClick={() => setActiveTab(id as keyof TabsType)}
className={classNames(
'btn-pressed-animation mr-3 rounded-lg px-3.5 py-1.5 font-medium',
activeTab === id
? 'bg-black/[.04]'
: 'btn-hover-animation after:bg-black/[.04] dark:after:bg-white/10'
)}
>
{name}
</div>
))}
</div>
)
}
const Tabs = () => {
return <div></div>
const tabs = {
playlist: '全部歌单',
album: '专辑',
artist: '艺人',
mv: 'MV',
podcast: '播客',
}
const [activeTab, setActiveTab] = useState<keyof TabsType>('playlist')
return (
<>
<TabHeader
activeTab={activeTab}
tabs={tabs}
setActiveTab={setActiveTab}
/>
<div className='mt-6'>
{activeTab === 'playlist' && <Playlists />}
{activeTab === 'album' && <Albums />}
{activeTab === 'artist' && <Artists />}
{activeTab === 'mv' && <MVs />}
{activeTab === 'podcast' && <Podcasts />}
</div>
</>
)
}
const Library = () => {
@ -148,6 +259,8 @@ const Library = () => {
<OtherCard name='最近播放' icon='playlist' className='' />
<OtherCard name='听歌排行' icon='music-library' className='' />
</div>
<Tabs />
</div>
)
}