feat: implement favorites functionality with SoundCard integration and FavoritesService
This commit is contained in:
@@ -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>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user