feat(blog): redesign post detail experience
This commit is contained in:
120
src/components/BlogPostActions.tsx
Normal file
120
src/components/BlogPostActions.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
"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;
|
||||
initialComments: number;
|
||||
};
|
||||
|
||||
export default function BlogPostActions({
|
||||
slug,
|
||||
initialLikes,
|
||||
initialSaves,
|
||||
initialComments,
|
||||
}: 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: initialComments,
|
||||
});
|
||||
|
||||
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 pt-4" 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="lg"
|
||||
onClick={toggleSave}
|
||||
disabled={!isAuthenticated || Boolean(loadingAction)}
|
||||
className="gap-2 rounded-full border border-border/60 bg-background/80 px-5 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",
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<span>ذخیره</span>
|
||||
</Button>
|
||||
{!isAuthenticated ? (
|
||||
<span className="basis-full text-center text-xs text-muted-foreground">
|
||||
برای پسندیدن یا ذخیره کردن وارد حساب کاربری شوید.
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user