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

BREAKING CHANGE: FitnessRingsCard组件API变更,移除手动传参改为自动获取数据
2025-09-19 14:16:11 +08:00

236 lines
7.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
};
}