import { Text } from '@/aesthetics/Text';
import { font, getScoreColor, radius, spacing, useColors, weight } from '@/aesthetics/styles';
import { useStrings } from '@/common/hooks/useStrings';
import { isSameDay } from '@/common/utils/format';
import type { Strings } from '@/common/strings';
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_BASE_HEIGHT = 24;
const BAR_SCORE_HEIGHT = 16;
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 scoreHeight = (day.score / 100) * BAR_SCORE_HEIGHT;
const totalHeight = BAR_BASE_HEIGHT + scoreHeight;
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: radius.sm,
},
bar: {
width: 8,
borderRadius: radius.pill,
},
todayDot: {
width: 4,
height: 4,
borderRadius: 2,
marginBottom: 4,
},
});