import { useAppDispatch, useAppSelector } from '@/hooks/redux'; import { ChallengeType } from '@/services/challengesApi'; import { reportChallengeProgress, selectChallengeList } from '@/store/challengesSlice'; import { logger } from '@/utils/logger'; import { fetchCompleteSleepData, formatSleepTime } from '@/utils/sleepHealthKit'; import dayjs from 'dayjs'; import { Image } from 'expo-image'; import { router } from 'expo-router'; import React, { useEffect, useMemo, useRef, useState } from 'react'; import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'; interface SleepCardProps { selectedDate?: Date; style?: object; } const SleepCard: React.FC = ({ selectedDate, style, }) => { const dispatch = useAppDispatch(); const challenges = useAppSelector(selectChallengeList); const [sleepDuration, setSleepDuration] = useState(null); const [loading, setLoading] = useState(false); const joinedSleepChallenges = useMemo( () => challenges.filter((challenge) => challenge.type === ChallengeType.SLEEP && challenge.isJoined), [challenges] ); const lastReportedRef = useRef<{ date: string; value: number | null } | null>(null); // 获取睡眠数据 useEffect(() => { const loadSleepData = async () => { if (!selectedDate) return; try { setLoading(true); const data = await fetchCompleteSleepData(selectedDate); setSleepDuration(data?.totalSleepTime || null); } catch (error) { console.error('SleepCard: 获取睡眠数据失败:', error); setSleepDuration(null); } finally { setLoading(false); } }; loadSleepData(); }, [selectedDate]); useEffect(() => { if (!selectedDate || !sleepDuration || !joinedSleepChallenges.length) { return; } // 如果当前日期不是今天,不上报 if (!dayjs(selectedDate).isSame(dayjs(), 'day')) { return; } const dateKey = dayjs(selectedDate).format('YYYY-MM-DD'); const lastReport = lastReportedRef.current; if (lastReport && lastReport.date === dateKey && lastReport.value === sleepDuration) { return; } const reportProgress = async () => { const sleepChallenge = joinedSleepChallenges.find((c) => c.type === ChallengeType.SLEEP); if (!sleepChallenge) { return; } try { await dispatch(reportChallengeProgress({ id: sleepChallenge.id, value: sleepDuration })).unwrap(); } catch (error) { logger.warn('SleepCard: 挑战进度上报失败', { error, challengeId: sleepChallenge.id }); } lastReportedRef.current = { date: dateKey, value: sleepDuration }; }; reportProgress(); }, [dispatch, joinedSleepChallenges, selectedDate, sleepDuration]); const CardContent = ( 睡眠 {loading ? '加载中...' : (sleepDuration != null ? formatSleepTime(sleepDuration) : '--')} ); return ( router.push(`/sleep-detail?date=${dayjs(selectedDate).format('YYYY-MM-DD')}`)} activeOpacity={0.7}> {CardContent} ); }; const styles = StyleSheet.create({ container: { // Container styles will be inherited from parent (FloatingCard) }, cardHeaderRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-start', }, titleIcon: { width: 16, height: 16, marginRight: 6, resizeMode: 'contain', }, cardTitle: { fontSize: 14, color: '#192126', fontWeight: '600', }, sleepValue: { fontSize: 16, color: '#1E40AF', fontWeight: '700', marginTop: 8, }, }); export default SleepCard;