mirror of
https://github.com/GiriNeko/YesPlayMusic.git
synced 2025-12-16 13:17:46 +00:00
feat: 支持收藏歌曲
This commit is contained in:
parent
bbcf1f9340
commit
bbd5299341
7 changed files with 127 additions and 49 deletions
|
|
@ -120,3 +120,23 @@ export function fetchLyric(
|
|||
params,
|
||||
})
|
||||
}
|
||||
|
||||
export interface LikeATrackParams {
|
||||
id: number
|
||||
like: boolean
|
||||
}
|
||||
export interface LikeATrackResponse {
|
||||
code: number
|
||||
playlistId: number
|
||||
songs: Track[]
|
||||
}
|
||||
export function likeATrack(params: LikeATrackParams) {
|
||||
return request({
|
||||
url: '/like',
|
||||
method: 'post',
|
||||
params: {
|
||||
...params,
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import request from '@/utils/request'
|
|||
|
||||
export enum UserApiNames {
|
||||
FETCH_USER_ACCOUNT = 'fetchUserAccount',
|
||||
FETCH_USER_LIKED_SONGS_IDS = 'fetchUserLikedSongsIDs',
|
||||
FETCH_USER_LIKED_TRACKS_IDS = 'fetchUserLikedTracksIDs',
|
||||
FETCH_USER_PLAYLISTS = 'fetchUserPlaylists',
|
||||
}
|
||||
|
||||
|
|
@ -113,17 +113,17 @@ export function fetchUserPlaylists(
|
|||
})
|
||||
}
|
||||
|
||||
export interface FetchUserLikedSongsIDsParams {
|
||||
export interface FetchUserLikedTracksIDsParams {
|
||||
uid: number
|
||||
}
|
||||
export interface FetchUserLikedSongsIDsResponse {
|
||||
export interface FetchUserLikedTracksIDsResponse {
|
||||
code: number
|
||||
checkPoint: number
|
||||
ids: number[]
|
||||
}
|
||||
export function fetchUserLikedSongsIDs(
|
||||
params: FetchUserLikedSongsIDsParams
|
||||
): Promise<FetchUserLikedSongsIDsResponse> {
|
||||
export function fetchUserLikedTracksIDs(
|
||||
params: FetchUserLikedTracksIDsParams
|
||||
): Promise<FetchUserLikedTracksIDsResponse> {
|
||||
return request({
|
||||
url: '/likelist',
|
||||
method: 'get',
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ import ArtistInline from '@/components/ArtistsInline'
|
|||
import IconButton from '@/components/IconButton'
|
||||
import Slider from '@/components/Slider'
|
||||
import SvgIcon from '@/components/SvgIcon'
|
||||
import useUser from '@/hooks/useUser'
|
||||
import useUserLikedSongsIDs from '@/hooks/useUserLikedSongsIDs'
|
||||
import useUserLikedTracksIDs, {
|
||||
useMutationLikeATrack,
|
||||
} from '@/hooks/useUserLikedTracksIDs'
|
||||
import { player } from '@/store'
|
||||
import { resizeImage } from '@/utils/common'
|
||||
import { State as PlayerState, Mode as PlayerMode } from '@/utils/player'
|
||||
|
|
@ -18,10 +19,8 @@ const PlayingTrack = () => {
|
|||
)
|
||||
|
||||
// Liked songs ids
|
||||
const { data: user } = useUser()
|
||||
const { data: userLikedSongs } = useUserLikedSongsIDs({
|
||||
uid: user?.account?.id ?? 0,
|
||||
})
|
||||
const { data: userLikedSongs } = useUserLikedTracksIDs()
|
||||
const mutationLikeATrack = useMutationLikeATrack()
|
||||
|
||||
const toAlbum = () => {
|
||||
const id = track?.al?.id
|
||||
|
|
@ -65,7 +64,9 @@ const PlayingTrack = () => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<IconButton onClick={() => toast('施工中...')}>
|
||||
<IconButton
|
||||
onClick={() => track?.id && mutationLikeATrack.mutate(track.id)}
|
||||
>
|
||||
<SvgIcon
|
||||
className='h-5 w-5 text-black dark:text-white'
|
||||
name={
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ import { memo } from 'react'
|
|||
import ArtistInline from '@/components/ArtistsInline'
|
||||
import Skeleton from '@/components/Skeleton'
|
||||
import SvgIcon from '@/components/SvgIcon'
|
||||
import useUser from '@/hooks/useUser'
|
||||
import useUserLikedSongsIDs from '@/hooks/useUserLikedSongsIDs'
|
||||
import useUserLikedTracksIDs, {
|
||||
useMutationLikeATrack,
|
||||
} from '@/hooks/useUserLikedTracksIDs'
|
||||
import { player } from '@/store'
|
||||
import { formatDuration } from '@/utils/common'
|
||||
import { State as PlayerState } from '@/utils/player'
|
||||
|
|
@ -65,6 +66,8 @@ const Track = memo(
|
|||
[track.alia, track.tns]
|
||||
)
|
||||
|
||||
const mutationLikeATrack = useMutationLikeATrack()
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={e => onClick(e, track.id)}
|
||||
|
|
@ -163,6 +166,7 @@ const Track = memo(
|
|||
{/* Like button */}
|
||||
{!isSkeleton && (
|
||||
<button
|
||||
onClick={() => track?.id && mutationLikeATrack.mutate(track.id)}
|
||||
className={classNames(
|
||||
'mr-5 cursor-default transition duration-300 hover:scale-[1.2]',
|
||||
isLiked
|
||||
|
|
@ -213,10 +217,7 @@ const TracksAlbum = ({
|
|||
const skeletonTracks: Track[] = new Array(1).fill({})
|
||||
|
||||
// Liked songs ids
|
||||
const { data: user } = useUser()
|
||||
const { data: userLikedSongs } = useUserLikedSongsIDs({
|
||||
uid: user?.account?.id ?? 0,
|
||||
})
|
||||
const { data: userLikedSongs } = useUserLikedTracksIDs()
|
||||
|
||||
const handleClick = useCallback(
|
||||
(e: React.MouseEvent<HTMLElement>, trackID: number) => {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ import ArtistInline from '@/components/ArtistsInline'
|
|||
import Skeleton from '@/components/Skeleton'
|
||||
import SvgIcon from '@/components/SvgIcon'
|
||||
import useUser from '@/hooks/useUser'
|
||||
import useUserLikedSongsIDs from '@/hooks/useUserLikedSongsIDs'
|
||||
import useUserLikedTracksIDs, {
|
||||
useMutationLikeATrack,
|
||||
} from '@/hooks/useUserLikedTracksIDs'
|
||||
import { formatDuration, resizeImage } from '@/utils/common'
|
||||
import { player } from '@/store'
|
||||
|
||||
|
|
@ -26,6 +28,7 @@ const Track = memo(
|
|||
() => track.tns?.at(0) ?? track.alia?.at(0),
|
||||
[track.alia, track.tns]
|
||||
)
|
||||
const mutationLikeATrack = useMutationLikeATrack()
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
@ -129,6 +132,7 @@ const Track = memo(
|
|||
{/* Like button */}
|
||||
{!isSkeleton && (
|
||||
<button
|
||||
onClick={() => track?.id && mutationLikeATrack.mutate(track.id)}
|
||||
className={classNames(
|
||||
'mr-5 cursor-default transition duration-300 hover:scale-[1.2]',
|
||||
!isLiked && 'text-gray-600 opacity-0 dark:text-gray-400',
|
||||
|
|
@ -181,10 +185,7 @@ const TracksList = memo(
|
|||
const skeletonTracks: Track[] = new Array(12).fill({})
|
||||
|
||||
// Liked songs ids
|
||||
const { data: user } = useUser()
|
||||
const { data: userLikedSongs } = useUserLikedSongsIDs({
|
||||
uid: user?.account?.id ?? 0,
|
||||
})
|
||||
const { data: userLikedSongs } = useUserLikedTracksIDs()
|
||||
|
||||
const handleClick = (e: React.MouseEvent<HTMLElement>, trackID: number) => {
|
||||
if (e.detail === 2) onTrackDoubleClick?.(trackID)
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
import type {
|
||||
FetchUserLikedSongsIDsParams,
|
||||
FetchUserLikedSongsIDsResponse,
|
||||
} from '@/api/user'
|
||||
import { UserApiNames, fetchUserLikedSongsIDs } from '@/api/user'
|
||||
|
||||
export default function useUserLikedSongsIDs(
|
||||
params: FetchUserLikedSongsIDsParams
|
||||
) {
|
||||
return useQuery(
|
||||
[UserApiNames.FETCH_USER_LIKED_SONGS_IDS, params],
|
||||
() => fetchUserLikedSongsIDs(params),
|
||||
{
|
||||
enabled: !!(params.uid && params.uid !== 0),
|
||||
refetchOnWindowFocus: true,
|
||||
placeholderData: (): FetchUserLikedSongsIDsResponse | undefined =>
|
||||
window.ipcRenderer?.sendSync('getApiCacheSync', {
|
||||
api: 'likelist',
|
||||
query: {
|
||||
uid: params.uid,
|
||||
},
|
||||
}),
|
||||
}
|
||||
)
|
||||
}
|
||||
80
src/renderer/hooks/useUserLikedTracksIDs.ts
Normal file
80
src/renderer/hooks/useUserLikedTracksIDs.ts
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import type { FetchUserLikedTracksIDsResponse } from '@/api/user'
|
||||
import { UserApiNames, fetchUserLikedTracksIDs } from '@/api/user'
|
||||
import { likeATrack } from '@/api/track'
|
||||
import useUser from './useUser'
|
||||
import { useQueryClient } from 'react-query'
|
||||
|
||||
export default function useUserLikedTracksIDs() {
|
||||
const { data: user } = useUser()
|
||||
const uid = user?.account?.id ?? 0
|
||||
|
||||
return useQuery(
|
||||
[UserApiNames.FETCH_USER_LIKED_TRACKS_IDS, uid],
|
||||
() => fetchUserLikedTracksIDs({ uid }),
|
||||
{
|
||||
enabled: !!(uid && uid !== 0),
|
||||
refetchOnWindowFocus: true,
|
||||
placeholderData: (): FetchUserLikedTracksIDsResponse | undefined =>
|
||||
window.ipcRenderer?.sendSync('getApiCacheSync', {
|
||||
api: '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.FETCH_USER_LIKED_TRACKS_IDS, uid]
|
||||
|
||||
return useMutation(
|
||||
(trackID: number) => {
|
||||
if (!trackID || userLikedSongs?.ids === undefined) {
|
||||
throw new Error('trackID is required or userLikedSongs is undefined')
|
||||
}
|
||||
return likeATrack({
|
||||
id: trackID,
|
||||
like: !userLikedSongs.ids.includes(trackID),
|
||||
})
|
||||
},
|
||||
{
|
||||
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]
|
||||
console.log(trackID, ids.includes(trackID), ids, newIds)
|
||||
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)
|
||||
},
|
||||
// Always refetch after error or success:
|
||||
onSettled: () => {
|
||||
queryClient.invalidateQueries(key)
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue