118 lines
3.4 KiB
TypeScript
118 lines
3.4 KiB
TypeScript
"use client";
|
||
|
||
import { useEffect, useState } from "react";
|
||
import { Bookmark, Heart, Loader2 } from "lucide-react";
|
||
import { Button } from "@/components/ui/button";
|
||
import { useAuth } from "@/contexts/AuthContext";
|
||
import { api } from "@/lib/api";
|
||
import type * as Types from "@/lib/types";
|
||
import { cn, toPersianDigits } from "@/lib/utils";
|
||
|
||
type BlogPostActionsProps = {
|
||
slug: string;
|
||
initialLikes: number;
|
||
initialSaves: number;
|
||
};
|
||
|
||
export default function BlogPostActions({
|
||
slug,
|
||
initialLikes,
|
||
initialSaves,
|
||
}: BlogPostActionsProps) {
|
||
const { isAuthenticated } = useAuth();
|
||
const [loadingAction, setLoadingAction] = useState<"like" | "save" | null>(null);
|
||
const [interaction, setInteraction] = useState<Types.BlogInteractionSchema>({
|
||
liked: false,
|
||
saved: false,
|
||
likes_count: initialLikes,
|
||
saves_count: initialSaves,
|
||
comments_count: 0,
|
||
});
|
||
|
||
useEffect(() => {
|
||
let mounted = true;
|
||
if (!isAuthenticated) return;
|
||
|
||
api.getBlogInteraction(slug)
|
||
.then((data) => {
|
||
if (mounted) setInteraction(data);
|
||
})
|
||
.catch(() => undefined);
|
||
|
||
return () => {
|
||
mounted = false;
|
||
};
|
||
}, [isAuthenticated, slug]);
|
||
|
||
const toggleLike = async () => {
|
||
if (!isAuthenticated || loadingAction) return;
|
||
setLoadingAction("like");
|
||
try {
|
||
setInteraction(await api.toggleLike(slug));
|
||
} finally {
|
||
setLoadingAction(null);
|
||
}
|
||
};
|
||
|
||
const toggleSave = async () => {
|
||
if (!isAuthenticated || loadingAction) return;
|
||
setLoadingAction("save");
|
||
try {
|
||
setInteraction(await api.toggleSave(slug));
|
||
} finally {
|
||
setLoadingAction(null);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className="flex flex-wrap items-center justify-center gap-3 border-t border-border/70 pt-6" dir="rtl">
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="lg"
|
||
onClick={toggleLike}
|
||
disabled={!isAuthenticated || Boolean(loadingAction)}
|
||
className="gap-2 rounded-full border border-border/60 bg-background/80 px-5 shadow-sm backdrop-blur hover:bg-rose-50 hover:text-rose-600 dark:hover:bg-rose-950/30"
|
||
aria-label="پسندیدن نوشته"
|
||
>
|
||
{loadingAction === "like" ? (
|
||
<Loader2 className="h-5 w-5 animate-spin" />
|
||
) : (
|
||
<Heart
|
||
className={cn(
|
||
"h-5 w-5",
|
||
interaction.liked && "fill-rose-500 text-rose-500",
|
||
)}
|
||
/>
|
||
)}
|
||
<span>{toPersianDigits(interaction.likes_count)}</span>
|
||
</Button>
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="icon"
|
||
onClick={toggleSave}
|
||
disabled={!isAuthenticated || Boolean(loadingAction)}
|
||
className="rounded-full border border-border/60 bg-background/80 shadow-sm backdrop-blur hover:bg-amber-50 hover:text-amber-600 dark:hover:bg-amber-950/30"
|
||
aria-label="ذخیره نوشته"
|
||
>
|
||
{loadingAction === "save" ? (
|
||
<Loader2 className="h-5 w-5 animate-spin" />
|
||
) : (
|
||
<Bookmark
|
||
className={cn(
|
||
"h-5 w-5",
|
||
interaction.saved && "fill-amber-500 text-amber-500",
|
||
)}
|
||
/>
|
||
)}
|
||
</Button>
|
||
{!isAuthenticated ? (
|
||
<span className="basis-full text-center text-xs text-muted-foreground">
|
||
برای پسندیدن یا ذخیره کردن وارد حساب کاربری شوید.
|
||
</span>
|
||
) : null}
|
||
</div>
|
||
);
|
||
}
|