From 5643170220508c6946605d593471d412969e9c04 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 15 Mar 2026 15:56:47 +0100 Subject: [PATCH] fix: handle non-string anniversary dates in contact detail JMAP servers can return PartialDate objects for anniversary dates per RFC 9553, causing a TypeError on .startsWith(). Coerce date input and guard URI property accesses. --- components/contacts/contact-detail.tsx | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/components/contacts/contact-detail.tsx b/components/contacts/contact-detail.tsx index 0dfe7739..78920635 100644 --- a/components/contacts/contact-detail.tsx +++ b/components/contacts/contact-detail.tsx @@ -22,7 +22,20 @@ function formatPhoneFeatures(features?: Record): string { return Object.keys(features).filter(k => features[k]).join(", "); } -function formatDate(dateStr: string): string { +function formatDate(dateInput: string | Record): string { + // Handle RFC 9553 PartialDate objects: { year?, month?, day?, calendarScale? } + if (typeof dateInput === 'object' && dateInput !== null) { + const year = dateInput.year as number | undefined; + const month = dateInput.month as number | undefined; + const day = dateInput.day as number | undefined; + const monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; + const parts: string[] = []; + if (month && monthNames[month - 1]) parts.push(monthNames[month - 1]); + if (day) parts.push(String(day)); + if (year) parts.push(String(year)); + return parts.join(' ') || String(dateInput); + } + const dateStr = String(dateInput); // Handle both ISO dates and partial dates like 1990-01-15 or --01-15 if (dateStr.startsWith("--")) { // Partial date without year @@ -216,12 +229,12 @@ export function ContactDetail({ contact, onEdit, onDelete, isMobile, className }
{onlineServices.map((svc, i) => (
- {svc.uri.startsWith("http") ? ( + {typeof svc.uri === 'string' && svc.uri.startsWith("http") ? ( {svc.user || svc.uri} ) : ( - {svc.user || svc.uri} + {svc.user || String(svc.uri ?? '')} )} {svc.service && ( {svc.service} @@ -320,12 +333,12 @@ export function ContactDetail({ contact, onEdit, onDelete, isMobile, className }
{cryptoKeys.map((key, i) => (
- {key.uri.startsWith("http") ? ( + {typeof key.uri === 'string' && key.uri.startsWith("http") ? ( {key.uri} ) : ( - {key.uri.substring(0, 80)}{key.uri.length > 80 ? "…" : ""} + {typeof key.uri === 'string' ? `${key.uri.substring(0, 80)}${key.uri.length > 80 ? "…" : ""}` : String(key.uri ?? '')} )}
))}