Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
1238ab3b3e
commit
f5fbaae66f
@ -7,14 +7,14 @@ import {
|
|||||||
add, heart, heartOutline, ban, checkmark, close, people
|
add, heart, heartOutline, ban, checkmark, close, people
|
||||||
} from 'ionicons/icons';
|
} from 'ionicons/icons';
|
||||||
import { useAppSelector } from '../../redux';
|
import { useAppSelector } from '../../redux';
|
||||||
import { selectIsAdmin, selectFavorites, selectSongs } from '../../redux';
|
import { selectIsAdmin, selectFavorites, selectSongs, selectQueue } from '../../redux';
|
||||||
import { useSongOperations } from '../../hooks/useSongOperations';
|
import { useSongOperations } from '../../hooks/useSongOperations';
|
||||||
import { useDisabledSongs } from '../../hooks/useDisabledSongs';
|
import { useDisabledSongs } from '../../hooks/useDisabledSongs';
|
||||||
import { useSelectSinger } from '../../hooks/useSelectSinger';
|
import { useSelectSinger } from '../../hooks/useSelectSinger';
|
||||||
import { useToast } from '../../hooks/useToast';
|
import { useToast } from '../../hooks/useToast';
|
||||||
import SelectSinger from './SelectSinger';
|
import SelectSinger from './SelectSinger';
|
||||||
import { SongInfoDisplay } from './SongItem';
|
import { SongInfoDisplay } from './SongItem';
|
||||||
import type { Song } from '../../types';
|
import type { Song, QueueItem } from '../../types';
|
||||||
|
|
||||||
interface SongInfoProps {
|
interface SongInfoProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@ -26,6 +26,7 @@ const SongInfo: React.FC<SongInfoProps> = ({ isOpen, onClose, song }) => {
|
|||||||
const isAdmin = useAppSelector(selectIsAdmin);
|
const isAdmin = useAppSelector(selectIsAdmin);
|
||||||
const favorites = useAppSelector(selectFavorites);
|
const favorites = useAppSelector(selectFavorites);
|
||||||
const allSongs = useAppSelector(selectSongs);
|
const allSongs = useAppSelector(selectSongs);
|
||||||
|
const queue = useAppSelector(selectQueue);
|
||||||
const { toggleFavorite } = useSongOperations();
|
const { toggleFavorite } = useSongOperations();
|
||||||
const { isSongDisabled, addDisabledSong, removeDisabledSong } = useDisabledSongs();
|
const { isSongDisabled, addDisabledSong, removeDisabledSong } = useDisabledSongs();
|
||||||
const { showSuccess, showError } = useToast();
|
const { showSuccess, showError } = useToast();
|
||||||
@ -40,6 +41,7 @@ const SongInfo: React.FC<SongInfoProps> = ({ isOpen, onClose, song }) => {
|
|||||||
|
|
||||||
const isInFavorites = (Object.values(favorites) as Song[]).some(favSong => favSong.path === song.path);
|
const isInFavorites = (Object.values(favorites) as Song[]).some(favSong => favSong.path === song.path);
|
||||||
const isDisabled = isSongDisabled(song);
|
const isDisabled = isSongDisabled(song);
|
||||||
|
const isInQueue = (Object.values(queue) as QueueItem[]).some(queueItem => queueItem.song && queueItem.song.path === song.path);
|
||||||
|
|
||||||
const artistSongs = (Object.values(allSongs) as Song[]).filter(s =>
|
const artistSongs = (Object.values(allSongs) as Song[]).filter(s =>
|
||||||
s.artist.toLowerCase() === song.artist.toLowerCase() && s.path !== song.path
|
s.artist.toLowerCase() === song.artist.toLowerCase() && s.path !== song.path
|
||||||
@ -110,16 +112,18 @@ const SongInfo: React.FC<SongInfoProps> = ({ isOpen, onClose, song }) => {
|
|||||||
{/* Action Buttons */}
|
{/* Action Buttons */}
|
||||||
<div className="flex flex-col items-center space-y-4">
|
<div className="flex flex-col items-center space-y-4">
|
||||||
{/* Queue Song Button */}
|
{/* Queue Song Button */}
|
||||||
<IonButton
|
{!isInQueue && (
|
||||||
fill="solid"
|
<IonButton
|
||||||
color="primary"
|
fill="solid"
|
||||||
onClick={handleQueueSong}
|
color="primary"
|
||||||
className="h-12 w-80"
|
onClick={handleQueueSong}
|
||||||
style={{ width: '320px' }}
|
className="h-12 w-80"
|
||||||
>
|
style={{ width: '320px' }}
|
||||||
<IonIcon icon={people} slot="start" />
|
>
|
||||||
Queue Song
|
<IonIcon icon={people} slot="start" />
|
||||||
</IonButton>
|
Queue Song
|
||||||
|
</IonButton>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Artist Songs Button */}
|
{/* Artist Songs Button */}
|
||||||
<IonButton
|
<IonButton
|
||||||
|
|||||||
@ -217,6 +217,15 @@ const SongItem: React.FC<SongItemProps> = ({
|
|||||||
const isInQueue = (Object.values(queue) as QueueItem[]).some(item => item.song.path === song.path);
|
const isInQueue = (Object.values(queue) as QueueItem[]).some(item => item.song.path === song.path);
|
||||||
const isInFavorites = (Object.values(favorites) as Song[]).some(favSong => favSong.path === song.path);
|
const isInFavorites = (Object.values(favorites) as Song[]).some(favSong => favSong.path === song.path);
|
||||||
|
|
||||||
|
// Debug logging for favorites
|
||||||
|
console.log('SongItem render:', {
|
||||||
|
songTitle: song.title,
|
||||||
|
songPath: song.path,
|
||||||
|
favoritesCount: Object.keys(favorites).length,
|
||||||
|
isInFavorites,
|
||||||
|
favorites: (Object.values(favorites) as Song[]).map(f => f.path)
|
||||||
|
});
|
||||||
|
|
||||||
// Default values based on context if not explicitly provided
|
// Default values based on context if not explicitly provided
|
||||||
const shouldShowPath = showPath !== undefined ? showPath : context !== 'queue';
|
const shouldShowPath = showPath !== undefined ? showPath : context !== 'queue';
|
||||||
const shouldShowCount = showCount !== undefined ? showCount : context === 'queue';
|
const shouldShowCount = showCount !== undefined ? showCount : context === 'queue';
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { InfiniteScrollList, SongItem } from '../../components/common';
|
import { InfiniteScrollList, SongItem, SongInfo } from '../../components/common';
|
||||||
import { useFavorites } from '../../hooks';
|
import { useFavorites } from '../../hooks';
|
||||||
import { useAppSelector } from '../../redux';
|
import { useAppSelector } from '../../redux';
|
||||||
import { selectFavorites } from '../../redux';
|
import { selectFavorites } from '../../redux';
|
||||||
@ -11,10 +11,12 @@ const Favorites: React.FC = () => {
|
|||||||
favoritesItems,
|
favoritesItems,
|
||||||
hasMore,
|
hasMore,
|
||||||
loadMore,
|
loadMore,
|
||||||
handleAddToQueue,
|
|
||||||
handleToggleFavorite,
|
handleToggleFavorite,
|
||||||
} = useFavorites();
|
} = useFavorites();
|
||||||
|
|
||||||
|
const [selectedSong, setSelectedSong] = useState<Song | null>(null);
|
||||||
|
const [isSongInfoOpen, setIsSongInfoOpen] = useState(false);
|
||||||
|
|
||||||
const favorites = useAppSelector(selectFavorites);
|
const favorites = useAppSelector(selectFavorites);
|
||||||
const favoritesCount = Object.keys(favorites).length;
|
const favoritesCount = Object.keys(favorites).length;
|
||||||
|
|
||||||
@ -22,6 +24,16 @@ const Favorites: React.FC = () => {
|
|||||||
debugLog('Favorites component - favorites count:', favoritesCount);
|
debugLog('Favorites component - favorites count:', favoritesCount);
|
||||||
debugLog('Favorites component - favorites items:', favoritesItems);
|
debugLog('Favorites component - favorites items:', favoritesItems);
|
||||||
|
|
||||||
|
const handleSongInfo = (song: Song) => {
|
||||||
|
setSelectedSong(song);
|
||||||
|
setIsSongInfoOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloseSongInfo = () => {
|
||||||
|
setIsSongInfoOpen(false);
|
||||||
|
setSelectedSong(null);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<InfiniteScrollList<Song>
|
<InfiniteScrollList<Song>
|
||||||
@ -33,8 +45,10 @@ const Favorites: React.FC = () => {
|
|||||||
<SongItem
|
<SongItem
|
||||||
song={song}
|
song={song}
|
||||||
context="favorites"
|
context="favorites"
|
||||||
onAddToQueue={() => handleAddToQueue(song)}
|
showInfoButton={true}
|
||||||
onToggleFavorite={() => handleToggleFavorite(song)}
|
showAddButton={false}
|
||||||
|
onSelectSinger={() => handleSongInfo(song)}
|
||||||
|
onDelete={() => handleToggleFavorite(song)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
emptyTitle="No favorites yet"
|
emptyTitle="No favorites yet"
|
||||||
@ -42,6 +56,15 @@ const Favorites: React.FC = () => {
|
|||||||
loadingTitle="Loading favorites..."
|
loadingTitle="Loading favorites..."
|
||||||
loadingMessage="Please wait while favorites data is being loaded"
|
loadingMessage="Please wait while favorites data is being loaded"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Song Info Modal */}
|
||||||
|
{selectedSong && (
|
||||||
|
<SongInfo
|
||||||
|
isOpen={isSongInfoOpen}
|
||||||
|
onClose={handleCloseSongInfo}
|
||||||
|
song={selectedSong}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { useState, useEffect, useCallback } from 'react';
|
|||||||
import { disabledSongsService } from '../firebase/services';
|
import { disabledSongsService } from '../firebase/services';
|
||||||
import { useAppSelector } from '../redux';
|
import { useAppSelector } from '../redux';
|
||||||
import { selectControllerName } from '../redux';
|
import { selectControllerName } from '../redux';
|
||||||
import { debugLog } from '../utils/logger';
|
|
||||||
import { useToast } from './useToast';
|
import { useToast } from './useToast';
|
||||||
import type { Song, DisabledSong } from '../types';
|
import type { Song, DisabledSong } from '../types';
|
||||||
|
|
||||||
@ -39,8 +38,21 @@ export const useDisabledSongs = () => {
|
|||||||
const unsubscribe = disabledSongsService.subscribeToDisabledSongs(
|
const unsubscribe = disabledSongsService.subscribeToDisabledSongs(
|
||||||
controllerName,
|
controllerName,
|
||||||
(songs) => {
|
(songs) => {
|
||||||
setDisabledSongs(songs);
|
// Only update if the data has actually changed
|
||||||
setDisabledSongPaths(new Set(Object.keys(songs).map(key => decodeURIComponent(key))));
|
setDisabledSongs(prevSongs => {
|
||||||
|
if (JSON.stringify(prevSongs) !== JSON.stringify(songs)) {
|
||||||
|
return songs;
|
||||||
|
}
|
||||||
|
return prevSongs;
|
||||||
|
});
|
||||||
|
|
||||||
|
setDisabledSongPaths(prevPaths => {
|
||||||
|
const newPaths = new Set(Object.values(songs).map((song: DisabledSong) => song.path));
|
||||||
|
if (JSON.stringify(Array.from(prevPaths)) !== JSON.stringify(Array.from(newPaths))) {
|
||||||
|
return newPaths;
|
||||||
|
}
|
||||||
|
return prevPaths;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -49,7 +61,8 @@ export const useDisabledSongs = () => {
|
|||||||
|
|
||||||
// Check if a song is disabled
|
// Check if a song is disabled
|
||||||
const isSongDisabled = useCallback((song: Song): boolean => {
|
const isSongDisabled = useCallback((song: Song): boolean => {
|
||||||
return disabledSongPaths.has(song.path);
|
const isDisabled = disabledSongPaths.has(song.path);
|
||||||
|
return isDisabled;
|
||||||
}, [disabledSongPaths]);
|
}, [disabledSongPaths]);
|
||||||
|
|
||||||
// Add a song to disabled list
|
// Add a song to disabled list
|
||||||
@ -67,7 +80,6 @@ export const useDisabledSongs = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
debugLog('Adding disabled song:', { controllerName, song });
|
|
||||||
await disabledSongsService.addDisabledSong(controllerName, song);
|
await disabledSongsService.addDisabledSong(controllerName, song);
|
||||||
showSuccess('Song marked as disabled');
|
showSuccess('Song marked as disabled');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -3,6 +3,8 @@ import { useAppSelector } from '../redux';
|
|||||||
import { selectControllerName, selectCurrentSinger, selectQueueObject } from '../redux';
|
import { selectControllerName, selectCurrentSinger, selectQueueObject } from '../redux';
|
||||||
import { queueService, favoritesService } from '../firebase/services';
|
import { queueService, favoritesService } from '../firebase/services';
|
||||||
import type { Song, QueueItem } from '../types';
|
import type { Song, QueueItem } from '../types';
|
||||||
|
import { ref, get } from 'firebase/database';
|
||||||
|
import { database } from '../firebase/config';
|
||||||
|
|
||||||
export const useSongOperations = () => {
|
export const useSongOperations = () => {
|
||||||
const controllerName = useAppSelector(selectControllerName);
|
const controllerName = useAppSelector(selectControllerName);
|
||||||
@ -57,15 +59,34 @@ export const useSongOperations = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (song.favorite) {
|
console.log('toggleFavorite called for song:', song.title, song.path);
|
||||||
|
|
||||||
|
// Check if the song is currently in favorites by looking it up
|
||||||
|
const favoritesRef = ref(database, `controllers/${controllerName}/favorites`);
|
||||||
|
const snapshot = await get(favoritesRef);
|
||||||
|
const favorites = snapshot.exists() ? snapshot.val() : {};
|
||||||
|
|
||||||
|
console.log('Current favorites:', favorites);
|
||||||
|
|
||||||
|
// Find if this song is already in favorites by matching the path
|
||||||
|
const existingFavoriteKey = Object.keys(favorites).find(key => {
|
||||||
|
const favoriteSong = favorites[key];
|
||||||
|
return favoriteSong && favoriteSong.path === song.path;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Existing favorite key:', existingFavoriteKey);
|
||||||
|
|
||||||
|
if (existingFavoriteKey) {
|
||||||
// Remove from favorites
|
// Remove from favorites
|
||||||
if (song.key) {
|
console.log('Removing from favorites');
|
||||||
await favoritesService.removeFromFavorites(controllerName, song.key);
|
await favoritesService.removeFromFavorites(controllerName, existingFavoriteKey);
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Add to favorites
|
// Add to favorites
|
||||||
|
console.log('Adding to favorites');
|
||||||
await favoritesService.addToFavorites(controllerName, song);
|
await favoritesService.addToFavorites(controllerName, song);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('toggleFavorite completed');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to toggle favorite:', error);
|
console.error('Failed to toggle favorite:', error);
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user