// Lightweight relative-time formatter for the native package. // // Mirrors the wording the builder web app produces via formatRelativeTime // ("5 minutes ago", "2 hours ago") so the home screen reads identically, // without depending on Intl.RelativeTimeFormat — which is not reliably // available across the JS engines the native app ships on. const DIVISIONS: Array<{ amount: number; unit: string }> = [ { amount: 60, unit: 'minute' }, { amount: 24, unit: 'hour' }, { amount: 7, unit: 'day' }, { amount: 4.34524, unit: 'week' }, { amount: 12, unit: 'month' }, { amount: Number.POSITIVE_INFINITY, unit: 'year' }, ]; /** "just now" / "5 minutes ago" / "2 hours ago" / "3 days ago" — or '' if unparseable. */ export function formatRelativeTime(input?: string | number | null): string { const ms = toMs(input); if (ms == null) return ''; const seconds = (Date.now() - ms) / 1000; if (seconds < 45) return 'just now'; let duration = seconds / 60; // start in minutes for (const division of DIVISIONS) { if (Math.abs(duration) < division.amount) { const value = Math.round(duration); return `${value} ${division.unit}${value === 1 ? '' : 's'} ago`; } duration /= division.amount; } return ''; } const WEEKDAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; export const DAY_MS = 86_400_000; /** * Chat-app style timestamp, like a messaging inbox: * today -> "9:41 AM" * yesterday -> "Yesterday" * this week -> "Mon" * older -> "3/14/26" */ export function formatChatTimestamp(input?: string | number | null): string { const ms = toMs(input); if (ms == null) return ''; const date = new Date(ms); const now = new Date(); const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime(); if (ms >= startOfToday) { let hours = date.getHours(); const minutes = date.getMinutes(); const meridiem = hours >= 12 ? 'PM' : 'AM'; hours %= 12; if (hours === 0) hours = 12; return `${hours}:${String(minutes).padStart(2, '0')} ${meridiem}`; } if (ms >= startOfToday - DAY_MS) return 'Yesterday'; if (ms >= startOfToday - 6 * DAY_MS) return WEEKDAYS[date.getDay()]; const year = String(date.getFullYear()).slice(-2); return `${date.getMonth() + 1}/${date.getDate()}/${year}`; } function toMs(input?: string | number | null): number | null { if (input == null) return null; if (typeof input === 'number') return Number.isFinite(input) ? input : null; // Match the builder's parseUTCDate: treat tz-less datetimes as UTC. const str = String(input).trim(); if (!str) return null; const hasTimezone = /([zZ]|[+-]\d{2}:?\d{2})$/.test(str); const isDateOnly = /^\d{4}-\d{2}-\d{2}$/.test(str); const normalized = hasTimezone ? str : str + (isDateOnly ? 'T00:00:00Z' : 'Z'); const ms = Date.parse(normalized); return Number.isNaN(ms) ? null : ms; }