feat(notifications): 新增晚餐和心情提醒功能,支持HRV压力检测和后台处理

- 新增晚餐提醒(18:00)和心情提醒(21:00)的定时通知
- 实现基于HRV数据的压力检测和智能鼓励通知
- 添加后台任务处理支持,修改iOS后台模式为processing
- 优化营养记录页面使用Redux状态管理,支持实时数据更新
- 重构卡路里计算公式,移除目标卡路里概念,改为基代+运动-饮食
- 新增营养目标动态计算功能,基于用户身体数据智能推荐
- 完善通知点击跳转逻辑,支持多种提醒类型的路由处理
This commit is contained in:
richarjiang
2025-09-01 10:29:13 +08:00
parent fe634ba258
commit a34ca556e8
12 changed files with 867 additions and 189 deletions

View File

@@ -1,5 +1,6 @@
import { ThemedText } from '@/components/ThemedText';
import { useThemeColor } from '@/hooks/useThemeColor';
import React, { useEffect, useRef } from 'react';
import { Animated, StyleSheet, View } from 'react-native';
import Svg, { Circle } from 'react-native-svg';
@@ -17,6 +18,7 @@ export type CalorieRingChartProps = {
proteinGoal: number;
fatGoal: number;
carbsGoal: number;
};
export function CalorieRingChart({
@@ -30,6 +32,7 @@ export function CalorieRingChart({
proteinGoal,
fatGoal,
carbsGoal,
}: CalorieRingChartProps) {
const surfaceColor = useThemeColor({}, 'surface');
const textColor = useThemeColor({}, 'text');
@@ -38,12 +41,12 @@ export function CalorieRingChart({
// 动画值
const animatedProgress = useRef(new Animated.Value(0)).current;
// 计算还能吃多少卡路里
const remainingCalories = metabolism + exercise - consumed - goal;
// 计算还能吃卡路里:代谢 + 运动 - 饮食
const remainingCalories = metabolism + exercise - consumed;
const canEat = Math.max(0, remainingCalories);
// 计算进度百分比 (用于圆环显示)
const totalAvailable = metabolism + exercise - goal;
const totalAvailable = metabolism + exercise;
const progressPercentage = totalAvailable > 0 ? Math.min((consumed / totalAvailable) * 100, 100) : 0;
// 圆环参数 - 更小的圆环以适应布局
@@ -74,7 +77,7 @@ export function CalorieRingChart({
{/* 左上角公式展示 */}
<View style={styles.formulaContainer}>
<ThemedText style={[styles.formulaText, { color: textSecondaryColor }]}>
= + - -
= + -
</ThemedText>
</View>
@@ -113,7 +116,7 @@ export function CalorieRingChart({
</ThemedText>
<ThemedText style={[styles.centerValue, { color: textColor }]}>
{canEat.toLocaleString()}
{canEat.toFixed(1)}
</ThemedText>
<ThemedText style={[styles.centerPercentage, { color: textSecondaryColor }]}>
{Math.round(progressPercentage)}%
@@ -127,30 +130,25 @@ export function CalorieRingChart({
<View style={styles.dataItem}>
<ThemedText style={[styles.dataLabel, { color: textSecondaryColor }]}></ThemedText>
<ThemedText style={[styles.dataValue, { color: textColor }]}>
{metabolism.toLocaleString()}
{Math.round(metabolism)}
</ThemedText>
</View>
<View style={styles.dataItem}>
<ThemedText style={[styles.dataLabel, { color: textSecondaryColor }]}></ThemedText>
<ThemedText style={[styles.dataValue, { color: textColor }]}>
{exercise}
{Math.round(exercise)}
</ThemedText>
</View>
<View style={styles.dataItem}>
<ThemedText style={[styles.dataLabel, { color: textSecondaryColor }]}></ThemedText>
<ThemedText style={[styles.dataValue, { color: textColor }]}>
{consumed}
{Math.round(consumed)}
</ThemedText>
</View>
<View style={styles.dataItem}>
<ThemedText style={[styles.dataLabel, { color: textSecondaryColor }]}></ThemedText>
<ThemedText style={[styles.dataValue, { color: textColor }]}>
{goal}
</ThemedText>
</View>
</View>
</View>
@@ -161,7 +159,7 @@ export function CalorieRingChart({
</ThemedText>
<ThemedText style={[styles.nutritionValue, { color: textColor }]}>
{protein.toFixed(2)}/{proteinGoal.toFixed(2)}g
{Math.round(protein)}/{Math.round(proteinGoal)}g
</ThemedText>
</View>
@@ -170,7 +168,7 @@ export function CalorieRingChart({
</ThemedText>
<ThemedText style={[styles.nutritionValue, { color: textColor }]}>
{fat.toFixed(2)}/{fatGoal.toFixed(2)}g
{Math.round(fat)}/{Math.round(fatGoal)}g
</ThemedText>
</View>
@@ -179,7 +177,7 @@ export function CalorieRingChart({
</ThemedText>
<ThemedText style={[styles.nutritionValue, { color: textColor }]}>
{carbs.toFixed(2)}/{carbsGoal.toFixed(2)}g
{Math.round(carbs)}/{Math.round(carbsGoal)}g
</ThemedText>
</View>
</View>