import React, { useState, useEffect, useRef } from 'react'; import { StyleSheet, Animated } from 'react-native'; import { View, Text, useTheme } from '@bettoredge/styles'; import { Ionicons } from '@expo/vector-icons'; export interface CalcuttaCountdownProps { targetDate: string; label?: string; size?: 'compact' | 'normal'; } type UrgencyTier = 'calm' | 'upcoming' | 'soon' | 'urgent' | 'critical' | 'imminent' | 'past'; const getTier = (ms: number): UrgencyTier => { if (ms <= 0) return 'past'; if (ms < 60_000) return 'imminent'; // < 1 min if (ms < 600_000) return 'critical'; // < 10 min if (ms < 3_600_000) return 'urgent'; // < 1 hour if (ms < 86_400_000) return 'soon'; // < 24 hours if (ms < 604_800_000) return 'upcoming'; // < 7 days return 'calm'; }; const formatRemaining = (ms: number, tier: UrgencyTier): string => { if (tier === 'past') return 'Now'; const totalSeconds = Math.floor(ms / 1000); const days = Math.floor(totalSeconds / 86400); const hours = Math.floor((totalSeconds % 86400) / 3600); const minutes = Math.floor((totalSeconds % 3600) / 60); const seconds = totalSeconds % 60; switch (tier) { case 'calm': { const d = new Date(Date.now() + ms); return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' }); } case 'upcoming': return `${days}d ${hours}h`; case 'soon': return `${hours}h ${minutes}m`; case 'urgent': return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`; case 'critical': return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`; case 'imminent': return `0:${String(seconds).padStart(2, '0')}`; default: return ''; } }; export const CalcuttaCountdown: React.FC = ({ targetDate, label, size = 'normal', }) => { const { theme } = useTheme(); const [remaining, setRemaining] = useState(() => new Date(targetDate).getTime() - Date.now()); const pulseAnim = useRef(new Animated.Value(1)).current; const tier = getTier(remaining); // Live tick for urgent tiers useEffect(() => { const needsTick = ['urgent', 'critical', 'imminent'].includes(tier); if (!needsTick) { // Update every minute for less urgent tiers const interval = setInterval(() => { setRemaining(new Date(targetDate).getTime() - Date.now()); }, 60_000); return () => clearInterval(interval); } const interval = setInterval(() => { setRemaining(new Date(targetDate).getTime() - Date.now()); }, 1000); return () => clearInterval(interval); }, [targetDate, tier]); // Pulse animation for critical/imminent useEffect(() => { if (tier === 'critical' || tier === 'imminent') { const loop = Animated.loop( Animated.sequence([ Animated.timing(pulseAnim, { toValue: 0.6, duration: 600, useNativeDriver: true }), Animated.timing(pulseAnim, { toValue: 1, duration: 600, useNativeDriver: true }), ]) ); loop.start(); return () => loop.stop(); } else { pulseAnim.setValue(1); } }, [tier]); const getTierColor = () => { switch (tier) { case 'calm': case 'upcoming': return theme.colors.text.secondary; case 'soon': return theme.colors.primary.default; case 'urgent': return '#D97706'; // amber case 'critical': case 'imminent': return theme.colors.status.error; case 'past': return theme.colors.text.tertiary; } }; const getTierBg = () => { switch (tier) { case 'critical': case 'imminent': return theme.colors.status.error + '15'; case 'urgent': return '#D9770615'; default: return 'transparent'; } }; const isCompact = size === 'compact'; const timeColor = getTierColor(); const formatted = formatRemaining(remaining, tier); if (tier === 'past') return null; return ( {label && ( {label} )} {tier === 'calm' || tier === 'upcoming' ? formatted : formatted} ); }; const styles = StyleSheet.create({ container: { borderRadius: 8, paddingHorizontal: 10, paddingVertical: 6, alignSelf: 'flex-start', }, containerCompact: { paddingHorizontal: 8, paddingVertical: 4, flexDirection: 'row', alignItems: 'center', }, label: { marginBottom: 2, }, labelCompact: { marginRight: 6, }, timeRow: { flexDirection: 'row', alignItems: 'center', }, });