Files
digital-pilates/hooks/useFastingNotifications.ts
richarjiang 635d835a50 feat(fasting): 完善断食通知系统并优化错误提示
在应用启动时添加断食通知初始化逻辑,改进错误消息提示,并新增后台任务支持断食通知同步。同时优化挑战加入后的数据刷新流程和会员卡片显示样式。

主要更改:
- 添加断食通知启动检测和初始化
- 改进断食通知错误消息,提供更详细的用户指导
- 新增断食通知后台任务处理
- 优化挑战加入后自动刷新详情和排名数据
- 调整会员价格字体大小以提升视觉效果
2025-11-03 14:13:49 +08:00

184 lines
5.1 KiB
TypeScript

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<void>;
forceSync: () => Promise<void>;
clearError: () => void;
}
export const useFastingNotifications = (
schedule: FastingSchedule | null,
plan: FastingPlan | undefined
): UseFastingNotificationsState & UseFastingNotificationsActions => {
const [state, setState] = useState<UseFastingNotificationsState>({
isReady: false,
isLoading: true,
error: null,
notificationIds: {},
lastSyncTime: null,
});
const isInitializedRef = useRef(false);
const notificationIdsRef = useRef<FastingNotificationIds>({});
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,
};
};