Files
2026-03-01 22:36:50 +08:00

99 lines
2.8 KiB
TypeScript

"use client"
import { Button } from "@/components/ui/button"
import { addFavorite, isFavorited, listFavorites, removeFavorite } from "@/lib/api/favorites"
import { toApiError } from "@/lib/errors"
import { notifyInfo } from "@/lib/toast"
import { useRequireAuth } from "@/lib/use-require-auth"
import { useAuthStore } from "@/store/auth"
import { Heart } from "lucide-react"
import { useEffect, useState } from "react"
interface FavoriteButtonProps {
initialFavorited: boolean
targetType: "player" | "shop"
targetId: string
}
export function FavoriteButton({ initialFavorited, targetType, targetId }: FavoriteButtonProps) {
const { requireAuth } = useRequireAuth()
const userId = useAuthStore((s) => s.user?.id)
const [favorited, setFavorited] = useState(initialFavorited)
const [pending, setPending] = useState(false)
useEffect(() => {
let cancelled = false
if (!userId) {
void Promise.resolve().then(() => {
if (cancelled) return
setFavorited(initialFavorited)
})
return () => {
cancelled = true
}
}
isFavorited(userId, targetType, targetId)
.then((value) => {
if (cancelled) return
setFavorited(value)
})
.catch((err: unknown) => {
if (cancelled) return
if (err instanceof Error && err.message === "UNAUTHORIZED") return
notifyInfo(toApiError(err).msg)
})
return () => {
cancelled = true
}
}, [initialFavorited, targetId, targetType, userId])
return (
<Button
variant={favorited ? "default" : "outline"}
size="sm"
className="gap-1.5"
disabled={pending}
onClick={() =>
requireAuth(() => {
if (pending) return
if (!userId) return
const prevFavorited = favorited
const nextFavorited = !prevFavorited
setFavorited(nextFavorited)
setPending(true)
const action = prevFavorited
? listFavorites().then((favorites) => {
const match = favorites.find(
(f) => f.targetType === targetType && f.targetId === targetId,
)
if (!match) return
return removeFavorite(match.id)
})
: addFavorite({ targetType, targetId }).then(() => {})
action
.catch((err: unknown) => {
setFavorited(prevFavorited)
if (err instanceof Error && err.message === "UNAUTHORIZED") {
notifyInfo("请先登录")
return
}
notifyInfo(toApiError(err).msg)
})
.finally(() => {
setPending(false)
})
})
}
>
<Heart className={`h-4 w-4 ${favorited ? "fill-current" : ""}`} />
{favorited ? "已收藏" : "收藏"}
</Button>
)
}