From 8a9f51a0c6709697b5d8ce4ca776f9e7b5eac938 Mon Sep 17 00:00:00 2001 From: Geert Rademakes Date: Fri, 8 Aug 2025 13:17:15 +0200 Subject: [PATCH] fix(dnd): align drop flow with modal add; use same backend save and show success toast; improve payload parsing for drop targets --- packages/frontend/src/App.tsx | 21 ++++++++++++------- .../src/components/PlaylistManager.tsx | 8 +++---- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/packages/frontend/src/App.tsx b/packages/frontend/src/App.tsx index f41c420..194b27c 100644 --- a/packages/frontend/src/App.tsx +++ b/packages/frontend/src/App.tsx @@ -1,4 +1,4 @@ -import { Box, Button, Flex, Heading, Spinner, Text, useBreakpointValue, IconButton, Drawer, DrawerBody, DrawerHeader, DrawerOverlay, DrawerContent, useDisclosure, Modal, ModalOverlay, ModalContent, ModalHeader, ModalBody, ModalFooter, VStack } from "@chakra-ui/react"; +import { Box, Button, Flex, Heading, Spinner, Text, useBreakpointValue, IconButton, Drawer, DrawerBody, DrawerHeader, DrawerOverlay, DrawerContent, useDisclosure, Modal, ModalOverlay, ModalContent, ModalHeader, ModalBody, ModalFooter, VStack, useToast } from "@chakra-ui/react"; import { ChevronLeftIcon, ChevronRightIcon, SettingsIcon, DownloadIcon } from "@chakra-ui/icons"; import React, { useState, useRef, useEffect, useCallback } from "react"; import { useNavigate, useLocation, Routes, Route } from "react-router-dom"; @@ -76,6 +76,7 @@ const RekordboxReader: React.FC = () => { const [isDatabaseInitialized, setIsDatabaseInitialized] = useState(false); const [isSwitchingPlaylist, setIsSwitchingPlaylist] = useState(false); const { currentSong, playSong, closePlayer } = useMusicPlayer(); + const toast = useToast(); // Memoized song selection handler to prevent unnecessary re-renders const handleSongSelect = useCallback((song: Song) => { @@ -254,8 +255,10 @@ const RekordboxReader: React.FC = () => { } return node; }); - const savedPlaylists = await api.savePlaylists(updatedPlaylists); - setPlaylists(savedPlaylists); + await api.savePlaylists(updatedPlaylists); + // Always normalize state to structure for consistent counters + const structure = await api.getPlaylistStructure(); + setPlaylists(structure); }; // Handle drop from song list into playlist (with duplicate check and user choice) @@ -286,11 +289,13 @@ const RekordboxReader: React.FC = () => { const finalIds = proceedMode === 'skip' ? songIds.filter(id => !existing.has(id)) : songIds; if (finalIds.length === 0) return; await handleAddSongsToPlaylist(finalIds, playlistName); - // If we were on that playlist, refresh counters by reloading structure - try { - const updated = await api.getPlaylistStructure(); - setPlaylists(updated); - } catch {} + toast({ + title: 'Songs Added', + description: `${finalIds.length} song${finalIds.length === 1 ? '' : 's'} added to "${playlistName}"`, + status: 'success', + duration: 3000, + isClosable: true, + }); }; const handleRemoveFromPlaylist = async (songIds: string[]) => { diff --git a/packages/frontend/src/components/PlaylistManager.tsx b/packages/frontend/src/components/PlaylistManager.tsx index a295e01..4fb9378 100644 --- a/packages/frontend/src/components/PlaylistManager.tsx +++ b/packages/frontend/src/components/PlaylistManager.tsx @@ -153,8 +153,8 @@ const PlaylistItem: React.FC = React.memo(({ onDragLeave={() => setIsDragOver(false)} onDrop={(e) => { try { - const data = e.dataTransfer.getData('application/json'); - const parsed = JSON.parse(data); + const json = e.dataTransfer.getData('application/json') || e.dataTransfer.getData('text/plain'); + const parsed = JSON.parse(json); if (parsed?.type === 'songs' && Array.isArray(parsed.songIds) && onDropSongs) { onDropSongs(node.name, parsed.songIds); } @@ -233,8 +233,8 @@ const PlaylistItem: React.FC = React.memo(({ onDragLeave={() => setIsDragOver(false)} onDrop={(e) => { try { - const data = e.dataTransfer.getData('application/json'); - const parsed = JSON.parse(data); + const json = e.dataTransfer.getData('application/json') || e.dataTransfer.getData('text/plain'); + const parsed = JSON.parse(json); if (parsed?.type === 'songs' && Array.isArray(parsed.songIds) && onDropSongs) { onDropSongs(node.name, parsed.songIds); }