From 9cba26a82f8f921b4c0467b52155d64f8264198a Mon Sep 17 00:00:00 2001 From: highperfocused Date: Mon, 17 Nov 2025 21:41:39 +0100 Subject: [PATCH] feat: add date validation and safe ISO string conversion for improved date handling --- src/components/ArticlePreview.tsx | 17 +++++++++++------ src/components/ArticleView.tsx | 25 +++++++++++++++---------- src/lib/date.ts | 13 +++++++++++++ src/pages/ArticlePage.tsx | 16 +++++++++++----- src/pages/EventPage.tsx | 30 +++++++++++++++++------------- src/pages/NotePage.tsx | 28 ++++++++++++++++------------ 6 files changed, 83 insertions(+), 46 deletions(-) create mode 100644 src/lib/date.ts diff --git a/src/components/ArticlePreview.tsx b/src/components/ArticlePreview.tsx index 6e5cd30..d56bfae 100644 --- a/src/components/ArticlePreview.tsx +++ b/src/components/ArticlePreview.tsx @@ -7,6 +7,7 @@ import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Calendar } from 'lucide-react'; import { useAuthor } from '@/hooks/useAuthor'; import { genUserName } from '@/lib/genUserName'; +import { isValidDate, toISOStringSafe } from '@/lib/date'; interface ArticlePreviewProps { post: NostrEvent; @@ -48,6 +49,8 @@ export function ArticlePreview({ post, variant = 'default', showAuthor = true }: ? { month: 'short', day: 'numeric', year: 'numeric' } : { year: 'numeric', month: 'long', day: 'numeric' }; + const valid = isValidDate(date); + return ( @@ -71,12 +74,14 @@ export function ArticlePreview({ post, variant = 'default', showAuthor = true }: )} -
0 ? 'mb-3' : ''}`}> - - -
+ {valid && ( +
0 ? 'mb-3' : ''}`}> + + +
+ )} {showAuthor && (
0 ? 'mb-3' : ''}`}> diff --git a/src/components/ArticleView.tsx b/src/components/ArticleView.tsx index 3c1ccba..6274ac7 100644 --- a/src/components/ArticleView.tsx +++ b/src/components/ArticleView.tsx @@ -17,6 +17,7 @@ import { Separator } from '@/components/ui/separator'; import { Calendar, Heart, Edit, ArrowLeft, Share2, Check, Code } from 'lucide-react'; import { genUserName } from '@/lib/genUserName'; import { calculateReadingTime } from '@/lib/calculateReadingTime'; +import { isValidDate, toISOStringSafe } from '@/lib/date'; import { useToast } from '@/hooks/useToast'; import { useState } from 'react'; import { @@ -108,6 +109,8 @@ export function ArticleView({ post }: ArticleViewProps) { } }; + const validDate = isValidDate(date); + return (
@@ -144,16 +147,18 @@ export function ArticleView({ post }: ArticleViewProps) {
{displayName}
-
- - -
+ {validDate && ( +
+ + +
+ )}
diff --git a/src/lib/date.ts b/src/lib/date.ts new file mode 100644 index 0000000..97f7533 --- /dev/null +++ b/src/lib/date.ts @@ -0,0 +1,13 @@ +export function isValidDate(date: Date | null | undefined): boolean { + if (!date) return false; + return !isNaN(date.getTime()); +} + +export function toISOStringSafe(date: Date | null | undefined): string { + if (!isValidDate(date as Date)) return ""; + try { + return (date as Date).toISOString(); + } catch { + return ""; + } +} diff --git a/src/pages/ArticlePage.tsx b/src/pages/ArticlePage.tsx index d95926e..82bd466 100644 --- a/src/pages/ArticlePage.tsx +++ b/src/pages/ArticlePage.tsx @@ -7,6 +7,7 @@ import { ArticleView } from '@/components/ArticleView'; import { Skeleton } from '@/components/ui/skeleton'; import NotFound from '@/pages/NotFound'; import { genUserName } from '@/lib/genUserName'; +import { toISOStringSafe } from '@/lib/date'; export default function ArticlePage() { const { nip19: naddr } = useParams<{ nip19: string }>(); @@ -71,11 +72,16 @@ export default function ArticlePage() { ogImage: image || `${siteUrl}/icon-512.png`, ogSiteName: 'zelo.news', // Article-specific OG tags - ...(post && isValidNaddr && { - articlePublishedTime: date.toISOString(), - articleAuthor: [authorName], - ...(hashtags.length > 0 && { articleTag: hashtags }), - }), + ...(post && isValidNaddr && (() => { + const iso = toISOStringSafe(date); + return iso + ? { + articlePublishedTime: iso, + articleAuthor: [authorName], + ...(hashtags.length > 0 && { articleTag: hashtags }), + } + : {}; + })()), // Twitter Card tags twitterCard: 'summary_large_image', twitterTitle: title, diff --git a/src/pages/EventPage.tsx b/src/pages/EventPage.tsx index c8a1687..b5727d2 100644 --- a/src/pages/EventPage.tsx +++ b/src/pages/EventPage.tsx @@ -13,6 +13,7 @@ import { ArrowLeft, Calendar, Hash } from 'lucide-react'; import { genUserName } from '@/lib/genUserName'; import type { NostrEvent } from '@nostrify/nostrify'; import NotFound from './NotFound'; +import { isValidDate, toISOStringSafe } from '@/lib/date'; interface EventPageProps { eventId: string; @@ -118,6 +119,7 @@ export function EventPage({ eventId, relayHints, authorPubkey, kind }: EventPage } const date = new Date(event.created_at * 1000); + const validDate = isValidDate(date); return (
@@ -141,19 +143,21 @@ export function EventPage({ eventId, relayHints, authorPubkey, kind }: EventPage Kind {event.kind} - + {validDate && ( + + )}
@@ -145,18 +147,20 @@ export function NotePage({ eventId }: NotePageProps) {
{displayName}
- + {validDate && ( + + )}