navidrome/ui/src/common/useToggleLove.jsx
polybjorn 8407fe6dda
fix(ui): reload the playlist after rating or loving a track (#6009)
A playlistTrack id is a position in the playlist, not a stable key, so refetching
a row by id after the annotation is saved can return a different song: in a smart
playlist filtered on that annotation the track is gone and every later row has
shifted up. The stale-keyed record then renders as a duplicate of its neighbour.

Signed-off-by: Bjørn A. Andersen <polybjorn@users.noreply.github.com>
Co-authored-by: Bjørn A. Andersen <polybjorn@users.noreply.github.com>
Co-authored-by: Deluan Quintão <deluan@navidrome.org>
2026-09-02 20:49:50 -04:00

63 lines
1.9 KiB
JavaScript

import { useCallback, useEffect, useRef, useState } from 'react'
import { useDataProvider, useNotify, useRefresh } from 'react-admin'
import subsonic from '../subsonic'
export const useToggleLove = (resource, record = {}) => {
const [loading, setLoading] = useState(false)
const notify = useNotify()
const mountedRef = useRef(false)
useEffect(() => {
mountedRef.current = true
return () => {
mountedRef.current = false
}
}, [])
const dataProvider = useDataProvider()
const refresh = useRefresh()
const refreshRecord = useCallback(() => {
// A playlistTrack id is a position, not a stable key: loving a song can drop it out of
// a smart playlist, and that position then holds a different track. Refetching the row
// by id would write the neighbour's data under this row, so reload the list instead.
const isPlaylistTrack = !!record.mediaFileId
const target = isPlaylistTrack
? { resource: 'song', params: { id: record.mediaFileId } }
: { resource, params: { id: record.id } }
dataProvider
.getOne(target.resource, target.params)
.catch((e) => {
// eslint-disable-next-line no-console
console.log('Error encountered: ' + e)
})
.finally(() => {
if (isPlaylistTrack) {
refresh()
}
if (mountedRef.current) {
setLoading(false)
}
})
}, [dataProvider, record.mediaFileId, record.id, refresh, resource])
const toggleLove = () => {
const toggle = record.starred ? subsonic.unstar : subsonic.star
const id = record.mediaFileId || record.id
setLoading(true)
toggle(id)
.then(refreshRecord)
.catch((e) => {
// eslint-disable-next-line no-console
console.log('Error toggling love: ', e)
notify('ra.page.error', 'warning')
if (mountedRef.current) {
setLoading(false)
}
})
}
return [toggleLove, loading]
}