feat: add relative time formatting for createdAt in KIND20Card and NoteCard components

This commit is contained in:
2025-05-27 12:28:43 +02:00
parent 4559956a2f
commit b24b3802b3
3 changed files with 131 additions and 6 deletions

View File

@@ -15,6 +15,8 @@ import Image from "next/image"
import CardOptionsDropdown from "./CardOptionsDropdown"
import { renderTextWithLinkedTags } from "@/utils/textUtils"
import { getProxiedImageUrl } from "@/utils/utils"
import { formatRelativeTime } from "@/utils/dateUtils"
import { Clock, UploadCloud } from "lucide-react"
// Function to extract all images from a kind 20 event's imeta tags
const extractImagesFromEvent = (tags: string[][]): string[] => {
@@ -197,11 +199,47 @@ const KIND20Card: React.FC<KIND20CardProps> = ({
</div>
</div>
</CardContent>
<CardFooter>
<div className="grid grid-cols-1">
<small className="text-muted">{createdAt.toLocaleString()}</small>
{uploadedVia && <small className="text-muted">Uploaded via {uploadedVia}</small>}
<CardFooter className="flex flex-row items-center justify-between">
<div className="flex flex-col text-sm text-muted-foreground">
<div className="flex items-center">
<Clock className="h-4 w-4 mr-1" />
<span>{formatRelativeTime(createdAt)}</span>
</div>
{uploadedVia && (
<div className="flex items-center mt-1">
{/* <svg
xmlns="http://www.w3.org/2000/svg"
className="h-4 w-4 mr-1"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={1.5}
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
/>
</svg> */}
<UploadCloud className="h-4 w-4 mr-1" />
<span>via {uploadedVia}</span>
</div>
)}
</div>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div className="text-xs text-muted-foreground/70 hover:text-muted-foreground cursor-help">
{createdAt.toLocaleString()}
</div>
</TooltipTrigger>
<TooltipContent side="bottom">
<p>Full timestamp</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</CardFooter>
</Card>
</div>

View File

@@ -31,6 +31,8 @@ import { Event as NostrEvent } from "nostr-tools";
import ZapButton from './ZapButton';
import CardOptionsDropdown from './CardOptionsDropdown';
import { renderTextWithLinkedTags } from '@/utils/textUtils';
import { Clock } from 'lucide-react';
import { formatRelativeTime } from '@/utils/dateUtils';
interface NoteCardProps {
pubkey: string;
@@ -155,8 +157,26 @@ const NoteCard: React.FC<NoteCardProps> = ({ pubkey, text, eventId, tags, event,
</div>
</div>
</CardContent>
<CardFooter>
<small className="text-muted">{createdAt.toLocaleString()}</small>
<CardFooter className="flex flex-row items-center justify-between">
<div className="flex flex-col text-sm text-muted-foreground">
<div className="flex items-center">
<Clock className="h-4 w-4 mr-1" />
<span>{formatRelativeTime(createdAt)}</span>
</div>
</div>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div className="text-xs text-muted-foreground/70 hover:text-muted-foreground cursor-help">
{createdAt.toLocaleString()}
</div>
</TooltipTrigger>
<TooltipContent side="bottom">
<p>Full timestamp</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</CardFooter>
</Card>
</>

67
utils/dateUtils.ts Normal file
View File

@@ -0,0 +1,67 @@
/**
* Formats a date into a readable format
*
* Examples:
* - Just now (less than 1 minute ago)
* - 5 minutes ago
* - 2 hours ago
* - Yesterday at 2:30 PM
* - May 25 at 3:45 PM
* - May 25, 2024 at 3:45 PM (if not current year)
*/
export function formatRelativeTime(date: Date): string {
const now = new Date();
const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);
// Just now - less than 1 minute
if (diffInSeconds < 60) {
return 'Just now';
}
// Minutes ago - less than 1 hour
if (diffInSeconds < 3600) {
const minutes = Math.floor(diffInSeconds / 60);
return `${minutes} ${minutes === 1 ? 'minute' : 'minutes'} ago`;
}
// Hours ago - less than 24 hours
if (diffInSeconds < 86400) {
const hours = Math.floor(diffInSeconds / 3600);
return `${hours} ${hours === 1 ? 'hour' : 'hours'} ago`;
}
// Format the time part (e.g., "3:45 PM")
const timeFormatted = date.toLocaleTimeString([], {
hour: 'numeric',
minute: '2-digit',
hour12: true
});
// Yesterday - less than 48 hours, but different calendar day
if (diffInSeconds < 172800 &&
date.getDate() === now.getDate() - 1 &&
date.getMonth() === now.getMonth() &&
date.getFullYear() === now.getFullYear()) {
return `Yesterday at ${timeFormatted}`;
}
// Format the date part
const isCurrentYear = date.getFullYear() === now.getFullYear();
if (isCurrentYear) {
// Same year - show month and day (e.g., "May 25 at 3:45 PM")
const dateFormatted = date.toLocaleDateString([], {
month: 'short',
day: 'numeric'
});
return `${dateFormatted} at ${timeFormatted}`;
} else {
// Different year - show full date (e.g., "May 25, 2024 at 3:45 PM")
const dateFormatted = date.toLocaleDateString([], {
month: 'short',
day: 'numeric',
year: 'numeric'
});
return `${dateFormatted} at ${timeFormatted}`;
}
}