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

@@ -0,0 +1,236 @@
import { useState, useEffect, useCallback, useRef } from 'react';
import {
healthPermissionManager,
HealthPermissionStatus,
ensureHealthPermissions,
checkHealthPermissionStatus,
fetchTodayHealthData,
fetchHealthDataForDate,
TodayHealthData
} from '@/utils/health';
export interface UseHealthPermissionsReturn {
// 权限状态
permissionStatus: HealthPermissionStatus;
isLoading: boolean;
// 权限操作
requestPermissions: () => Promise<boolean>;
checkPermissions: (forceCheck?: boolean) => Promise<HealthPermissionStatus>;
// 数据刷新
refreshHealthData: () => Promise<void>;
refreshHealthDataForDate: (date: Date) => Promise<void>;
// 健康数据
healthData: TodayHealthData | null;
// 状态检查
hasPermission: boolean;
needsPermission: boolean;
}
/**
* HealthKit权限状态管理Hook
*
* 功能:
* 1. 监听权限状态变化
* 2. 自动刷新数据当权限状态改变时
* 3. 提供权限请求和数据刷新方法
* 4. 缓存健康数据状态
*/
export function useHealthPermissions(): UseHealthPermissionsReturn {
const [permissionStatus, setPermissionStatus] = useState<HealthPermissionStatus>(
healthPermissionManager.getPermissionStatus()
);
const [isLoading, setIsLoading] = useState(false);
const [healthData, setHealthData] = useState<TodayHealthData | null>(null);
// 使用ref避免闭包问题
const isLoadingRef = useRef(false);
const lastRefreshTime = useRef(0);
const refreshThrottle = 2000; // 2秒内避免重复刷新
// 刷新健康数据
const refreshHealthData = useCallback(async () => {
const now = Date.now();
// 防抖:避免短时间内重复刷新
if (isLoadingRef.current || (now - lastRefreshTime.current) < refreshThrottle) {
console.log('健康数据刷新被节流,跳过本次刷新');
return;
}
if (permissionStatus !== HealthPermissionStatus.Authorized) {
console.log('没有HealthKit权限跳过数据刷新');
return;
}
isLoadingRef.current = true;
setIsLoading(true);
lastRefreshTime.current = now;
try {
console.log('开始刷新今日健康数据...');
const data = await fetchTodayHealthData();
setHealthData(data);
console.log('健康数据刷新成功:', data);
} catch (error) {
console.error('刷新健康数据失败:', error);
} finally {
isLoadingRef.current = false;
setIsLoading(false);
}
}, [permissionStatus]);
// 刷新指定日期的健康数据
const refreshHealthDataForDate = useCallback(async (date: Date) => {
if (permissionStatus !== HealthPermissionStatus.Authorized) {
console.log('没有HealthKit权限跳过数据刷新');
return;
}
setIsLoading(true);
try {
console.log('开始刷新指定日期健康数据...', date);
const data = await fetchHealthDataForDate(date);
// 只有是今天的数据才更新state
const today = new Date();
if (date.toDateString() === today.toDateString()) {
setHealthData(data);
}
console.log('指定日期健康数据刷新成功:', data);
} catch (error) {
console.error('刷新指定日期健康数据失败:', error);
} finally {
setIsLoading(false);
}
}, [permissionStatus]);
// 请求权限
const requestPermissions = useCallback(async (): Promise<boolean> => {
setIsLoading(true);
try {
console.log('开始请求HealthKit权限...');
const granted = await ensureHealthPermissions();
if (granted) {
console.log('权限请求成功,准备刷新数据');
// 权限获取成功后,稍微延迟刷新数据
setTimeout(() => {
refreshHealthData();
}, 500);
}
return granted;
} catch (error) {
console.error('请求HealthKit权限失败:', error);
return false;
} finally {
setIsLoading(false);
}
}, [refreshHealthData]);
// 检查权限状态
const checkPermissions = useCallback(async (forceCheck: boolean = false): Promise<HealthPermissionStatus> => {
try {
const status = await checkHealthPermissionStatus(forceCheck);
return status;
} catch (error) {
console.error('检查权限状态失败:', error);
return HealthPermissionStatus.Unknown;
}
}, []);
// 监听权限状态变化
useEffect(() => {
console.log('设置HealthKit权限状态监听器...');
// 权限状态变化监听
const handlePermissionStatusChanged = (newStatus: HealthPermissionStatus, oldStatus: HealthPermissionStatus) => {
console.log(`权限状态变化: ${oldStatus} -> ${newStatus}`);
setPermissionStatus(newStatus);
// 如果从无权限变为有权限,自动刷新数据
if (oldStatus !== HealthPermissionStatus.Authorized && newStatus === HealthPermissionStatus.Authorized) {
console.log('权限状态变为已授权,准备刷新健康数据...');
setTimeout(() => {
refreshHealthData();
}, 500);
}
};
// 权限获取成功监听
const handlePermissionGranted = () => {
console.log('权限获取成功事件触发,准备刷新数据...');
setTimeout(() => {
refreshHealthData();
}, 500);
};
healthPermissionManager.on('permissionStatusChanged', handlePermissionStatusChanged);
healthPermissionManager.on('permissionGranted', handlePermissionGranted);
// 组件挂载时检查一次权限状态
checkPermissions(true);
// 如果已经有权限,立即刷新数据
if (permissionStatus === HealthPermissionStatus.Authorized) {
refreshHealthData();
}
return () => {
console.log('清理HealthKit权限状态监听器...');
healthPermissionManager.off('permissionStatusChanged', handlePermissionStatusChanged);
healthPermissionManager.off('permissionGranted', handlePermissionGranted);
};
}, [checkPermissions, refreshHealthData, permissionStatus]);
// 计算派生状态
const hasPermission = permissionStatus === HealthPermissionStatus.Authorized;
const needsPermission = permissionStatus === HealthPermissionStatus.NotDetermined ||
permissionStatus === HealthPermissionStatus.Unknown;
return {
permissionStatus,
isLoading,
requestPermissions,
checkPermissions,
refreshHealthData,
refreshHealthDataForDate,
healthData,
hasPermission,
needsPermission
};
}
/**
* 简化版Hook只关注权限状态
*/
export function useHealthPermissionStatus() {
const [permissionStatus, setPermissionStatus] = useState<HealthPermissionStatus>(
healthPermissionManager.getPermissionStatus()
);
useEffect(() => {
const handlePermissionStatusChanged = (newStatus: HealthPermissionStatus) => {
setPermissionStatus(newStatus);
};
healthPermissionManager.on('permissionStatusChanged', handlePermissionStatusChanged);
// 检查一次当前状态
checkHealthPermissionStatus(true);
return () => {
healthPermissionManager.off('permissionStatusChanged', handlePermissionStatusChanged);
};
}, []);
return {
permissionStatus,
hasPermission: permissionStatus === HealthPermissionStatus.Authorized,
needsPermission: permissionStatus === HealthPermissionStatus.NotDetermined ||
permissionStatus === HealthPermissionStatus.Unknown
};
}