feat: 集成健康数据功能
- 在项目中引入 react-native-health 库以获取健康数据 - 在 Explore 页面中添加步数和能量消耗的显示 - 实现页面聚焦时自动拉取今日健康数据 - 更新 iOS 权限设置以支持健康数据访问 - 添加健康数据相关的工具函数以简化数据获取
This commit is contained in:
@@ -2,8 +2,10 @@ import { ProgressBar } from '@/components/ProgressBar';
|
||||
import { Colors } from '@/constants/Colors';
|
||||
import { getTabBarBottomPadding } from '@/constants/TabBar';
|
||||
import { getMonthDaysZh, getMonthTitleZh, getTodayIndexInMonth } from '@/utils/date';
|
||||
import { ensureHealthPermissions, fetchTodayHealthData } from '@/utils/health';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
|
||||
import { useFocusEffect } from '@react-navigation/native';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
SafeAreaView,
|
||||
@@ -46,6 +48,30 @@ export default function ExploreScreen() {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [scrollWidth]);
|
||||
|
||||
// HealthKit: 每次页面聚焦都拉取今日数据
|
||||
const [stepCount, setStepCount] = useState<number | null>(null);
|
||||
const [activeCalories, setActiveCalories] = useState<number | null>(null);
|
||||
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
let isActive = true;
|
||||
const run = async () => {
|
||||
console.log('HealthKit init start');
|
||||
const ok = await ensureHealthPermissions();
|
||||
if (!ok) return;
|
||||
const data = await fetchTodayHealthData();
|
||||
if (!isActive) return;
|
||||
setStepCount(data.steps);
|
||||
setActiveCalories(Math.round(data.activeEnergyBurned));
|
||||
};
|
||||
run();
|
||||
return () => {
|
||||
isActive = false;
|
||||
};
|
||||
}, [])
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<SafeAreaView style={styles.safeArea}>
|
||||
@@ -100,15 +126,17 @@ export default function ExploreScreen() {
|
||||
<View style={styles.metricsRight}>
|
||||
<View style={[styles.metricsRightCard, styles.caloriesCard, { minHeight: 88 }]}>
|
||||
<Text style={styles.cardTitleSecondary}>消耗卡路里</Text>
|
||||
<Text style={styles.caloriesValue}>645 千卡</Text>
|
||||
<Text style={styles.caloriesValue}>
|
||||
{activeCalories != null ? `${activeCalories} 千卡` : '——'}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={[styles.metricsRightCard, styles.stepsCard, { minHeight: 88 }]}>
|
||||
<View style={styles.cardHeaderRow}>
|
||||
<View style={styles.iconSquare}><Ionicons name="footsteps-outline" size={18} color="#192126" /></View>
|
||||
<Text style={styles.cardTitle}>步数</Text>
|
||||
</View>
|
||||
<Text style={styles.stepsValue}>999/2000</Text>
|
||||
<ProgressBar progress={0.5} height={12} trackColor="#FFEBCB" fillColor="#FFC365" />
|
||||
<Text style={styles.stepsValue}>{stepCount != null ? `${stepCount}/2000` : '——/2000'}</Text>
|
||||
<ProgressBar progress={Math.min(1, Math.max(0, (stepCount ?? 0) / 2000))} height={12} trackColor="#FFEBCB" fillColor="#FFC365" />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
Reference in New Issue
Block a user