feat(i18n): Add common translations and mood-related strings in English and Chinese fix(i18n): Update metabolism titles for consistency in health translations chore: Update Podfile.lock to include SDWebImage 5.21.4 and other dependency versions refactor(moodCheckins): Improve mood configuration retrieval with optional translation support refactor(sleepHealthKit): Replace useI18n with direct i18n import for sleep quality descriptions
203 lines
5.5 KiB
TypeScript
203 lines
5.5 KiB
TypeScript
import { useI18n } from '@/hooks/useI18n';
|
|
import { MoodCheckin, getMoodConfig } from '@/services/moodCheckins';
|
|
import dayjs from 'dayjs';
|
|
import React from 'react';
|
|
import { StyleSheet, Text, View } from 'react-native';
|
|
|
|
interface MoodHistoryCardProps {
|
|
moodCheckins: MoodCheckin[];
|
|
title?: string;
|
|
}
|
|
|
|
export function MoodHistoryCard({ moodCheckins, title }: MoodHistoryCardProps) {
|
|
const { t } = useI18n();
|
|
const defaultTitle = t('mood.history.title');
|
|
// 计算心情统计
|
|
const moodStats = React.useMemo(() => {
|
|
const stats = {
|
|
total: moodCheckins.length,
|
|
averageIntensity: 0,
|
|
moodDistribution: {} as Record<string, number>,
|
|
mostFrequentMood: '',
|
|
};
|
|
|
|
if (moodCheckins.length === 0) return stats;
|
|
|
|
// 计算平均强度
|
|
const totalIntensity = moodCheckins.reduce((sum, checkin) => sum + checkin.intensity, 0);
|
|
stats.averageIntensity = Math.round(totalIntensity / moodCheckins.length);
|
|
|
|
// 计算心情分布
|
|
moodCheckins.forEach(checkin => {
|
|
const moodLabel = getMoodConfig(checkin.moodType, t)?.label || checkin.moodType;
|
|
stats.moodDistribution[moodLabel] = (stats.moodDistribution[moodLabel] || 0) + 1;
|
|
});
|
|
|
|
// 找出最频繁的心情
|
|
const sortedMoods = Object.entries(stats.moodDistribution)
|
|
.sort(([, a], [, b]) => b - a);
|
|
stats.mostFrequentMood = sortedMoods[0]?.[0] || '';
|
|
|
|
return stats;
|
|
}, [moodCheckins]);
|
|
|
|
// 获取最近的心情记录
|
|
const recentMoods = moodCheckins
|
|
.sort((a, b) => dayjs(b.createdAt).valueOf() - dayjs(a.createdAt).valueOf())
|
|
.slice(0, 5);
|
|
|
|
return (
|
|
<View style={styles.container}>
|
|
<Text style={styles.title}>{title || defaultTitle}</Text>
|
|
|
|
{moodCheckins.length === 0 ? (
|
|
<View style={styles.emptyState}>
|
|
<Text style={styles.emptyText}>{t('mood.history.noRecords')}</Text>
|
|
</View>
|
|
) : (
|
|
<>
|
|
{/* 统计信息 */}
|
|
<View style={styles.statsContainer}>
|
|
<View style={styles.statItem}>
|
|
<Text style={styles.statValue}>{moodStats.total}</Text>
|
|
<Text style={styles.statLabel}>{t('mood.history.totalRecords')}</Text>
|
|
</View>
|
|
<View style={styles.statItem}>
|
|
<Text style={styles.statValue}>{moodStats.averageIntensity}</Text>
|
|
<Text style={styles.statLabel}>{t('mood.history.averageIntensity')}</Text>
|
|
</View>
|
|
<View style={styles.statItem}>
|
|
<Text style={styles.statValue}>{moodStats.mostFrequentMood}</Text>
|
|
<Text style={styles.statLabel}>{t('mood.history.mostFrequent')}</Text>
|
|
</View>
|
|
</View>
|
|
|
|
{/* 最近记录 */}
|
|
<View style={styles.recentContainer}>
|
|
<Text style={styles.sectionTitle}>{t('mood.history.recentRecords')}</Text>
|
|
{recentMoods.map((checkin, index) => {
|
|
const moodConfig = getMoodConfig(checkin.moodType, t);
|
|
return (
|
|
<View key={checkin.id} style={styles.moodItem}>
|
|
<View style={styles.moodInfo}>
|
|
<Text style={styles.moodEmoji}>😊</Text>
|
|
<View style={styles.moodDetails}>
|
|
<Text style={styles.moodLabel}>{moodConfig?.label}</Text>
|
|
<Text style={styles.moodDate}>
|
|
{dayjs(checkin.createdAt).format(t('mood.history.dateTimeFormat'))}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
<View style={styles.moodIntensity}>
|
|
<Text style={styles.intensityText}>{t('mood.history.intensity')} {checkin.intensity}</Text>
|
|
</View>
|
|
</View>
|
|
);
|
|
})}
|
|
</View>
|
|
</>
|
|
)}
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
backgroundColor: '#FFFFFF',
|
|
borderRadius: 16,
|
|
padding: 16,
|
|
marginBottom: 16,
|
|
shadowColor: '#000',
|
|
shadowOffset: {
|
|
width: 0,
|
|
height: 2,
|
|
},
|
|
shadowOpacity: 0.1,
|
|
shadowRadius: 3.84,
|
|
elevation: 5,
|
|
},
|
|
title: {
|
|
fontSize: 18,
|
|
fontWeight: '600',
|
|
color: '#192126',
|
|
marginBottom: 16,
|
|
},
|
|
emptyState: {
|
|
alignItems: 'center',
|
|
paddingVertical: 32,
|
|
},
|
|
emptyText: {
|
|
fontSize: 14,
|
|
color: '#9CA3AF',
|
|
fontStyle: 'italic',
|
|
},
|
|
statsContainer: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-around',
|
|
marginBottom: 20,
|
|
paddingVertical: 16,
|
|
backgroundColor: '#F8F9FA',
|
|
borderRadius: 12,
|
|
},
|
|
statItem: {
|
|
alignItems: 'center',
|
|
},
|
|
statValue: {
|
|
fontSize: 20,
|
|
fontWeight: '700',
|
|
color: '#192126',
|
|
marginBottom: 4,
|
|
},
|
|
statLabel: {
|
|
fontSize: 12,
|
|
color: '#6B7280',
|
|
},
|
|
recentContainer: {
|
|
marginTop: 8,
|
|
},
|
|
sectionTitle: {
|
|
fontSize: 16,
|
|
fontWeight: '600',
|
|
color: '#192126',
|
|
marginBottom: 12,
|
|
},
|
|
moodItem: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
paddingVertical: 8,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: '#F3F4F6',
|
|
},
|
|
moodInfo: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
flex: 1,
|
|
},
|
|
moodEmoji: {
|
|
fontSize: 20,
|
|
marginRight: 12,
|
|
},
|
|
moodDetails: {
|
|
flex: 1,
|
|
},
|
|
moodLabel: {
|
|
fontSize: 14,
|
|
fontWeight: '500',
|
|
color: '#192126',
|
|
marginBottom: 2,
|
|
},
|
|
moodDate: {
|
|
fontSize: 12,
|
|
color: '#6B7280',
|
|
},
|
|
moodIntensity: {
|
|
alignItems: 'flex-end',
|
|
},
|
|
intensityText: {
|
|
fontSize: 12,
|
|
color: '#6B7280',
|
|
fontWeight: '500',
|
|
},
|
|
});
|