feat: optimize sound addition and removal in PlaylistEditPage with optimistic updates
This commit is contained in:
@@ -560,14 +560,32 @@ export function PlaylistEditPage() {
|
||||
await playlistsService.addSoundToPlaylist(playlistId, soundId, position)
|
||||
toast.success('Sound added to playlist')
|
||||
|
||||
// Refresh playlist sounds
|
||||
await fetchSounds()
|
||||
|
||||
// Remove the added sound from available sounds
|
||||
setAvailableSounds(prev => prev.filter(s => s.id !== soundId))
|
||||
|
||||
// Refresh playlist data to update counts
|
||||
await fetchPlaylist()
|
||||
// Find the sound in available sounds to get its data
|
||||
const soundToAdd = availableSounds.find(s => s.id === soundId)
|
||||
if (soundToAdd) {
|
||||
// Optimistically update the state without refetching
|
||||
const newPlaylistSound = {
|
||||
...soundToAdd,
|
||||
// Add any missing properties that PlaylistSound has but Sound doesn't
|
||||
}
|
||||
setSounds(prev => [...prev, newPlaylistSound])
|
||||
|
||||
// Remove from available sounds
|
||||
setAvailableSounds(prev => prev.filter(s => s.id !== soundId))
|
||||
|
||||
// Only update the playlist counter optimistically (avoid full refetch)
|
||||
if (playlist) {
|
||||
setPlaylist(prev => prev ? {
|
||||
...prev,
|
||||
sound_count: (prev.sound_count || 0) + 1,
|
||||
total_duration: (prev.total_duration || 0) + (soundToAdd.duration || 0)
|
||||
} : null)
|
||||
}
|
||||
} else {
|
||||
// Fallback to refresh if we can't find the sound data
|
||||
await fetchSounds()
|
||||
await fetchPlaylist()
|
||||
}
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : 'Failed to add sound to playlist'
|
||||
toast.error(errorMessage)
|
||||
@@ -576,33 +594,53 @@ export function PlaylistEditPage() {
|
||||
|
||||
const handleRemoveSound = async (soundId: number) => {
|
||||
try {
|
||||
// Find the sound being removed to check if it's EXT type
|
||||
// Find the sound being removed
|
||||
const removedSound = sounds.find(s => s.id === soundId)
|
||||
if (!removedSound) return
|
||||
|
||||
await playlistsService.removeSoundFromPlaylist(playlistId, soundId)
|
||||
|
||||
// Optimistically update the sounds list (backend reorders automatically)
|
||||
setSounds(prev => prev.filter(sound => sound.id !== soundId))
|
||||
toast.success('Sound removed from playlist')
|
||||
|
||||
// If it's an EXT sound and we're in add mode, add it back to available sounds
|
||||
if (isAddMode && removedSound && removedSound.type === 'EXT') {
|
||||
// Get the full sound data from the API to add back to available sounds
|
||||
try {
|
||||
const allExtSounds = await soundsService.getSoundsByType('EXT')
|
||||
const soundToAddBack = allExtSounds.find(s => s.id === soundId)
|
||||
if (soundToAddBack) {
|
||||
setAvailableSounds(prev => [...prev, soundToAddBack].sort((a, b) => a.name.localeCompare(b.name)))
|
||||
}
|
||||
} catch {
|
||||
// If we can't fetch the sound data, just refresh the available sounds
|
||||
await fetchAvailableSounds()
|
||||
if (isAddMode && removedSound.type === 'EXT') {
|
||||
// Convert PlaylistSound back to Sound format for available list
|
||||
const soundToAddBack = {
|
||||
id: removedSound.id,
|
||||
name: removedSound.name,
|
||||
filename: removedSound.filename,
|
||||
duration: removedSound.duration,
|
||||
size: removedSound.size,
|
||||
hash: removedSound.hash,
|
||||
type: removedSound.type,
|
||||
play_count: removedSound.play_count,
|
||||
is_normalized: removedSound.is_normalized,
|
||||
normalized_filename: removedSound.normalized_filename,
|
||||
normalized_duration: removedSound.normalized_duration,
|
||||
normalized_size: removedSound.normalized_size,
|
||||
normalized_hash: removedSound.normalized_hash,
|
||||
created_at: removedSound.created_at,
|
||||
updated_at: removedSound.updated_at
|
||||
}
|
||||
setAvailableSounds(prev => [...prev, soundToAddBack].sort((a, b) => a.name.localeCompare(b.name)))
|
||||
}
|
||||
|
||||
// Refresh playlist data to update counts
|
||||
await fetchPlaylist()
|
||||
// Optimistically update playlist stats
|
||||
if (playlist) {
|
||||
setPlaylist(prev => prev ? {
|
||||
...prev,
|
||||
sound_count: Math.max(0, (prev.sound_count || 0) - 1),
|
||||
total_duration: Math.max(0, (prev.total_duration || 0) - (removedSound.duration || 0))
|
||||
} : null)
|
||||
}
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : 'Failed to remove sound'
|
||||
toast.error(errorMessage)
|
||||
// On error, refresh to get correct state
|
||||
await fetchSounds()
|
||||
await fetchPlaylist()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user