132 lines
3.4 KiB
TypeScript
132 lines
3.4 KiB
TypeScript
"use client"
|
|
|
|
import type React from "react"
|
|
|
|
import { useState, useEffect } from "react"
|
|
import { ThumbsUp } from "lucide-react"
|
|
import { Button } from "@/components/ui/button"
|
|
import { useAuth } from "@/contexts/auth-context"
|
|
import { useToast } from "@/hooks/use-toast"
|
|
import { cn } from "@/lib/utils"
|
|
|
|
interface LikeButtonProps {
|
|
appId: string
|
|
size?: "sm" | "default" | "lg"
|
|
className?: string
|
|
showCount?: boolean
|
|
likeCount?: number
|
|
userLiked?: boolean
|
|
}
|
|
|
|
export function LikeButton({ appId, size = "default", className, showCount = true, likeCount: propLikeCount, userLiked: propUserLiked }: LikeButtonProps) {
|
|
const { user, toggleLike, getAppLikes, isLiked } = useAuth()
|
|
const { toast } = useToast()
|
|
const [isLoading, setIsLoading] = useState(false)
|
|
const [localLikeCount, setLocalLikeCount] = useState(propLikeCount || 0)
|
|
const [localUserLiked, setLocalUserLiked] = useState(propUserLiked || false)
|
|
|
|
const likeCount = localLikeCount
|
|
const hasLiked = user ? isLiked(appId) : localUserLiked
|
|
|
|
// 載入用戶的按讚狀態
|
|
useEffect(() => {
|
|
if (user) {
|
|
const liked = isLiked(appId)
|
|
setLocalUserLiked(liked)
|
|
}
|
|
}, [user, appId, isLiked])
|
|
|
|
// 同步外部 props 變化
|
|
useEffect(() => {
|
|
if (propLikeCount !== undefined) {
|
|
setLocalLikeCount(propLikeCount)
|
|
}
|
|
}, [propLikeCount])
|
|
|
|
useEffect(() => {
|
|
if (propUserLiked !== undefined) {
|
|
setLocalUserLiked(propUserLiked)
|
|
}
|
|
}, [propUserLiked])
|
|
|
|
const handleLike = async (e: React.MouseEvent) => {
|
|
e.stopPropagation()
|
|
|
|
if (!user) {
|
|
toast({
|
|
title: "請先登入",
|
|
description: "您需要登入才能為應用按讚",
|
|
variant: "destructive",
|
|
})
|
|
return
|
|
}
|
|
|
|
setIsLoading(true)
|
|
|
|
try {
|
|
const newLikedState = await toggleLike(appId)
|
|
|
|
// 更新本地狀態(基於 API 返回的結果)
|
|
setLocalUserLiked(newLikedState)
|
|
|
|
if (newLikedState) {
|
|
// 剛剛按讚了
|
|
setLocalLikeCount(prev => prev + 1)
|
|
toast({
|
|
title: "按讚成功!",
|
|
description: "感謝您的支持",
|
|
})
|
|
} else {
|
|
// 剛剛取消按讚了
|
|
setLocalLikeCount(prev => Math.max(prev - 1, 0))
|
|
toast({
|
|
title: "取消按讚",
|
|
description: "已取消對該應用的按讚",
|
|
})
|
|
}
|
|
} catch (error) {
|
|
console.error("按讚操作失敗:", error)
|
|
toast({
|
|
title: "操作失敗",
|
|
description: "請稍後再試",
|
|
variant: "destructive",
|
|
})
|
|
} finally {
|
|
setIsLoading(false)
|
|
}
|
|
}
|
|
|
|
const sizeClasses = {
|
|
sm: "h-6 px-2 text-xs gap-1",
|
|
default: "h-8 px-3 text-sm gap-1.5",
|
|
lg: "h-10 px-4 text-base gap-2",
|
|
}
|
|
|
|
const iconSizes = {
|
|
sm: "w-3 h-3",
|
|
default: "w-4 h-4",
|
|
lg: "w-5 h-5",
|
|
}
|
|
|
|
return (
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={handleLike}
|
|
disabled={isLoading}
|
|
className={cn(
|
|
sizeClasses[size],
|
|
"flex items-center",
|
|
hasLiked
|
|
? "text-blue-600 bg-blue-50 border-blue-200 hover:text-blue-700 hover:bg-blue-100"
|
|
: "text-gray-500 hover:text-blue-600 hover:bg-blue-50",
|
|
"transition-all duration-200",
|
|
className,
|
|
)}
|
|
>
|
|
<ThumbsUp className={cn(iconSizes[size], hasLiked ? "fill-current" : "")} />
|
|
{showCount && <span>{likeCount}</span>}
|
|
</Button>
|
|
)
|
|
}
|