import { useEffect, useState } from "react"; import { Text, TextStyle } from "react-native"; import { gameUIColors } from "../gameUI"; interface TimeDisplayProps { time: Date | string | number; format?: "relative" | "absolute" | "duration" | "mixed"; updateInterval?: number; style?: TextStyle; showSeconds?: boolean; prefix?: string; } export function TimeDisplay({ time, format = "relative", updateInterval = 60000, style, showSeconds = false, prefix, }: TimeDisplayProps) { const [displayTime, setDisplayTime] = useState(""); const formatRelativeTime = (date: Date): string => { const now = Date.now(); const diff = now - date.getTime(); const seconds = Math.floor(diff / 1000); if (seconds < 60) return "just now"; const minutes = Math.floor(seconds / 60); if (minutes < 60) return `${minutes}m ago`; const hours = Math.floor(minutes / 60); if (hours < 24) return `${hours}h ago`; const days = Math.floor(hours / 24); if (days < 7) return `${days}d ago`; const weeks = Math.floor(days / 7); if (weeks < 4) return `${weeks}w ago`; const months = Math.floor(days / 30); if (months < 12) return `${months}mo ago`; const years = Math.floor(days / 365); return `${years}y ago`; }; const formatAbsoluteTime = (date: Date, withSeconds: boolean): string => { if (withSeconds) { return date.toLocaleTimeString(); } return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }); }; const formatDuration = (ms: number): string => { if (ms < 1000) return `${ms}ms`; const seconds = Math.floor(ms / 1000); if (seconds < 60) return `${seconds}s`; const minutes = Math.floor(seconds / 60); const remainingSeconds = seconds % 60; if (minutes < 60) { return remainingSeconds > 0 ? `${minutes}m ${remainingSeconds}s` : `${minutes}m`; } const hours = Math.floor(minutes / 60); const remainingMinutes = minutes % 60; return remainingMinutes > 0 ? `${hours}h ${remainingMinutes}m` : `${hours}h`; }; useEffect(() => { const formatMixed = (date: Date): string => { const now = Date.now(); const diff = now - date.getTime(); const hours = Math.floor(diff / (1000 * 60 * 60)); if (hours < 24) { return formatRelativeTime(date); } return date.toLocaleDateString([], { month: "short", day: "numeric", year: date.getFullYear() !== new Date().getFullYear() ? "numeric" : undefined, }); }; const updateTime = () => { if (format === "duration" && typeof time === "number") { setDisplayTime(formatDuration(time)); return; } const date = new Date(time); switch (format) { case "relative": setDisplayTime(formatRelativeTime(date)); break; case "absolute": setDisplayTime(formatAbsoluteTime(date, showSeconds)); break; case "mixed": setDisplayTime(formatMixed(date)); break; default: setDisplayTime(formatRelativeTime(date)); } }; updateTime(); if (format === "relative" && updateInterval > 0) { const interval = setInterval(updateTime, updateInterval); return () => clearInterval(interval); } }, [time, format, updateInterval, showSeconds]); return ( {prefix ? `${prefix} ${displayTime}` : displayTime} ); } interface TimestampProps { time: Date | string | number; style?: TextStyle; } TimeDisplay.Timestamp = function Timestamp({ time, style }: TimestampProps) { return ; }; interface DurationProps { milliseconds: number; style?: TextStyle; } TimeDisplay.Duration = function Duration({ milliseconds, style, }: DurationProps) { return ; }; interface RelativeProps { time: Date | string; updateInterval?: number; style?: TextStyle; } TimeDisplay.Relative = function Relative({ time, updateInterval = 60000, style, }: RelativeProps) { return ( ); }; const defaultStyles = { text: { fontSize: 12, color: gameUIColors.tertiary, }, };