mirror of
https://github.com/lumina-rocks/lumina.git
synced 2026-06-04 01:31:13 +02:00
feat: add relative time formatting for createdAt in KIND20Card and NoteCard components
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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
67
utils/dateUtils.ts
Normal 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}`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user