import { FastingPlan } from '@/constants/Fasting'; import { ensureFastingNotificationsReady, resyncFastingNotifications, verifyFastingNotifications, } from '@/services/fastingNotifications'; import { FastingSchedule } from '@/store/fastingSlice'; import { FastingNotificationIds, loadStoredFastingNotificationIds } from '@/utils/fasting'; import { useCallback, useEffect, useRef, useState } from 'react'; export interface UseFastingNotificationsState { isReady: boolean; isLoading: boolean; error: string | null; notificationIds: FastingNotificationIds; lastSyncTime: Date | null; } export interface UseFastingNotificationsActions { verifyAndSync: () => Promise; forceSync: () => Promise; clearError: () => void; } export const useFastingNotifications = ( schedule: FastingSchedule | null, plan: FastingPlan | undefined ): UseFastingNotificationsState & UseFastingNotificationsActions => { const [state, setState] = useState({ isReady: false, isLoading: true, error: null, notificationIds: {}, lastSyncTime: null, }); const isInitializedRef = useRef(false); const notificationIdsRef = useRef({}); const isSyncingRef = useRef(false); // 初始化通知系统 const initialize = useCallback(async () => { if (isInitializedRef.current) return; try { setState(prev => ({ ...prev, isLoading: true, error: null })); // 1. 检查通知权限 const ready = await ensureFastingNotificationsReady(); if (!ready) { setState(prev => ({ ...prev, isReady: false, isLoading: false, error: '通知权限未授予', })); return; } // 2. 加载存储的通知ID const storedIds = await loadStoredFastingNotificationIds(); notificationIdsRef.current = storedIds; setState(prev => ({ ...prev, isReady: true, isLoading: false, notificationIds: storedIds, })); isInitializedRef.current = true; } catch (error) { console.error('初始化断食通知失败', error); setState(prev => ({ ...prev, isReady: false, isLoading: false, error: error instanceof Error ? error.message : '初始化失败', })); } }, []); // 验证和同步通知 const verifyAndSync = useCallback(async () => { if (!state.isReady || isSyncingRef.current) return; try { isSyncingRef.current = true; setState(prev => ({ ...prev, error: null })); const { isValid, updatedIds } = await verifyFastingNotifications({ schedule, plan, storedIds: notificationIdsRef.current, }); notificationIdsRef.current = updatedIds; setState(prev => ({ ...prev, notificationIds: updatedIds, lastSyncTime: new Date(), })); if (!isValid) { console.log('断食通知已重新同步'); } } catch (error) { console.error('验证断食通知失败', error); setState(prev => ({ ...prev, error: error instanceof Error ? error.message : '验证失败', })); // 验证失败时不立即强制同步,避免重复调用 // forceSync 会在用户点击重试按钮时调用 } finally { isSyncingRef.current = false; } }, [state.isReady, schedule, plan]); // 强制同步通知 const forceSync = useCallback(async () => { if (!state.isReady || isSyncingRef.current) return; try { isSyncingRef.current = true; setState(prev => ({ ...prev, error: null })); const nextIds = await resyncFastingNotifications({ schedule, plan, previousIds: notificationIdsRef.current, enabled: true, }); notificationIdsRef.current = nextIds; setState(prev => ({ ...prev, notificationIds: nextIds, lastSyncTime: new Date(), })); console.log('断食通知已强制同步', { schedule: schedule?.startISO, plan: plan?.id, notificationIds: nextIds, }); } catch (error) { console.error('强制同步断食通知失败', error); setState(prev => ({ ...prev, error: error instanceof Error ? error.message : '同步失败', })); } finally { isSyncingRef.current = false; } }, [state.isReady, schedule, plan]); // 清除错误 const clearError = useCallback(() => { setState(prev => ({ ...prev, error: null })); }, []); // 初始化 useEffect(() => { initialize(); }, [initialize]); // 当计划或方案变化时验证和同步 useEffect(() => { if (state.isReady) { verifyAndSync(); } }, [state.isReady, schedule?.startISO, schedule?.endISO, plan?.id, verifyAndSync]); return { ...state, verifyAndSync, forceSync, clearError, }; };