feat(health): 完善HealthKit权限管理和数据获取系统

- 重构权限管理,新增SimpleEventEmitter实现状态监听
- 实现完整的健身圆环数据获取(活动热量、锻炼时间、站立小时)
- 优化组件状态管理,支持实时数据刷新和权限状态响应
- 新增useHealthPermissions Hook,简化权限状态管理
- 完善iOS原生代码,支持按小时统计健身数据
- 优化应用启动时权限初始化流程,避免启动弹窗

BREAKING CHANGE: FitnessRingsCard组件API变更,移除手动传参改为自动获取数据
This commit is contained in:
richarjiang
2025-09-19 14:16:11 +08:00
parent 184fb672b7
commit ccfccca7bc
11 changed files with 1044 additions and 360 deletions

View File

@@ -221,9 +221,7 @@ export default function PersonalScreen() {
<Button
variant='default'
onPress={() => {
console.log(111111);
// pushIfAuthedElseLogin('/profile/edit')
pushIfAuthedElseLogin('/profile/edit')
}}
modifiers={[
frame({
@@ -237,10 +235,10 @@ export default function PersonalScreen() {
}
})
]} >
<SwiftText size={14} color='black' weight={'medium'}></SwiftText>
<SwiftText size={14} color='black' weight={'medium'}>{isLoggedIn ? '编辑' : '登录'}</SwiftText>
</Button>
</Host> : <TouchableOpacity style={styles.editButton} onPress={() => pushIfAuthedElseLogin('/profile/edit')}>
<Text style={styles.editButtonText}></Text>
<Text style={styles.editButtonText}>{isLoggedIn ? '编辑' : '登录'}</Text>
</TouchableOpacity>}

View File

@@ -95,28 +95,6 @@ export default function ExploreScreen() {
const oxygenSaturation = useMockData ? (mockData?.oxygenSaturation ?? null) : (healthData?.oxygenSaturation ?? null);
const fitnessRingsData = useMockData ? {
activeCalories: mockData?.activeCalories ?? 0,
activeCaloriesGoal: mockData?.activeCaloriesGoal ?? 350,
exerciseMinutes: mockData?.exerciseMinutes ?? 0,
exerciseMinutesGoal: mockData?.exerciseMinutesGoal ?? 30,
standHours: mockData?.standHours ?? 0,
standHoursGoal: mockData?.standHoursGoal ?? 12,
} : (healthData ? {
activeCalories: healthData.activeEnergyBurned,
activeCaloriesGoal: healthData.activeCaloriesGoal,
exerciseMinutes: healthData.exerciseMinutes,
exerciseMinutesGoal: healthData.exerciseMinutesGoal,
standHours: healthData.standHours,
standHoursGoal: healthData.standHoursGoal,
} : {
activeCalories: 0,
activeCaloriesGoal: 350,
exerciseMinutes: 0,
exerciseMinutesGoal: 30,
standHours: 0,
standHoursGoal: 12,
});
// 用于触发动画重置的 token当日期或数据变化时更新
const [animToken, setAnimToken] = useState(0);
@@ -564,7 +542,6 @@ export default function ExploreScreen() {
<FloatingCard style={styles.masonryCard}>
<SleepCard
selectedDate={currentSelectedDate}
onPress={() => pushIfAuthedElseLogin(`/sleep-detail?date=${dayjs(currentSelectedDate).format('YYYY-MM-DD')}`)}
/>
</FloatingCard>
</View>
@@ -573,12 +550,7 @@ export default function ExploreScreen() {
<View style={styles.masonryColumn}>
<FloatingCard style={styles.masonryCard} delay={250}>
<FitnessRingsCard
activeCalories={fitnessRingsData.activeCalories}
activeCaloriesGoal={fitnessRingsData.activeCaloriesGoal}
exerciseMinutes={fitnessRingsData.exerciseMinutes}
exerciseMinutesGoal={fitnessRingsData.exerciseMinutesGoal}
standHours={fitnessRingsData.standHours}
standHoursGoal={fitnessRingsData.standHoursGoal}
selectedDate={currentSelectedDate}
resetToken={animToken}
/>
</FloatingCard>

View File

@@ -16,7 +16,7 @@ import { WaterRecordSource } from '@/services/waterRecords';
import { store } from '@/store';
import { rehydrateUserSync, setPrivacyAgreed } from '@/store/userSlice';
import { createWaterRecordAction } from '@/store/waterSlice';
import { ensureHealthPermissions } from '@/utils/health';
import { ensureHealthPermissions, initializeHealthPermissions } from '@/utils/health';
import { DailySummaryNotificationHelpers, MoodNotificationHelpers, NutritionNotificationHelpers } from '@/utils/notificationHelpers';
import { clearPendingWaterRecords, syncPendingWidgetChanges } from '@/utils/widgetDataSync';
import React from 'react';
@@ -44,13 +44,24 @@ function Bootstrapper({ children }: { children: React.ReactNode }) {
};
const initHealthPermissions = async () => {
// 初始化 HealthKit 权限
// 初始化 HealthKit 权限管理系统
try {
console.log('开始请求 HealthKit 权限...');
await ensureHealthPermissions();
console.log('HealthKit 权限初始化完成');
console.log('初始化 HealthKit 权限管理系统...');
initializeHealthPermissions();
// 延迟请求权限,避免应用启动时弹窗
setTimeout(async () => {
try {
await ensureHealthPermissions();
console.log('HealthKit 权限请求完成');
} catch (error) {
console.warn('HealthKit 权限请求失败,可能在模拟器上运行:', error);
}
}, 2000);
console.log('HealthKit 权限管理初始化完成');
} catch (error) {
console.warn('HealthKit 权限初始化失败,可能在模拟器上运行:', error);
console.warn('HealthKit 权限管理初始化失败:', error);
}
}