import dayjs from 'dayjs'; import { FastingPlan } from '@/constants/Fasting'; import { FastingSchedule } from '@/store/fastingSlice'; import { clearFastingNotificationIds, getFastingNotificationsRegistered, loadStoredFastingNotificationIds, saveFastingNotificationIds, setFastingNotificationsRegistered, FastingNotificationIds, } from '@/utils/fasting'; import { getNotificationEnabled } from '@/utils/userPreferences'; import { notificationService, NotificationTypes } from './notifications'; const REMINDER_OFFSET_MINUTES = 10; const cancelNotificationIds = async (ids?: FastingNotificationIds) => { if (!ids) return; const { startId, endId } = ids; try { if (startId) { await notificationService.cancelNotification(startId); } } catch (error) { console.warn('取消断食开始提醒失败', error); } try { if (endId) { await notificationService.cancelNotification(endId); } } catch (error) { console.warn('取消断食结束提醒失败', error); } }; export const ensureFastingNotificationsReady = async (): Promise => { try { const notificationsEnabled = await getNotificationEnabled(); if (!notificationsEnabled) { return false; } const registered = await getFastingNotificationsRegistered(); if (registered) { return true; } const status = await notificationService.getPermissionStatus(); if (status !== 'granted') { const requestStatus = await notificationService.requestPermission(); if (requestStatus !== 'granted') { return false; } } await setFastingNotificationsRegistered(true); return true; } catch (error) { console.warn('初始化断食通知失败', error); return false; } }; type ResyncOptions = { schedule: FastingSchedule | null; plan?: FastingPlan; previousIds?: FastingNotificationIds; enabled: boolean; }; export const resyncFastingNotifications = async ({ schedule, plan, previousIds, enabled, }: ResyncOptions): Promise => { const storedIds = previousIds ?? (await loadStoredFastingNotificationIds()); await cancelNotificationIds(storedIds); if (!enabled) { await setFastingNotificationsRegistered(false); await clearFastingNotificationIds(); return {}; } const preferenceEnabled = await getNotificationEnabled(); if (!preferenceEnabled) { await setFastingNotificationsRegistered(false); await clearFastingNotificationIds(); return {}; } if (!schedule || !plan) { await clearFastingNotificationIds(); return {}; } const now = dayjs(); const start = dayjs(schedule.startISO); const end = dayjs(schedule.endISO); if (end.isBefore(now)) { await clearFastingNotificationIds(); return {}; } const notificationIds: FastingNotificationIds = {}; if (start.isAfter(now)) { const preStart = start.subtract(REMINDER_OFFSET_MINUTES, 'minute'); const triggerMoment = preStart.isAfter(now) ? preStart : start; try { const startId = await notificationService.scheduleNotificationAtDate( { title: `${plan.title} 即将开始`, body: preStart.isAfter(now) ? `还有 ${REMINDER_OFFSET_MINUTES} 分钟就要进入断食窗口,喝一杯温水,准备迎接更轻盈的自己!` : `现在开始 ${plan.title},放下零食,给身体一次真正的休息时间。`, data: { type: NotificationTypes.FASTING_START, planId: plan.id, }, sound: true, priority: 'high', }, triggerMoment.toDate() ); notificationIds.startId = startId; } catch (error) { console.error('安排断食开始通知失败', error); } } if (end.isAfter(now)) { try { const endId = await notificationService.scheduleNotificationAtDate( { title: '补能时刻到啦', body: `${plan.title} 已完成!用一顿高蛋白 + 低GI 的餐食奖励自己,让代谢持续高效运转。`, data: { type: NotificationTypes.FASTING_END, planId: plan.id, }, sound: true, priority: 'high', }, end.toDate() ); notificationIds.endId = endId; } catch (error) { console.error('安排断食结束通知失败', error); } } await saveFastingNotificationIds(notificationIds); return notificationIds; }; export type { FastingNotificationIds };