import { View, Text } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { useTranslation } from 'react-i18next'; import { useColors } from '../../lib/theme'; export type CooldownEntry = { id: string; startedAt: string; rawStartedAt: string; durationLabel: string; status: 'active' | 'resolved' | 'cancelled'; reason: string | null; }; type Props = { currentDays: number; longestDays: number; startDate: string; cooldowns: CooldownEntry[]; }; const WEEKS = 8; const MAX_BAR_HEIGHT = 28; const MIN_BAR_HEIGHT = 2; function getMondayOfWeek(date: Date): Date { const d = new Date(date); const day = d.getDay(); const diff = (day === 0 ? -6 : 1 - day); d.setDate(d.getDate() + diff); d.setHours(0, 0, 0, 0); return d; } function buildWeekBuckets(cooldowns: CooldownEntry[]): number[] { const now = new Date(); const currentWeekMonday = getMondayOfWeek(now); const buckets: number[] = Array(WEEKS).fill(0); for (const c of cooldowns) { if (!c.rawStartedAt) continue; const started = new Date(c.rawStartedAt); const weekMonday = getMondayOfWeek(started); const diffMs = currentWeekMonday.getTime() - weekMonday.getTime(); const diffWeeks = Math.round(diffMs / (7 * 24 * 60 * 60 * 1000)); if (diffWeeks >= 0 && diffWeeks < WEEKS) { const bucketIndex = WEEKS - 1 - diffWeeks; buckets[bucketIndex]++; } } return buckets; } function formatLastDate(cooldowns: CooldownEntry[], language: string): string { if (cooldowns.length === 0) return ''; const sorted = [...cooldowns].sort( (a, b) => new Date(b.rawStartedAt).getTime() - new Date(a.rawStartedAt).getTime(), ); const latest = new Date(sorted[0].rawStartedAt); if (language === 'de') { const day = String(latest.getDate()).padStart(2, '0'); const month = String(latest.getMonth() + 1).padStart(2, '0'); return `${day}.${month}.`; } return latest.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } function formatAvg(totalCount: number, language: string): string { if (totalCount === 0) return '0'; const avg = WEEKS / totalCount; if (language === 'de') { return avg.toFixed(1).replace('.', ','); } return avg.toFixed(1); } export function StreakSection({ currentDays, longestDays, startDate, cooldowns }: Props) { const colors = useColors(); const { t, i18n } = useTranslation(); const lang = i18n.language ?? 'de'; const buckets = buildWeekBuckets(cooldowns); const maxCount = Math.max(...buckets, 1); const totalInWindow = buckets.reduce((s, v) => s + v, 0); const cooldownsInWindow = totalInWindow; const lastDate = cooldowns.length > 0 ? formatLastDate(cooldowns, lang) : null; const avgStr = formatAvg(cooldownsInWindow, lang); const countLabel = cooldownsInWindow === 0 ? t('profile.cooldown.none') : cooldownsInWindow === 1 ? t('profile.cooldown.count_one', { weeks: WEEKS }) : t('profile.cooldown.count_other', { n: cooldownsInWindow, weeks: WEEKS }); const avgLabel = cooldownsInWindow > 0 && lastDate ? t('profile.cooldown.avg_last', { avg: avgStr, date: lastDate }) : null; return ( {t('profile.streak_section_label')} {currentDays} {t('profile.streak_days_protected')} {t('profile.streak_since', { date: startDate })} {t('profile.streak_longest', { days: longestDays })} {t('profile.cooldown.heading')} {t('profile.cooldown.window_label', { weeks: WEEKS })} {buckets.map((count, i) => { const isEmpty = count === 0; const barHeight = isEmpty ? MIN_BAR_HEIGHT : Math.max( MIN_BAR_HEIGHT, Math.min(count, 5) / Math.min(maxCount, 5) * MAX_BAR_HEIGHT, ); return ( ); })} {buckets.map((_, i) => ( {t('profile.cooldown.week_label', { n: i + 1 })} ))} {countLabel} {avgLabel ? ( {avgLabel} ) : null} ); }