Files
aragrp/components/blog-post-meta.tsx
T
alirez.br ff2632f8db feat: enhance image handling and layout adjustments
- Added image quality settings and device sizes in next.config.ts for better image optimization.
- Updated package dependencies, including cheerio and baseline-browser-mapping.
- Improved global styles for blog content images to ensure responsiveness.
- Refactored button components for consistent sizing and styling across the application.
- Enhanced blog post card layout with new variants and improved author name formatting.
- Adjusted hero section and content layout for better visual appeal and user experience.
2026-06-17 21:35:35 +03:30

181 lines
5.0 KiB
TypeScript

"use client";
import { useState, useCallback, useEffect } from "react";
import {
Link2,
Clock,
Calendar,
User,
Share2,
Check,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
export interface BlogPostMetaProps {
/** Path (e.g. /blog/slug). Full URL is built on client for share/copy. */
url: string;
title: string;
readingTimeMinutes: number;
/** Shamsi publish date (e.g. "۱۴۰۳/۰۸/۱۵") */
date?: string | null;
/** ISO date for <time dateTime> */
dateTime?: string | null;
modifiedDate?: string | null;
authorName?: string | null;
className?: string;
}
export function BlogPostMeta({
url,
title,
readingTimeMinutes,
date,
dateTime,
modifiedDate,
authorName,
className,
}: BlogPostMetaProps) {
const [copied, setCopied] = useState(false);
const [shared, setShared] = useState(false);
const [fullUrl, setFullUrl] = useState(url);
const [canNativeShare, setCanNativeShare] = useState(false);
useEffect(() => {
if (url.startsWith("/")) {
setFullUrl(window.location.origin + url);
} else {
setFullUrl(url);
}
setCanNativeShare(Boolean(typeof navigator !== "undefined" && navigator.share));
}, [url]);
const copyLink = useCallback(async () => {
try {
await navigator.clipboard.writeText(fullUrl);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch {
setCopied(false);
}
}, [fullUrl]);
const handleShare = useCallback(async () => {
if (typeof navigator !== "undefined" && navigator.share) {
try {
await navigator.share({
title,
url: fullUrl,
text: title,
});
setShared(true);
setTimeout(() => setShared(false), 2000);
} catch (err) {
if ((err as Error).name !== "AbortError") copyLink();
}
} else {
copyLink();
}
}, [title, fullUrl, copyLink]);
return (
<TooltipProvider delayDuration={300}>
<div
className={cn(
"flex flex-wrap items-center gap-x-5 gap-y-2 text-sm text-muted-foreground",
className
)}
>
{/* Reading time */}
<span className="inline-flex items-center gap-1.5">
<Clock className="size-4 shrink-0" aria-hidden />
<span>
{readingTimeMinutes === 1
? "۱ دقیقه مطالعه"
: `${readingTimeMinutes.toLocaleString("fa-IR")} دقیقه مطالعه`}
</span>
</span>
{/* Publish date */}
{date && (
<time
className="inline-flex items-center gap-1.5"
dateTime={dateTime ?? undefined}
>
<Calendar className="size-4 shrink-0" aria-hidden />
<span>انتشار: {date}</span>
</time>
)}
{/* Last updated */}
{modifiedDate && (
<span className="inline-flex items-center gap-1.5">
<Calendar className="size-4 shrink-0" aria-hidden />
<span>آخرین بروزرسانی: {modifiedDate}</span>
</span>
)}
{/* Author */}
{authorName && (
<span className="inline-flex items-center gap-1.5">
<User className="size-4 shrink-0" aria-hidden />
<span>{authorName}</span>
</span>
)}
{/* Divider + actions */}
<span className="mr-auto inline-flex items-center gap-1">
<span
className="hidden sm:inline h-4 w-px bg-border ml-2"
aria-hidden
/>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon-sm"
onClick={copyLink}
aria-label="کپی لینک"
>
{copied ? (
<Check className="size-4 text-green-600" />
) : (
<Link2 className="size-4" />
)}
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">
{copied ? "کپی شد" : "کپی لینک"}
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon-sm"
onClick={handleShare}
aria-label="اشتراک‌گذاری"
>
{shared ? (
<Check className="size-4 text-green-600" />
) : (
<Share2 className="size-4" />
)}
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">
{shared ? "اشتراک گذاشته شد" : canNativeShare ? "اشتراک‌گذاری" : "کپی لینک"}
</TooltipContent>
</Tooltip>
</span>
</div>
</TooltipProvider>
);
}