import { Ionicons } from '@expo/vector-icons'; import React, { useState } from 'react'; import { Animated, Modal, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import { Colors } from '@/constants/Colors'; import { useColorScheme } from '@/hooks/useColorScheme'; import { useI18n } from '@/hooks/useI18n'; // 睡眠详情数据类型 export type SleepDetailData = { sleepScore: number; totalSleepTime: number; sleepQualityPercentage: number; bedtime: string; wakeupTime: string; timeInBed: number; sleepStages: any[]; rawSleepSamples: any[]; averageHeartRate: number | null; sleepHeartRateData: any[]; sleepEfficiency: number; qualityDescription: string; recommendation: string; }; // Sleep Grade Component 睡眠等级组件 const SleepGradeCard = ({ icon, grade, range, isActive = false }: { icon: string; grade: string; range: string; isActive?: boolean; }) => { const { t } = useI18n(); const theme = (useColorScheme() ?? 'light') as 'light' | 'dark'; const colorTokens = Colors[theme]; const getGradeColor = (grade: string) => { switch (grade) { case t('sleepDetail.sleepGrades.low'): case t('sleepDetail.sleepGrades.poor'): return { bg: '#FECACA', text: '#DC2626' }; case t('sleepDetail.sleepGrades.normal'): case t('sleepDetail.sleepGrades.fair'): return { bg: '#D1FAE5', text: '#065F46' }; case t('sleepDetail.sleepGrades.good'): return { bg: '#D1FAE5', text: '#065F46' }; case t('sleepDetail.sleepGrades.excellent'): return { bg: '#FEF3C7', text: '#92400E' }; default: return { bg: colorTokens.pageBackgroundEmphasis, text: colorTokens.textSecondary }; } }; const colors = getGradeColor(grade); return ( {grade} {range} ); }; // Info Modal 组件 export const InfoModal = ({ visible, onClose, title, type, sleepData }: { visible: boolean; onClose: () => void; title: string; type: 'sleep-time' | 'sleep-quality'; sleepData: SleepDetailData; }) => { const { t } = useI18n(); const theme = (useColorScheme() ?? 'light') as 'light' | 'dark'; const colorTokens = Colors[theme]; const slideAnim = useState(new Animated.Value(0))[0]; React.useEffect(() => { if (visible) { // 重置动画值确保每次打开都有动画 slideAnim.setValue(0); Animated.spring(slideAnim, { toValue: 1, useNativeDriver: true, tension: 100, friction: 8, }).start(); } else { Animated.spring(slideAnim, { toValue: 0, useNativeDriver: true, tension: 100, friction: 8, }).start(); } }, [visible]); const translateY = slideAnim.interpolate({ inputRange: [0, 1], outputRange: [300, 0], }); const opacity = slideAnim.interpolate({ inputRange: [0, 1], outputRange: [0, 1], }); // 根据实际睡眠时间计算等级 const getSleepTimeGrade = (totalSleepMinutes: number) => { const hours = totalSleepMinutes / 60; if (hours < 6) return 0; // 低 if ((hours >= 6 && hours < 7) || hours > 9) return 1; // 正常 if (hours >= 7 && hours < 8) return 2; // 良好 if (hours >= 8 && hours <= 9) return 3; // 优秀 return 1; // 默认正常 }; // 根据实际睡眠质量百分比计算等级 const getSleepQualityGrade = (qualityPercentage: number) => { if (qualityPercentage < 55) return 0; // 较差 if (qualityPercentage < 70) return 1; // 一般 if (qualityPercentage < 85) return 2; // 良好 return 3; // 优秀 }; const currentSleepTimeGrade = getSleepTimeGrade(sleepData.totalSleepTime || 443); // 默认7h23m const currentSleepQualityGrade = getSleepQualityGrade(sleepData.sleepQualityPercentage || 94); // 默认94% const sleepTimeGrades = [ { icon: 'alert-circle-outline', grade: t('sleepDetail.sleepGrades.low'), range: '< 6h', isActive: currentSleepTimeGrade === 0 }, { icon: 'checkmark-circle-outline', grade: t('sleepDetail.sleepGrades.normal'), range: '6h - 7h or > 9h', isActive: currentSleepTimeGrade === 1 }, { icon: 'checkmark-circle', grade: t('sleepDetail.sleepGrades.good'), range: '7h - 8h', isActive: currentSleepTimeGrade === 2 }, { icon: 'star', grade: t('sleepDetail.sleepGrades.excellent'), range: '8h - 9h', isActive: currentSleepTimeGrade === 3 }, ]; const sleepQualityGrades = [ { icon: 'alert-circle-outline', grade: t('sleepDetail.sleepGrades.poor'), range: '< 55%', isActive: currentSleepQualityGrade === 0 }, { icon: 'checkmark-circle-outline', grade: t('sleepDetail.sleepGrades.fair'), range: '55% - 69%', isActive: currentSleepQualityGrade === 1 }, { icon: 'checkmark-circle', grade: t('sleepDetail.sleepGrades.good'), range: '70% - 84%', isActive: currentSleepQualityGrade === 2 }, { icon: 'star', grade: t('sleepDetail.sleepGrades.excellent'), range: '85% - 100%', isActive: currentSleepQualityGrade === 3 }, ]; const currentGrades = type === 'sleep-time' ? sleepTimeGrades : sleepQualityGrades; const getDescription = () => { if (type === 'sleep-time') { return t('sleepDetail.sleepTimeDescription'); } else { return t('sleepDetail.sleepQualityDescription'); } }; return ( {title} {/* 等级卡片区域 */} {currentGrades.map((grade, index) => ( ))} {getDescription()} ); }; const styles = StyleSheet.create({ // Info Modal 样式 modalOverlay: { flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.5)', justifyContent: 'flex-end', }, infoModalContent: { borderTopLeftRadius: 24, borderTopRightRadius: 24, paddingTop: 12, paddingHorizontal: 20, paddingBottom: 34, minHeight: 200, shadowColor: '#000', shadowOffset: { width: 0, height: -4 }, shadowOpacity: 0.1, shadowRadius: 16, elevation: 8, }, modalHandle: { width: 36, height: 4, backgroundColor: '#D1D5DB', borderRadius: 2, alignSelf: 'center', marginBottom: 20, }, infoModalHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16, }, infoModalTitle: { fontSize: 18, fontWeight: '700', letterSpacing: -0.3, }, infoModalCloseButton: { padding: 4, }, infoModalText: { fontSize: 15, lineHeight: 22, letterSpacing: -0.1, }, // Grade Cards 样式 gradesContainer: { marginBottom: 20, gap: 8, }, gradeCard: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingHorizontal: 16, paddingVertical: 12, borderRadius: 12, borderWidth: 1, }, gradeCardLeft: { flexDirection: 'row', alignItems: 'center', gap: 8, }, gradeText: { fontSize: 16, fontWeight: '600', letterSpacing: -0.2, }, gradeRange: { fontSize: 16, fontWeight: '700', letterSpacing: -0.3, }, });