feat: implement favorites functionality with SoundCard integration and FavoritesService

This commit is contained in:
JSC
2025-08-16 21:16:13 +02:00
parent ecb17e9f94
commit 2e41d5b695
5 changed files with 228 additions and 16 deletions

View File

@@ -17,9 +17,11 @@ import {
type SoundSortField,
soundsService,
} from '@/lib/api/services/sounds'
import { favoritesService } from '@/lib/api/services/favorites'
import { SOUND_EVENTS, soundEvents } from '@/lib/events'
import {
AlertCircle,
Heart,
RefreshCw,
Search,
SortAsc,
@@ -77,6 +79,7 @@ export function SoundsPage() {
const [searchQuery, setSearchQuery] = useState('')
const [sortBy, setSortBy] = useState<SoundSortField>('name')
const [sortOrder, setSortOrder] = useState<SortOrder>('asc')
const [showFavoritesOnly, setShowFavoritesOnly] = useState(false)
const handlePlaySound = async (sound: Sound) => {
try {
@@ -89,6 +92,39 @@ export function SoundsPage() {
}
}
const handleFavoriteToggle = async (soundId: number, shouldFavorite: boolean) => {
try {
if (shouldFavorite) {
await favoritesService.addSoundFavorite(soundId)
toast.success('Added to favorites')
} else {
await favoritesService.removeSoundFavorite(soundId)
toast.success('Removed from favorites')
}
// Update the sound in the local state
setSounds(prevSounds =>
prevSounds.map(sound =>
sound.id === soundId
? {
...sound,
is_favorited: shouldFavorite,
favorite_count: shouldFavorite
? sound.favorite_count + 1
: Math.max(0, sound.favorite_count - 1),
}
: sound,
),
)
} catch (error) {
toast.error(
`Failed to ${shouldFavorite ? 'add to' : 'remove from'} favorites: ${
error instanceof Error ? error.message : 'Unknown error'
}`,
)
}
}
const { theme } = useTheme()
useEffect(() => {
@@ -188,15 +224,23 @@ export function SoundsPage() {
)
}
if (sounds.length === 0) {
// Filter sounds based on favorites filter
const filteredSounds = showFavoritesOnly ? sounds.filter(sound => sound.is_favorited) : sounds
if (filteredSounds.length === 0) {
return (
<div className="flex flex-col items-center justify-center py-12 text-center">
<div className="h-12 w-12 rounded-full bg-muted flex items-center justify-center mb-4">
<span className="text-2xl">🎵</span>
<span className="text-2xl">{showFavoritesOnly ? '💝' : '🎵'}</span>
</div>
<h3 className="text-lg font-semibold mb-2">No sounds found</h3>
<h3 className="text-lg font-semibold mb-2">
{showFavoritesOnly ? 'No favorite sounds found' : 'No sounds found'}
</h3>
<p className="text-muted-foreground">
No SDB type sounds are available in your library.
{showFavoritesOnly
? 'You haven\'t favorited any sounds yet. Click the heart icon on sounds to add them to your favorites.'
: 'No SDB type sounds are available in your library.'
}
</p>
</div>
)
@@ -204,11 +248,12 @@ export function SoundsPage() {
return (
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4">
{sounds.map((sound, idx) => (
{filteredSounds.map((sound, idx) => (
<SoundCard
key={sound.id}
sound={sound}
playSound={handlePlaySound}
onFavoriteToggle={handleFavoriteToggle}
colorClasses={getSoundColor(idx)}
/>
))}
@@ -232,7 +277,10 @@ export function SoundsPage() {
</div>
{!loading && !error && (
<div className="text-sm text-muted-foreground">
{sounds.length} sound{sounds.length !== 1 ? 's' : ''}
{showFavoritesOnly
? `${sounds.filter(s => s.is_favorited).length} favorite sound${sounds.filter(s => s.is_favorited).length !== 1 ? 's' : ''}`
: `${sounds.length} sound${sounds.length !== 1 ? 's' : ''}`
}
</div>
)}
</div>
@@ -293,6 +341,15 @@ export function SoundsPage() {
)}
</Button>
<Button
variant={showFavoritesOnly ? "default" : "outline"}
size="icon"
onClick={() => setShowFavoritesOnly(!showFavoritesOnly)}
title={showFavoritesOnly ? "Show all sounds" : "Show only favorites"}
>
<Heart className={`h-4 w-4 ${showFavoritesOnly ? 'fill-current' : ''}`} />
</Button>
<Button
variant="outline"
size="icon"