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

@@ -4,43 +4,83 @@ import { cn } from '@/lib/utils'
import { formatDuration } from '@/utils/format-duration'
import { formatSize } from '@/utils/format-size'
import NumberFlow from '@number-flow/react'
import { Clock, Play, Weight } from 'lucide-react'
import { Clock, Heart, Play, Weight } from 'lucide-react'
interface SoundCardProps {
sound: Sound
playSound: (sound: Sound) => void
onFavoriteToggle: (soundId: number, isFavorited: boolean) => void
colorClasses: string
}
export function SoundCard({ sound, playSound, colorClasses }: SoundCardProps) {
const handlePlaySound = () => {
export function SoundCard({ sound, playSound, onFavoriteToggle, colorClasses }: SoundCardProps) {
const handlePlaySound = (e: React.MouseEvent) => {
// Don't play sound if clicking on favorite button
if ((e.target as HTMLElement).closest('[data-favorite-button]')) {
return
}
playSound(sound)
}
const handleFavoriteToggle = (e: React.MouseEvent) => {
e.stopPropagation()
onFavoriteToggle(sound.id, !sound.is_favorited)
}
return (
<Card
onClick={handlePlaySound}
className={cn(
'py-2 transition-all duration-100 shadow-sm cursor-pointer active:scale-95',
'py-2 transition-all duration-100 shadow-sm cursor-pointer active:scale-95 relative',
colorClasses,
)}
>
<CardContent className="grid grid-cols-1 pl-3 pr-3 gap-1">
<h3 className="font-medium text-s truncate">{sound.name}</h3>
<div className="grid grid-cols-3 gap-1 text-xs text-muted-foreground">
{/* Favorite button */}
<button
data-favorite-button
onClick={handleFavoriteToggle}
className={cn(
'absolute top-2 right-2 p-1 rounded-full transition-all duration-200 hover:scale-110',
'bg-background/80 hover:bg-background/90 shadow-sm',
sound.is_favorited
? 'text-red-500 hover:text-red-600'
: 'text-muted-foreground hover:text-foreground'
)}
title={sound.is_favorited ? 'Remove from favorites' : 'Add to favorites'}
>
<Heart
className={cn(
'h-3.5 w-3.5 transition-all duration-200',
sound.is_favorited && 'fill-current'
)}
/>
</button>
<h3 className="font-medium text-s truncate pr-8">{sound.name}</h3>
<div className="grid grid-cols-2 gap-1 text-xs text-muted-foreground">
<div className="flex">
<Clock className="h-3.5 w-3.5 mr-0.5" />
<span>{formatDuration(sound.duration)}</span>
</div>
<div className="flex justify-center">
<Weight className="h-3.5 w-3.5 mr-0.5" />
<span>{formatSize(sound.size)}</span>
</div>
<div className="flex justify-end items-center">
<Play className="h-3.5 w-3.5 mr-0.5" />
<NumberFlow value={sound.play_count} />
</div>
</div>
{/* Show favorite count if > 0 */}
<div className="grid grid-cols-2 gap-1 text-xs text-muted-foreground">
<div className="flex">
<Weight className="h-3.5 w-3.5 mr-0.5" />
<span>{formatSize(sound.size)}</span>
</div>
<div className="flex justify-end items-center text-xs text-muted-foreground">
<Heart className="h-3.5 w-3.5 mr-0.5" />
<NumberFlow value={sound.favorite_count} />
</div>
</div>
</CardContent>
</Card>
)