76 lines
2.3 KiB
TypeScript
76 lines
2.3 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect } from "react";
|
|
import { useRouter, usePathname } from "next/navigation";
|
|
import { Bookmark } from "lucide-react";
|
|
import { apiToggleFavorite } from "@/lib/api";
|
|
import { toast } from "@/lib/toast";
|
|
|
|
interface FavoriteButtonProps {
|
|
postId: string;
|
|
favorited: boolean;
|
|
variant?: "default" | "compact" | "article";
|
|
}
|
|
|
|
export default function FavoriteButton({
|
|
postId,
|
|
favorited: initialFavorited,
|
|
variant = "default",
|
|
}: FavoriteButtonProps) {
|
|
const router = useRouter();
|
|
const pathname = usePathname();
|
|
const [favorited, setFavorited] = useState(initialFavorited);
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
// router.refresh()(登录/登出后)服务端会重新下发 favorited 态,同步到本地
|
|
useEffect(() => {
|
|
setFavorited(initialFavorited);
|
|
}, [initialFavorited]);
|
|
|
|
const handleClick = async () => {
|
|
if (loading) return;
|
|
setLoading(true);
|
|
const wasFavorited = favorited;
|
|
setFavorited(!wasFavorited);
|
|
try {
|
|
const res = await apiToggleFavorite(postId);
|
|
if (res.error === "未登录") {
|
|
router.push(`/login?redirect=${encodeURIComponent(pathname)}`);
|
|
return;
|
|
}
|
|
if (typeof res.favorited === "boolean") {
|
|
setFavorited(res.favorited);
|
|
} else {
|
|
throw new Error(res.error || "FAIL");
|
|
}
|
|
} catch {
|
|
setFavorited(wasFavorited);
|
|
toast("操作失败,请稍后重试");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const sizeCls =
|
|
variant === "compact"
|
|
? "px-2.5 py-1.5 gap-1 text-[12.5px]"
|
|
: variant === "article"
|
|
? "px-5 py-2.5 gap-1.5 text-[14px]"
|
|
: "px-4 py-2 gap-1.5 text-sm";
|
|
const iconSize = variant === "compact" ? 13 : variant === "article" ? 16 : 15;
|
|
|
|
return (
|
|
<button
|
|
onClick={handleClick}
|
|
disabled={loading}
|
|
data-tip={favorited ? "取消收藏" : "收藏"}
|
|
aria-label={favorited ? "取消收藏" : "收藏"}
|
|
aria-pressed={favorited}
|
|
className={`j13-fav inline-flex items-center justify-center tabular-nums border disabled:opacity-50 ${variant === "article" ? "j13-like-article" : ""} ${favorited ? "is-favorited" : ""} ${sizeCls}`}
|
|
>
|
|
<Bookmark size={iconSize} fill={favorited ? "currentColor" : "none"} />
|
|
{variant === "article" && <span>{favorited ? "已收藏" : "收藏"}</span>}
|
|
</button>
|
|
);
|
|
}
|