feat(fasting): 完善断食通知系统并优化错误提示

在应用启动时添加断食通知初始化逻辑,改进错误消息提示,并新增后台任务支持断食通知同步。同时优化挑战加入后的数据刷新流程和会员卡片显示样式。

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

View File

@@ -154,6 +154,17 @@ function Bootstrapper({ children }: { children: React.ReactNode }) {
await WaterNotificationHelpers.scheduleRegularWaterReminders(profile.name || '用户');
logger.info('默认喝水提醒已注册');
// 安排断食通知(如果存在活跃的断食计划)
try {
const fastingSchedule = store.getState().fasting.activeSchedule;
if (fastingSchedule) {
const fastingPlan = store.getState().fasting.activeSchedule ? null : null;
// 断食通知将通过 useFastingNotifications hook 在页面加载时自动安排
logger.info('检测到活跃的断食计划,将通过页面 hook 自动安排通知');
}
} catch (error) {
logger.warn('安排断食通知失败:', error);
}
// 初始化快捷动作
await setupQuickActions();

View File

@@ -206,7 +206,9 @@ export default function ChallengeDetailScreen() {
}
try {
await dispatch(joinChallenge(id));
await dispatch(joinChallenge(id)).unwrap();
await dispatch(fetchChallengeDetail(id)).unwrap();
await dispatch(fetchChallengeRankings({ id }));
setShowCelebration(true)
} catch (error) {
Toast.error('加入挑战失败')

View File

@@ -1232,7 +1232,7 @@ const styles = StyleSheet.create({
color: '#241F1F',
},
planCardPrice: {
fontSize: 22,
fontSize: 16,
fontWeight: '700',
marginTop: 12,
},

View File

@@ -52,7 +52,7 @@ export const useFastingNotifications = (
...prev,
isReady: false,
isLoading: false,
error: '通知权限未授予',
error: '通知权限未开启。请前往"个人"页面开启消息推送,或检查系统通知权限设置。',
}));
return;
}
@@ -109,7 +109,7 @@ export const useFastingNotifications = (
console.error('验证断食通知失败', error);
setState(prev => ({
...prev,
error: error instanceof Error ? error.message : '验证失败',
error: '同步断食通知失败:' + (error instanceof Error ? error.message : '未知错误') + '。请点击"重试"按钮,或检查推送权限设置。',
}));
// 验证失败时不立即强制同步,避免重复调用
@@ -151,7 +151,7 @@ export const useFastingNotifications = (
console.error('强制同步断食通知失败', error);
setState(prev => ({
...prev,
error: error instanceof Error ? error.message : '同步失败',
error: '强制同步断食通知失败:' + (error instanceof Error ? error.message : '未知错误') + '。请点击"重试"按钮,或检查推送权限设置。',
}));
} finally {
isSyncingRef.current = false;

View File

@@ -5,6 +5,8 @@ import AsyncStorage from '@/utils/kvStore';
import { log } from '@/utils/logger';
import { ChallengeNotificationHelpers, WaterNotificationHelpers } from '@/utils/notificationHelpers';
import { getWaterGoalFromStorage } from '@/utils/userPreferences';
import { resyncFastingNotifications } from '@/services/fastingNotifications';
import { selectActiveFastingSchedule, selectActiveFastingPlan } from '@/store/fastingSlice';
import dayjs from 'dayjs';
import * as BackgroundTask from 'expo-background-task';
import * as TaskManager from 'expo-task-manager';
@@ -191,6 +193,54 @@ async function executeChallengeReminderTask(): Promise<void> {
}
}
// 执行断食通知后台任务
async function executeFastingNotificationTask(): Promise<void> {
try {
console.log('执行断食通知后台任务...');
let state;
try {
state = store.getState();
} catch (error) {
console.log('无法获取 Redux state跳过断食通知任务:', error);
return;
}
const activeSchedule = selectActiveFastingSchedule(state);
const activePlan = selectActiveFastingPlan(state);
if (!activeSchedule || !activePlan) {
console.log('没有激活的断食计划,跳过断食通知任务');
return;
}
// 检查断食计划是否已结束
const end = dayjs(activeSchedule.endISO);
if (end.isBefore(dayjs())) {
console.log('断食计划已结束,跳过断食通知任务');
return;
}
console.log('正在同步断食通知...', {
planId: activePlan.id,
start: activeSchedule.startISO,
end: activeSchedule.endISO,
});
// 重新同步断食通知
await resyncFastingNotifications({
schedule: activeSchedule,
plan: activePlan,
enabled: true,
});
console.log('断食通知后台同步完成');
} catch (error) {
console.error('执行断食通知后台任务失败:', error);
}
}
// 发送测试通知以验证后台任务执行
async function sendTestNotification(): Promise<void> {
try {
@@ -286,6 +336,9 @@ async function executeBackgroundTasks(): Promise<void> {
// 执行挑战鼓励提醒任务
await executeChallengeReminderTask();
// 执行断食通知任务
await executeFastingNotificationTask();
console.log('后台任务执行完成');
} catch (error) {
console.error('执行后台任务失败:', error);
@@ -318,7 +371,7 @@ export class BackgroundTaskManager {
try {
// 定义后台任务
TaskManager.defineTask(BACKGROUND_TASK_IDENTIFIER, async (body: TaskManagerTaskBody) => {
TaskManager.defineTask(BACKGROUND_TASK_IDENTIFIER, async (_body: TaskManagerTaskBody) => {
try {
log.info(`[BackgroundTask] 后台任务执行, 任务 ID: ${BACKGROUND_TASK_IDENTIFIER}`);
await executeBackgroundTasks();