mirror of
https://github.com/dergigi/boris.git
synced 2026-01-25 01:34:30 +01:00
85 lines
2.5 KiB
TypeScript
85 lines
2.5 KiB
TypeScript
import React from 'react'
|
|
import { formatDistanceToNow, differenceInSeconds, differenceInMinutes, differenceInHours, differenceInDays, differenceInMonths, differenceInYears } from 'date-fns'
|
|
import { ParsedContent, ParsedNode } from '../types/bookmarks'
|
|
import ResolvedMention from '../components/ResolvedMention'
|
|
// Note: ContentWithResolvedProfiles is imported by components directly to keep this file component-only for fast refresh
|
|
|
|
export const formatDate = (timestamp: number) => {
|
|
const date = new Date(timestamp * 1000)
|
|
return formatDistanceToNow(date, { addSuffix: true })
|
|
}
|
|
|
|
// Ultra-compact date format for tight spaces (e.g., compact view)
|
|
export const formatDateCompact = (timestamp: number) => {
|
|
const date = new Date(timestamp * 1000)
|
|
const now = new Date()
|
|
|
|
const seconds = differenceInSeconds(now, date)
|
|
const minutes = differenceInMinutes(now, date)
|
|
const hours = differenceInHours(now, date)
|
|
const days = differenceInDays(now, date)
|
|
const months = differenceInMonths(now, date)
|
|
const years = differenceInYears(now, date)
|
|
|
|
if (seconds < 60) return 'now'
|
|
if (minutes < 60) return `${minutes}m`
|
|
if (hours < 24) return `${hours}h`
|
|
if (days < 30) return `${days}d`
|
|
if (months < 12) return `${months}mo`
|
|
return `${years}y`
|
|
}
|
|
|
|
// Component to render content with resolved nprofile names
|
|
// Intentionally no exports except components and render helpers
|
|
|
|
// Component to render parsed content using applesauce-content
|
|
export const renderParsedContent = (parsedContent: ParsedContent) => {
|
|
if (!parsedContent || !parsedContent.children) {
|
|
return null
|
|
}
|
|
|
|
const renderNode = (node: ParsedNode, index: number): React.ReactNode => {
|
|
if (node.type === 'text') {
|
|
return <span key={index}>{node.value}</span>
|
|
}
|
|
|
|
if (node.type === 'mention') {
|
|
return <ResolvedMention key={index} encoded={node.encoded} />
|
|
}
|
|
|
|
if (node.type === 'link') {
|
|
return (
|
|
<a
|
|
key={index}
|
|
href={node.url}
|
|
className="nostr-link"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
>
|
|
{node.url}
|
|
</a>
|
|
)
|
|
}
|
|
|
|
if (node.children) {
|
|
return (
|
|
<span key={index}>
|
|
{node.children.map((child: ParsedNode, childIndex: number) =>
|
|
renderNode(child, childIndex)
|
|
)}
|
|
</span>
|
|
)
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
return (
|
|
<div className="parsed-content">
|
|
{parsedContent.children.map((node: ParsedNode, index: number) =>
|
|
renderNode(node, index)
|
|
)}
|
|
</div>
|
|
)
|
|
}
|