import { Text } from '@/aesthetics/Text'; import { font, getScoreColor, radius, spacing, useColors, weight } from '@/aesthetics/styles'; import { useStrings } from '@/common/hooks/useStrings'; import type { Strings } from '@/common/strings'; import { isSameDay } from '@/common/utils/format'; import { useCallback, useEffect, useMemo, useRef } from 'react'; import { Pressable, ScrollView, StyleSheet, View, useWindowDimensions } from 'react-native'; import type { DayScore } from './useNutritionView'; const getDayName = (dayIndex: number, time: Strings['time']) => { const days = [ time.days.sun, time.days.mon, time.days.tue, time.days.wed, time.days.thu, time.days.fri, time.days.sat, ]; return days[dayIndex]; }; const getMonthName = (monthIndex: number, time: Strings['time']) => { const months = [ time.months.january, time.months.february, time.months.march, time.months.april, time.months.may, time.months.june, time.months.july, time.months.august, time.months.september, time.months.october, time.months.november, time.months.december, ]; return months[monthIndex]; }; const BAR_SIZE = 10; const BAR_WIDTH = 28; function DayBar({ day, isSelected, isToday, isFuture, onPress, }: { day: DayScore; isSelected: boolean; isToday: boolean; isFuture: boolean; onPress: () => void; }) { const c = useColors(); const barColor = isFuture ? c.borderSubtle : day.score === 0 ? c.progressBg : getScoreColor(day.score, c); return ( {isToday && } ); } type DayBarScrollProps = { date: Date; dayScores: DayScore[]; onSelectDate: (date: Date) => void; onLoadMore: () => void; }; export function DayBarScroll({ date, dayScores, onSelectDate, onLoadMore }: DayBarScrollProps) { const c = useColors(); const strings = useStrings(); const { width: screenWidth } = useWindowDimensions(); const barPadding = (screenWidth - BAR_WIDTH) / 2; const scrollRef = useRef(null); const today = useMemo(() => new Date(), []); const lastSelectedIndex = useRef(-1); const initialScrollDone = useRef(false); const scrollToIndex = useCallback((index: number, animated = true) => { scrollRef.current?.scrollTo({ x: index * BAR_WIDTH, animated }); }, []); useEffect(() => { if (initialScrollDone.current || dayScores.length === 0) return; const todayIndex = dayScores.findIndex(d => isSameDay(d.date, today)); if (todayIndex >= 0) { const timer = setTimeout(() => { scrollRef.current?.scrollTo({ x: todayIndex * BAR_WIDTH, animated: false }); lastSelectedIndex.current = todayIndex; initialScrollDone.current = true; }, 50); return () => clearTimeout(timer); } }, [dayScores, today]); const handleBarPress = useCallback((index: number) => scrollToIndex(index), [scrollToIndex]); const handleScroll = useCallback( (offsetX: number) => { const index = Math.round(offsetX / BAR_WIDTH); if (index >= 0 && index < dayScores.length && index !== lastSelectedIndex.current) { lastSelectedIndex.current = index; onSelectDate(dayScores[index].date); } }, [dayScores, onSelectDate] ); return ( {date.getFullYear()} {getMonthName(date.getMonth(), strings.time)} {date.getDate()} {getDayName(date.getDay(), strings.time)} { const { contentOffset, contentSize, layoutMeasurement } = nativeEvent; handleScroll(contentOffset.x); if (contentOffset.x + layoutMeasurement.width >= contentSize.width - 50) { onLoadMore(); } }} scrollEventThrottle={16} > {dayScores.map((day, i) => ( today} onPress={() => handleBarPress(i)} /> ))} ); } const styles = StyleSheet.create({ container: { alignItems: 'center', paddingTop: spacing.sm, paddingBottom: spacing.lg, marginBottom: spacing.sm, }, dateDisplay: { alignItems: 'center', marginBottom: spacing.lg, }, dateYear: { fontSize: font.sm, letterSpacing: 1, textTransform: 'uppercase', }, dateMonth: { fontSize: font.sm, fontWeight: weight.medium, marginTop: 2, }, dateDayRow: { alignItems: 'center', marginTop: spacing.xs, }, dateDayNum: { fontSize: font.display, letterSpacing: -1, }, dateDayName: { fontSize: font.sm, textTransform: 'uppercase', letterSpacing: 2, marginTop: 2, }, barScrollView: { width: '100%', }, barScroll: { alignItems: 'flex-end', }, barWrapper: { width: BAR_WIDTH, alignItems: 'center', justifyContent: 'flex-end', }, barOuter: { padding: 4, borderRadius: (BAR_SIZE + 8) / 2, }, bar: { width: BAR_SIZE, height: BAR_SIZE, borderRadius: BAR_SIZE / 2, }, todayDot: { width: 4, height: 4, borderRadius: 2, marginBottom: 4, }, });