mirror of
https://github.com/GiriNeko/YesPlayMusic.git
synced 2025-12-17 13:48:02 +00:00
feat: updates
This commit is contained in:
parent
ffcc60b793
commit
dd5361b8c4
106 changed files with 11989 additions and 4143 deletions
56
packages/web/api/hooks/useAlbum.ts
Normal file
56
packages/web/api/hooks/useAlbum.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import { fetchAlbum } from '@/web/api/album'
|
||||
import reactQueryClient from '@/web/utils/reactQueryClient'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import {
|
||||
FetchAlbumParams,
|
||||
AlbumApiNames,
|
||||
FetchAlbumResponse,
|
||||
} from '@/shared/api/Album'
|
||||
import { useQuery } from 'react-query'
|
||||
|
||||
const fetch = async (params: FetchAlbumParams, noCache?: boolean) => {
|
||||
const album = await fetchAlbum(params, !!noCache)
|
||||
if (album?.album?.songs) {
|
||||
album.album.songs = album.songs
|
||||
}
|
||||
return album
|
||||
}
|
||||
|
||||
export default function useAlbum(params: FetchAlbumParams, noCache?: boolean) {
|
||||
return useQuery(
|
||||
[AlbumApiNames.FetchAlbum, params.id],
|
||||
() => fetch(params, noCache),
|
||||
{
|
||||
enabled: !!params.id,
|
||||
staleTime: 24 * 60 * 60 * 1000, // 24 hours
|
||||
placeholderData: (): FetchAlbumResponse =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: APIs.Album,
|
||||
query: {
|
||||
id: params.id,
|
||||
},
|
||||
}),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export function fetchAlbumWithReactQuery(params: FetchAlbumParams) {
|
||||
return reactQueryClient.fetchQuery(
|
||||
[AlbumApiNames.FetchAlbum, params.id],
|
||||
() => fetch(params),
|
||||
{
|
||||
staleTime: Infinity,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export async function prefetchAlbum(params: FetchAlbumParams) {
|
||||
await reactQueryClient.prefetchQuery(
|
||||
[AlbumApiNames.FetchAlbum, params.id],
|
||||
() => fetch(params),
|
||||
{
|
||||
staleTime: Infinity,
|
||||
}
|
||||
)
|
||||
}
|
||||
30
packages/web/api/hooks/useArtist.ts
Normal file
30
packages/web/api/hooks/useArtist.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { fetchArtist } from '@/web/api/artist'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import {
|
||||
FetchArtistParams,
|
||||
ArtistApiNames,
|
||||
FetchArtistResponse,
|
||||
} from '@/shared/api/Artist'
|
||||
import { useQuery } from 'react-query'
|
||||
|
||||
export default function useArtist(
|
||||
params: FetchArtistParams,
|
||||
noCache?: boolean
|
||||
) {
|
||||
return useQuery(
|
||||
[ArtistApiNames.FetchArtist, params],
|
||||
() => fetchArtist(params, !!noCache),
|
||||
{
|
||||
enabled: !!params.id && params.id > 0 && !isNaN(Number(params.id)),
|
||||
staleTime: 5 * 60 * 1000, // 5 mins
|
||||
placeholderData: (): FetchArtistResponse =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: APIs.Artist,
|
||||
query: {
|
||||
id: params.id,
|
||||
},
|
||||
}),
|
||||
}
|
||||
)
|
||||
}
|
||||
30
packages/web/api/hooks/useArtistAlbums.ts
Normal file
30
packages/web/api/hooks/useArtistAlbums.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { fetchArtistAlbums } from '@/web/api/artist'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import {
|
||||
FetchArtistAlbumsParams,
|
||||
ArtistApiNames,
|
||||
FetchArtistAlbumsResponse,
|
||||
} from '@/shared/api/Artist'
|
||||
import { useQuery } from 'react-query'
|
||||
|
||||
export default function useUserAlbums(params: FetchArtistAlbumsParams) {
|
||||
return useQuery(
|
||||
[ArtistApiNames.FetchArtistAlbums, params],
|
||||
async () => {
|
||||
const data = await fetchArtistAlbums(params)
|
||||
return data
|
||||
},
|
||||
{
|
||||
enabled: !!params.id && params.id !== 0,
|
||||
staleTime: 3600000,
|
||||
placeholderData: (): FetchArtistAlbumsResponse =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: APIs.ArtistAlbum,
|
||||
query: {
|
||||
id: params.id,
|
||||
},
|
||||
}),
|
||||
}
|
||||
)
|
||||
}
|
||||
47
packages/web/api/hooks/useLyric.ts
Normal file
47
packages/web/api/hooks/useLyric.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import { fetchLyric } from '@/web/api/track'
|
||||
import reactQueryClient from '@/web/utils/reactQueryClient'
|
||||
import {
|
||||
FetchLyricParams,
|
||||
FetchLyricResponse,
|
||||
TrackApiNames,
|
||||
} from '@/shared/api/Track'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import { useQuery } from 'react-query'
|
||||
|
||||
export default function useLyric(params: FetchLyricParams) {
|
||||
return useQuery(
|
||||
[TrackApiNames.FetchLyric, params],
|
||||
() => {
|
||||
return fetchLyric(params)
|
||||
},
|
||||
{
|
||||
enabled: !!params.id && params.id !== 0,
|
||||
refetchInterval: false,
|
||||
staleTime: Infinity,
|
||||
initialData: (): FetchLyricResponse | undefined =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: APIs.Lyric,
|
||||
query: {
|
||||
id: params.id,
|
||||
},
|
||||
}),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export function fetchTracksWithReactQuery(params: FetchLyricParams) {
|
||||
return reactQueryClient.fetchQuery(
|
||||
[TrackApiNames.FetchLyric, params],
|
||||
() => {
|
||||
return fetchLyric(params)
|
||||
},
|
||||
{
|
||||
retry: 4,
|
||||
retryDelay: (retryCount: number) => {
|
||||
return retryCount * 500
|
||||
},
|
||||
staleTime: Infinity,
|
||||
}
|
||||
)
|
||||
}
|
||||
18
packages/web/api/hooks/usePersonalFM.ts
Normal file
18
packages/web/api/hooks/usePersonalFM.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { fetchPersonalFM, PersonalFMApiNames } from '@/web/api/personalFM'
|
||||
import reactQueryClient from '@/web/utils/reactQueryClient'
|
||||
|
||||
export function fetchPersonalFMWithReactQuery() {
|
||||
return reactQueryClient.fetchQuery(
|
||||
PersonalFMApiNames.FetchPersonalFm,
|
||||
async () => {
|
||||
const data = await fetchPersonalFM()
|
||||
if (!data.data?.length) {
|
||||
throw new Error('No data')
|
||||
}
|
||||
return data
|
||||
},
|
||||
{
|
||||
retry: 3,
|
||||
}
|
||||
)
|
||||
}
|
||||
55
packages/web/api/hooks/usePlaylist.ts
Normal file
55
packages/web/api/hooks/usePlaylist.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import { fetchPlaylist } from '@/web/api/playlist'
|
||||
import reactQueryClient from '@/web/utils/reactQueryClient'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import {
|
||||
FetchPlaylistParams,
|
||||
PlaylistApiNames,
|
||||
FetchPlaylistResponse,
|
||||
} from '@/shared/api/Playlists'
|
||||
import { useQuery } from 'react-query'
|
||||
|
||||
const fetch = (params: FetchPlaylistParams, noCache?: boolean) => {
|
||||
return fetchPlaylist(params, !!noCache)
|
||||
}
|
||||
|
||||
export default function usePlaylist(
|
||||
params: FetchPlaylistParams,
|
||||
noCache?: boolean
|
||||
) {
|
||||
return useQuery(
|
||||
[PlaylistApiNames.FetchPlaylist, params],
|
||||
() => fetch(params, noCache),
|
||||
{
|
||||
enabled: !!(params.id && params.id > 0 && !isNaN(Number(params.id))),
|
||||
refetchOnWindowFocus: true,
|
||||
placeholderData: (): FetchPlaylistResponse | undefined =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: APIs.Playlist,
|
||||
query: {
|
||||
id: params.id,
|
||||
},
|
||||
}),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export function fetchPlaylistWithReactQuery(params: FetchPlaylistParams) {
|
||||
return reactQueryClient.fetchQuery(
|
||||
[PlaylistApiNames.FetchPlaylist, params],
|
||||
() => fetch(params),
|
||||
{
|
||||
staleTime: 3600000,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export async function prefetchPlaylist(params: FetchPlaylistParams) {
|
||||
await reactQueryClient.prefetchQuery(
|
||||
[PlaylistApiNames.FetchPlaylist, params],
|
||||
() => fetch(params),
|
||||
{
|
||||
staleTime: 3600000,
|
||||
}
|
||||
)
|
||||
}
|
||||
62
packages/web/api/hooks/useTracks.ts
Normal file
62
packages/web/api/hooks/useTracks.ts
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import { fetchAudioSource, fetchTracks } from '@/web/api/track'
|
||||
import type {} from '@/web/api/track'
|
||||
import reactQueryClient from '@/web/utils/reactQueryClient'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import {
|
||||
FetchAudioSourceParams,
|
||||
FetchTracksParams,
|
||||
FetchTracksResponse,
|
||||
TrackApiNames,
|
||||
} from '@/shared/api/Track'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import { useQuery } from 'react-query'
|
||||
|
||||
export default function useTracks(params: FetchTracksParams) {
|
||||
return useQuery(
|
||||
[TrackApiNames.FetchTracks, params],
|
||||
() => {
|
||||
return fetchTracks(params)
|
||||
},
|
||||
{
|
||||
enabled: params.ids.length !== 0,
|
||||
refetchInterval: false,
|
||||
staleTime: Infinity,
|
||||
initialData: (): FetchTracksResponse | undefined =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: APIs.Track,
|
||||
query: {
|
||||
ids: params.ids.join(','),
|
||||
},
|
||||
}),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export function fetchTracksWithReactQuery(params: FetchTracksParams) {
|
||||
return reactQueryClient.fetchQuery(
|
||||
[TrackApiNames.FetchTracks, params],
|
||||
() => {
|
||||
return fetchTracks(params)
|
||||
},
|
||||
{
|
||||
retry: 4,
|
||||
retryDelay: (retryCount: number) => {
|
||||
return retryCount * 500
|
||||
},
|
||||
staleTime: 86400000,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export function fetchAudioSourceWithReactQuery(params: FetchAudioSourceParams) {
|
||||
return reactQueryClient.fetchQuery(
|
||||
[TrackApiNames.FetchAudioSource, params],
|
||||
() => {
|
||||
return fetchAudioSource(params)
|
||||
},
|
||||
{
|
||||
retry: 3,
|
||||
staleTime: 0, // TODO: Web版1小时缓存
|
||||
}
|
||||
)
|
||||
}
|
||||
33
packages/web/api/hooks/useTracksInfinite.ts
Normal file
33
packages/web/api/hooks/useTracksInfinite.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { FetchTracksParams, TrackApiNames } from '@/shared/api/Track'
|
||||
import { useInfiniteQuery } from 'react-query'
|
||||
import { fetchTracks } from '../track'
|
||||
|
||||
// 100 tracks each page
|
||||
const offset = 100
|
||||
|
||||
export default function useTracksInfinite(params: FetchTracksParams) {
|
||||
return useInfiniteQuery(
|
||||
[TrackApiNames.FetchTracks, params],
|
||||
({ pageParam = 0 }) => {
|
||||
const cursorStart = pageParam * offset
|
||||
const cursorEnd = cursorStart + offset
|
||||
const ids = params.ids.slice(cursorStart, cursorEnd)
|
||||
return fetchTracks({ ids })
|
||||
},
|
||||
{
|
||||
enabled: params.ids.length !== 0,
|
||||
refetchOnMount: false,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
refetchInterval: 0,
|
||||
staleTime: Infinity,
|
||||
getNextPageParam: (lastPage, pages) => {
|
||||
// 当 return undefined 时,hasNextPage会等于false
|
||||
// 当 return 非 undefined 时,return 的数据会传入上面的fetchTracks函数中
|
||||
return pages.length * offset < params.ids.length // 判断是否还有下一页
|
||||
? pages.length
|
||||
: undefined
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
15
packages/web/api/hooks/useUser.ts
Normal file
15
packages/web/api/hooks/useUser.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { fetchUserAccount } from '@/web/api/user'
|
||||
import { UserApiNames, FetchUserAccountResponse } from '@/shared/api/User'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import { useQuery } from 'react-query'
|
||||
|
||||
export default function useUser() {
|
||||
return useQuery(UserApiNames.FetchUserAccount, fetchUserAccount, {
|
||||
refetchOnWindowFocus: true,
|
||||
placeholderData: (): FetchUserAccountResponse | undefined =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: APIs.UserAccount,
|
||||
}),
|
||||
})
|
||||
}
|
||||
80
packages/web/api/hooks/useUserAlbums.ts
Normal file
80
packages/web/api/hooks/useUserAlbums.ts
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import { likeAAlbum } from '@/web/api/album'
|
||||
import { useMutation, useQuery, useQueryClient } from 'react-query'
|
||||
import useUser from './useUser'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import {
|
||||
FetchUserAlbumsParams,
|
||||
UserApiNames,
|
||||
FetchUserAlbumsResponse,
|
||||
} from '@/shared/api/User'
|
||||
import { fetchUserAlbums } from '../user'
|
||||
|
||||
export default function useUserAlbums(params: FetchUserAlbumsParams = {}) {
|
||||
const { data: user } = useUser()
|
||||
return useQuery(
|
||||
[UserApiNames.FetchUserAlbums, user?.profile?.userId ?? 0],
|
||||
() => fetchUserAlbums(params),
|
||||
{
|
||||
refetchOnWindowFocus: true,
|
||||
placeholderData: (): FetchUserAlbumsResponse | undefined =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: APIs.UserAlbums,
|
||||
query: params,
|
||||
}),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export const useMutationLikeAAlbum = () => {
|
||||
const queryClient = useQueryClient()
|
||||
const { data: user } = useUser()
|
||||
const { data: userAlbums } = useUserAlbums({ limit: 2000 })
|
||||
const uid = user?.account?.id ?? 0
|
||||
const key = [UserApiNames.FetchUserAlbums, uid]
|
||||
|
||||
return useMutation(
|
||||
async (album: Album) => {
|
||||
if (!album.id || userAlbums?.data === undefined) {
|
||||
throw new Error('album id is required or userAlbums is undefined')
|
||||
}
|
||||
const response = await likeAAlbum({
|
||||
id: album.id,
|
||||
t: userAlbums?.data.findIndex(a => a.id === album.id) > -1 ? 2 : 1,
|
||||
})
|
||||
if (response.code !== 200) throw new Error((response as any).msg)
|
||||
return response
|
||||
},
|
||||
{
|
||||
onMutate: async album => {
|
||||
// Cancel any outgoing refetches (so they don't overwrite our optimistic update)
|
||||
await queryClient.cancelQueries(key)
|
||||
|
||||
// Snapshot the previous value
|
||||
const previousData = queryClient.getQueryData(key)
|
||||
|
||||
// Optimistically update to the new value
|
||||
queryClient.setQueryData(key, old => {
|
||||
const userAlbums = old as FetchUserAlbumsResponse
|
||||
const albums = userAlbums.data
|
||||
const newAlbums =
|
||||
albums.findIndex(a => a.id === album.id) > -1
|
||||
? albums.filter(a => a.id !== album.id)
|
||||
: [...albums, album]
|
||||
return {
|
||||
...userAlbums,
|
||||
data: newAlbums,
|
||||
}
|
||||
})
|
||||
|
||||
// Return a context object with the snapshotted value
|
||||
return { previousData }
|
||||
},
|
||||
// If the mutation fails, use the context returned from onMutate to roll back
|
||||
onError: (err, trackID, context) => {
|
||||
queryClient.setQueryData(key, (context as any).previousData)
|
||||
toast((err as any).toString())
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
15
packages/web/api/hooks/useUserArtists.ts
Normal file
15
packages/web/api/hooks/useUserArtists.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { fetchUserArtists } from '@/web/api/user'
|
||||
import { UserApiNames, FetchUserArtistsResponse } from '@/shared/api/User'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import { useQuery } from 'react-query'
|
||||
|
||||
export default function useUserArtists() {
|
||||
return useQuery([UserApiNames.FetchUserArtist], fetchUserArtists, {
|
||||
refetchOnWindowFocus: true,
|
||||
placeholderData: (): FetchUserArtistsResponse =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: APIs.UserArtists,
|
||||
}),
|
||||
})
|
||||
}
|
||||
85
packages/web/api/hooks/useUserLikedTracksIDs.ts
Normal file
85
packages/web/api/hooks/useUserLikedTracksIDs.ts
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
import { likeATrack } from '@/web/api/track'
|
||||
import useUser from './useUser'
|
||||
import { useMutation, useQueryClient } from 'react-query'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import { fetchUserLikedTracksIDs } from '../user'
|
||||
import {
|
||||
FetchUserLikedTracksIDsResponse,
|
||||
UserApiNames,
|
||||
} from '@/shared/api/User'
|
||||
import { useQuery } from 'react-query'
|
||||
import toast from 'react-hot-toast'
|
||||
|
||||
export default function useUserLikedTracksIDs() {
|
||||
const { data: user } = useUser()
|
||||
const uid = user?.account?.id ?? 0
|
||||
|
||||
return useQuery(
|
||||
[UserApiNames.FetchUserLikedTracksIds, uid],
|
||||
() => fetchUserLikedTracksIDs({ uid }),
|
||||
{
|
||||
enabled: !!(uid && uid !== 0),
|
||||
refetchOnWindowFocus: true,
|
||||
placeholderData: (): FetchUserLikedTracksIDsResponse | undefined =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: APIs.Likelist,
|
||||
query: {
|
||||
uid,
|
||||
},
|
||||
}),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export const useMutationLikeATrack = () => {
|
||||
const queryClient = useQueryClient()
|
||||
const { data: user } = useUser()
|
||||
const { data: userLikedSongs } = useUserLikedTracksIDs()
|
||||
const uid = user?.account?.id ?? 0
|
||||
const key = [UserApiNames.FetchUserLikedTracksIds, uid]
|
||||
|
||||
return useMutation(
|
||||
async (trackID: number) => {
|
||||
if (!trackID || userLikedSongs?.ids === undefined) {
|
||||
throw new Error('trackID is required or userLikedSongs is undefined')
|
||||
}
|
||||
const response = await likeATrack({
|
||||
id: trackID,
|
||||
like: !userLikedSongs.ids.includes(trackID),
|
||||
})
|
||||
if (response.code !== 200) throw new Error((response as any).msg)
|
||||
return response
|
||||
},
|
||||
{
|
||||
onMutate: async trackID => {
|
||||
// Cancel any outgoing refetches (so they don't overwrite our optimistic update)
|
||||
await queryClient.cancelQueries(key)
|
||||
|
||||
// Snapshot the previous value
|
||||
const previousData = queryClient.getQueryData(key)
|
||||
|
||||
// Optimistically update to the new value
|
||||
queryClient.setQueryData(key, old => {
|
||||
const likedSongs = old as FetchUserLikedTracksIDsResponse
|
||||
const ids = likedSongs.ids
|
||||
const newIds = ids.includes(trackID)
|
||||
? ids.filter(id => id !== trackID)
|
||||
: [...ids, trackID]
|
||||
return {
|
||||
...likedSongs,
|
||||
ids: newIds,
|
||||
}
|
||||
})
|
||||
|
||||
// Return a context object with the snapshotted value
|
||||
return { previousData }
|
||||
},
|
||||
// If the mutation fails, use the context returned from onMutate to roll back
|
||||
onError: (err, trackID, context) => {
|
||||
queryClient.setQueryData(key, (context as any).previousData)
|
||||
toast((err as any).toString())
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
100
packages/web/api/hooks/useUserPlaylists.ts
Normal file
100
packages/web/api/hooks/useUserPlaylists.ts
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
import { likeAPlaylist } from '@/web/api/playlist'
|
||||
import { useMutation, useQuery, useQueryClient } from 'react-query'
|
||||
import useUser from './useUser'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import { fetchUserPlaylists } from '@/web/api/user'
|
||||
import { FetchUserPlaylistsResponse, UserApiNames } from '@/shared/api/User'
|
||||
|
||||
export default function useUserPlaylists() {
|
||||
const { data: user } = useUser()
|
||||
const uid = user?.profile?.userId ?? 0
|
||||
|
||||
const params = {
|
||||
uid: uid,
|
||||
offset: 0,
|
||||
limit: 2000,
|
||||
}
|
||||
|
||||
return useQuery(
|
||||
[UserApiNames.FetchUserPlaylists, uid],
|
||||
async () => {
|
||||
if (!params.uid) {
|
||||
throw new Error('请登录后再请求用户收藏的歌单')
|
||||
}
|
||||
const data = await fetchUserPlaylists(params)
|
||||
return data
|
||||
},
|
||||
{
|
||||
enabled: !!(
|
||||
!!params.uid &&
|
||||
params.uid !== 0 &&
|
||||
params.offset !== undefined
|
||||
),
|
||||
refetchOnWindowFocus: true,
|
||||
placeholderData: (): FetchUserPlaylistsResponse =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: APIs.UserPlaylist,
|
||||
query: {
|
||||
uid: params.uid,
|
||||
},
|
||||
}),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export const useMutationLikeAPlaylist = () => {
|
||||
const queryClient = useQueryClient()
|
||||
const { data: user } = useUser()
|
||||
const { data: userPlaylists } = useUserPlaylists()
|
||||
const uid = user?.account?.id ?? 0
|
||||
const key = [UserApiNames.FetchUserPlaylists, uid]
|
||||
|
||||
return useMutation(
|
||||
async (playlist: Playlist) => {
|
||||
if (!playlist.id || userPlaylists?.playlist === undefined) {
|
||||
throw new Error('playlist id is required or userPlaylists is undefined')
|
||||
}
|
||||
const response = await likeAPlaylist({
|
||||
id: playlist.id,
|
||||
t:
|
||||
userPlaylists.playlist.findIndex(p => p.id === playlist.id) > -1
|
||||
? 2
|
||||
: 1,
|
||||
})
|
||||
if (response.code !== 200) throw new Error((response as any).msg)
|
||||
return response
|
||||
},
|
||||
{
|
||||
onMutate: async playlist => {
|
||||
// Cancel any outgoing refetches (so they don't overwrite our optimistic update)
|
||||
await queryClient.cancelQueries(key)
|
||||
|
||||
// Snapshot the previous value
|
||||
const previousData = queryClient.getQueryData(key)
|
||||
|
||||
// Optimistically update to the new value
|
||||
queryClient.setQueryData(key, old => {
|
||||
const userPlaylists = old as FetchUserPlaylistsResponse
|
||||
const playlists = userPlaylists.playlist
|
||||
const newPlaylists =
|
||||
playlists.findIndex(p => p.id === playlist.id) > -1
|
||||
? playlists.filter(p => p.id !== playlist.id)
|
||||
: [...playlists, playlist]
|
||||
return {
|
||||
...userPlaylists,
|
||||
playlist: newPlaylists,
|
||||
}
|
||||
})
|
||||
|
||||
// Return a context object with the snapshotted value
|
||||
return { previousData }
|
||||
},
|
||||
// If the mutation fails, use the context returned from onMutate to roll back
|
||||
onError: (err, trackID, context) => {
|
||||
queryClient.setQueryData(key, (context as any).previousData)
|
||||
toast((err as any).toString())
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue