feat: Add immediate visual feedback for playlist switching - Clear song list immediately when switching playlists for instant feedback - Add 'Switching playlist...' loading indicator with spinner - Set switching state immediately on playlist selection - Reset switching state when new songs are loaded - Makes playlist switching feel much more responsive and clear
This commit is contained in:
parent
4c3b3e31d4
commit
491235af29
@ -71,6 +71,7 @@ export default function RekordboxReader() {
|
|||||||
const { playlists, setPlaylists, loading: xmlLoading } = useXmlParser();
|
const { playlists, setPlaylists, loading: xmlLoading } = useXmlParser();
|
||||||
const [selectedSong, setSelectedSong] = useState<Song | null>(null);
|
const [selectedSong, setSelectedSong] = useState<Song | null>(null);
|
||||||
const [isDatabaseInitialized, setIsDatabaseInitialized] = useState(false);
|
const [isDatabaseInitialized, setIsDatabaseInitialized] = useState(false);
|
||||||
|
const [isSwitchingPlaylist, setIsSwitchingPlaylist] = useState(false);
|
||||||
|
|
||||||
// Memoized song selection handler to prevent unnecessary re-renders
|
// Memoized song selection handler to prevent unnecessary re-renders
|
||||||
const handleSongSelect = useCallback((song: Song) => {
|
const handleSongSelect = useCallback((song: Song) => {
|
||||||
@ -153,7 +154,17 @@ export default function RekordboxReader() {
|
|||||||
}
|
}
|
||||||
}, [currentPlaylist, playlists, navigate, xmlLoading]);
|
}, [currentPlaylist, playlists, navigate, xmlLoading]);
|
||||||
|
|
||||||
|
// Reset switching state when songs are loaded
|
||||||
|
useEffect(() => {
|
||||||
|
if (songs.length > 0 && isSwitchingPlaylist) {
|
||||||
|
setIsSwitchingPlaylist(false);
|
||||||
|
}
|
||||||
|
}, [songs.length, isSwitchingPlaylist]);
|
||||||
|
|
||||||
const handlePlaylistSelect = (name: string) => {
|
const handlePlaylistSelect = (name: string) => {
|
||||||
|
// Set switching state immediately for visual feedback
|
||||||
|
setIsSwitchingPlaylist(true);
|
||||||
|
|
||||||
// Clear selected song immediately to prevent stale state
|
// Clear selected song immediately to prevent stale state
|
||||||
setSelectedSong(null);
|
setSelectedSong(null);
|
||||||
|
|
||||||
@ -549,6 +560,7 @@ export default function RekordboxReader() {
|
|||||||
onLoadMore={loadNextPage}
|
onLoadMore={loadNextPage}
|
||||||
onSearch={searchSongs}
|
onSearch={searchSongs}
|
||||||
searchQuery={searchQuery}
|
searchQuery={searchQuery}
|
||||||
|
isSwitchingPlaylist={isSwitchingPlaylist}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|||||||
@ -39,6 +39,7 @@ interface PaginatedSongListProps {
|
|||||||
onSearch: (query: string) => void;
|
onSearch: (query: string) => void;
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
depth?: number;
|
depth?: number;
|
||||||
|
isSwitchingPlaylist?: boolean; // New prop to indicate playlist switching
|
||||||
}
|
}
|
||||||
|
|
||||||
// Memoized song item component to prevent unnecessary re-renders
|
// Memoized song item component to prevent unnecessary re-renders
|
||||||
@ -118,7 +119,8 @@ export const PaginatedSongList: React.FC<PaginatedSongListProps> = memo(({
|
|||||||
onLoadMore,
|
onLoadMore,
|
||||||
onSearch,
|
onSearch,
|
||||||
searchQuery,
|
searchQuery,
|
||||||
depth = 0
|
depth = 0,
|
||||||
|
isSwitchingPlaylist = false
|
||||||
}) => {
|
}) => {
|
||||||
const [selectedSongs, setSelectedSongs] = useState<Set<string>>(new Set());
|
const [selectedSongs, setSelectedSongs] = useState<Set<string>>(new Set());
|
||||||
const [localSearchQuery, setLocalSearchQuery] = useState(searchQuery);
|
const [localSearchQuery, setLocalSearchQuery] = useState(searchQuery);
|
||||||
@ -427,12 +429,21 @@ export const PaginatedSongList: React.FC<PaginatedSongListProps> = memo(({
|
|||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* No results message */}
|
{/* No results message or playlist switching indicator */}
|
||||||
{!loading && songs.length === 0 && (
|
{!loading && songs.length === 0 && (
|
||||||
<Flex justify="center" p={8} key="no-results">
|
<Flex justify="center" p={8} key="no-results" direction="column" align="center" gap={3}>
|
||||||
<Text color="gray.500">
|
{isSwitchingPlaylist ? (
|
||||||
{searchQuery ? 'No songs found matching your search' : 'No songs available'}
|
<>
|
||||||
</Text>
|
<Spinner size="md" color="blue.400" />
|
||||||
|
<Text color="blue.400" fontSize="sm" fontWeight="medium">
|
||||||
|
Switching playlist...
|
||||||
|
</Text>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Text color="gray.500">
|
||||||
|
{searchQuery ? 'No songs found matching your search' : 'No songs available'}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@ -146,9 +146,11 @@ export const usePaginatedSongs = (options: UsePaginatedSongsOptions = {}) => {
|
|||||||
currentSearchQueryRef.current = searchQuery;
|
currentSearchQueryRef.current = searchQuery;
|
||||||
previousPlaylistRef.current = playlistName;
|
previousPlaylistRef.current = playlistName;
|
||||||
|
|
||||||
// Batch all state updates together to reduce re-renders
|
// Clear songs IMMEDIATELY for instant visual feedback
|
||||||
|
setSongs([]);
|
||||||
|
|
||||||
|
// Batch remaining state updates together to reduce re-renders
|
||||||
React.startTransition(() => {
|
React.startTransition(() => {
|
||||||
setSongs([]);
|
|
||||||
setHasMore(true);
|
setHasMore(true);
|
||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
setSearchQuery(initialSearch);
|
setSearchQuery(initialSearch);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user