import '@/i18n';
import { DefaultTheme, ThemeProvider } from '@react-navigation/native';
import { useFonts } from 'expo-font';
import { Stack } from 'expo-router';
import { StatusBar } from 'expo-status-bar';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import 'react-native-reanimated';
import PrivacyConsentModal from '@/components/PrivacyConsentModal';
import { useAppDispatch, useAppSelector } from '@/hooks/redux';
import { useQuickActions } from '@/hooks/useQuickActions';
import { clearAiCoachSessionCache } from '@/services/aiCoachSession';
import { notificationService } from '@/services/notifications';
import { setupQuickActions } from '@/services/quickActions';
import { sleepMonitorService } from '@/services/sleepMonitor';
import { initializeWaterRecordBridge } from '@/services/waterRecordBridge';
import { WaterRecordSource } from '@/services/waterRecords';
import { workoutMonitorService } from '@/services/workoutMonitor';
import { store } from '@/store';
import { hydrateActiveSchedule, selectActiveFastingSchedule } from '@/store/fastingSlice';
import { fetchMyProfile, setPrivacyAgreed } from '@/store/userSlice';
import { createWaterRecordAction } from '@/store/waterSlice';
import { loadActiveFastingSchedule } from '@/utils/fasting';
import { ensureHealthPermissions, initializeHealthPermissions } from '@/utils/health';
import { MoodNotificationHelpers, NutritionNotificationHelpers, WaterNotificationHelpers } from '@/utils/notificationHelpers';
import { clearPendingWaterRecords, syncPendingWidgetChanges } from '@/utils/widgetDataSync';
import React, { useEffect } from 'react';
import { DialogProvider } from '@/components/ui/DialogProvider';
import { MembershipModalProvider } from '@/contexts/MembershipModalContext';
import { ToastProvider } from '@/contexts/ToastContext';
import { useAuthGuard } from '@/hooks/useAuthGuard';
import { STORAGE_KEYS } from '@/services/api';
import { BackgroundTaskManager } from '@/services/backgroundTaskManagerV2';
import { fetchChallenges } from '@/store/challengesSlice';
import AsyncStorage from '@/utils/kvStore';
import { logger } from '@/utils/logger';
import { Provider } from 'react-redux';
// 在开发环境中导入调试工具
let BackgroundTaskDebugger: any = null;
if (__DEV__) {
try {
const debuggerModule = require('@/services/backgroundTaskDebugger');
BackgroundTaskDebugger = debuggerModule.BackgroundTaskDebugger;
} catch (error) {
logger.warn('无法导入后台任务调试工具:', error);
}
}
function Bootstrapper({ children }: { children: React.ReactNode }) {
const dispatch = useAppDispatch();
const { profile, onboardingCompleted } = useAppSelector((state) => state.user);
const activeFastingSchedule = useAppSelector(selectActiveFastingSchedule);
const [showPrivacyModal, setShowPrivacyModal] = React.useState(false);
const { isLoggedIn } = useAuthGuard()
const fastingHydrationRequestedRef = React.useRef(false);
const permissionInitializedRef = React.useRef(false);
// 初始化快捷动作处理
useQuickActions();
React.useEffect(() => {
if (fastingHydrationRequestedRef.current) return;
if (activeFastingSchedule) {
fastingHydrationRequestedRef.current = true;
return;
}
fastingHydrationRequestedRef.current = true;
let cancelled = false;
const hydrate = async () => {
try {
const stored = await loadActiveFastingSchedule();
if (cancelled || !stored) return;
if (store.getState().fasting.activeSchedule) return;
dispatch(hydrateActiveSchedule(stored));
} catch (error) {
logger.warn('恢复断食计划失败:', error);
}
};
hydrate();
return () => {
cancelled = true;
};
}, [dispatch, activeFastingSchedule]);
useEffect(() => {
if (isLoggedIn) {
dispatch(fetchChallenges());
}
}, [isLoggedIn]);
// ==================== 基础服务初始化(不需要权限,总是执行)====================
React.useEffect(() => {
const initializeBasicServices = async () => {
try {
logger.info('🚀 开始初始化基础服务(不需要权限)...');
// 1. 加载用户数据(首屏展示需要)
await dispatch(fetchMyProfile());
logger.info('✅ 用户数据加载完成');
// 2. 初始化 HealthKit 权限系统(不请求权限,仅初始化)
initializeHealthPermissions();
logger.info('✅ HealthKit 权限系统初始化完成');
// 3. 初始化快捷动作(用户可能立即使用)
await setupQuickActions();
logger.info('✅ 快捷动作初始化完成');
// 4. 清空 AI 教练会话缓存(轻量操作)
clearAiCoachSessionCache();
logger.info('✅ AI 教练缓存清理完成');
// 5. 初始化喝水记录 Bridge
initializeWaterRecordBridge();
logger.info('✅ 喝水记录 Bridge 初始化完成');
logger.info('🎉 基础服务初始化完成');
} catch (error) {
logger.error('❌ 基础服务初始化失败:', error);
}
};
initializeBasicServices();
}, [dispatch]);
// ==================== 权限相关服务初始化(仅在 onboarding 完成后执行)====================
React.useEffect(() => {
// 如果还没完成 onboarding,或已经初始化过权限,则跳过
if (!onboardingCompleted || permissionInitializedRef.current) {
return;
}
permissionInitializedRef.current = true;
const initializePermissionServices = async () => {
try {
logger.info('🔐 开始初始化需要权限的服务(onboarding 已完成)...');
// 1. 初始化通知服务(包含权限请求)
await notificationService.initialize();
logger.info('✅ 通知服务初始化完成');
// 2. 延迟请求 HealthKit 权限(避免立即弹窗打断用户)
setTimeout(async () => {
try {
await ensureHealthPermissions();
logger.info('✅ HealthKit 权限请求完成');
} catch (error) {
logger.warn('⚠️ HealthKit 权限请求失败,可能在模拟器上运行:', error);
}
}, 2000);
// 3. 异步同步 Widget 数据
syncWidgetDataInBackground();
logger.info('🎉 权限相关服务初始化完成');
} catch (error) {
logger.error('❌ 权限相关服务初始化失败:', error);
}
};
// ==================== 后台服务初始化(延迟执行)====================
const initializeBackgroundServices = () => {
const { InteractionManager } = require('react-native');
InteractionManager.runAfterInteractions(() => {
setTimeout(async () => {
try {
logger.info('📅 开始初始化后台服务...');
// 1. 批量注册所有通知提醒
await registerAllNotifications();
// 2. 初始化后台任务管理器
await initializeBackgroundTaskManager();
// 3. 初始化健康监听服务
await initializeHealthMonitoring();
logger.info('🎉 后台服务初始化完成');
} catch (error) {
logger.error('❌ 后台服务初始化失败:', error);
}
}, 3000);
});
};
// ==================== 空闲服务初始化====================
const initializeIdleServices = () => {
setTimeout(async () => {
try {
logger.info('🔄 开始初始化空闲服务...');
// 1. 后台任务详细状态检查
await checkBackgroundTaskStatus();
// 2. 开发环境调试工具
if (__DEV__ && BackgroundTaskDebugger) {
BackgroundTaskDebugger.getInstance().initialize();
logger.info('✅ 后台任务调试工具已初始化(开发环境)');
}
logger.info('🎉 空闲服务初始化完成');
} catch (error) {
logger.error('❌ 空闲服务初始化失败:', error);
}
}, 8000);
};
// ==================== 辅助函数 ====================
// 异步同步 Widget 数据(不阻塞主流程)
const syncWidgetDataInBackground = async () => {
try {
const widgetSync = await syncPendingWidgetChanges();
if (widgetSync.hasPendingChanges && widgetSync.pendingRecords) {
logger.info(`🔄 检测到 ${widgetSync.pendingRecords.length} 条待同步的水记录`);
// 异步处理每条记录
for (const record of widgetSync.pendingRecords) {
try {
await store.dispatch(createWaterRecordAction({
amount: record.amount,
recordedAt: record.recordedAt,
source: WaterRecordSource.Auto,
})).unwrap();
logger.info(`✅ 成功同步水记录: ${record.amount}ml at ${record.recordedAt}`);
} catch (error) {
logger.error('❌ 同步水记录失败:', error);
}
}
// 清除已同步的记录
await clearPendingWaterRecords();
logger.info('✅ 所有待同步的水记录已处理完成');
}
} catch (error) {
logger.error('❌ Widget 数据同步失败:', error);
}
};
// 批量注册所有通知提醒
const registerAllNotifications = async () => {
try {
logger.info('📢 开始批量注册通知提醒...');
// 并行注册所有通知,提高效率
await Promise.all([
// 营养提醒
NutritionNotificationHelpers.scheduleDailyLunchReminder(profile.name || '').then(() =>
logger.info('✅ 午餐提醒已注册')
),
NutritionNotificationHelpers.scheduleDailyDinnerReminder(profile.name || '').then(() =>
logger.info('✅ 晚餐提醒已注册')
),
// 心情提醒
MoodNotificationHelpers.scheduleDailyMoodReminder(profile.name || '').then(() =>
logger.info('✅ 心情提醒已注册')
),
// 喝水提醒
WaterNotificationHelpers.scheduleRegularWaterReminders(profile.name || '用户').then(() =>
logger.info('✅ 喝水提醒已注册')
),
]);
// 检查断食通知(如果有活跃计划)
const fastingSchedule = store.getState().fasting.activeSchedule;
if (fastingSchedule) {
logger.info('✅ 检测到活跃的断食计划,将通过页面 hook 自动安排通知');
}
logger.info('🎉 所有通知提醒注册完成');
} catch (error) {
logger.error('❌ 通知提醒注册失败:', error);
}
};
// 初始化后台任务管理器
const initializeBackgroundTaskManager = async () => {
try {
logger.info('⚙️ 初始化后台任务管理器...');
await BackgroundTaskManager.getInstance().initialize();
logger.info('✅ 后台任务管理器初始化成功');
// 简单的任务调度检查
const taskManager = BackgroundTaskManager.getInstance();
const status = await taskManager.getStatus();
if (status === 'available') {
const pendingRequests = await taskManager.getPendingRequests();
if (pendingRequests.length === 0) {
await taskManager.scheduleNextTask();
logger.info('✅ 已调度新的后台任务');
}
}
} catch (error) {
logger.error('❌ 后台任务管理器初始化失败:', error);
}
};
// 初始化健康监听服务(锻炼 + 睡眠)
const initializeHealthMonitoring = async () => {
try {
logger.info('💪 初始化健康监听服务...');
// 并行初始化锻炼和睡眠监听
await Promise.allSettled([
workoutMonitorService.initialize().then(() =>
logger.info('✅ 锻炼监听服务初始化成功')
),
sleepMonitorService.initialize().then(() =>
logger.info('✅ 睡眠监听服务初始化成功')
),
]);
logger.info('🎉 健康监听服务初始化完成');
} catch (error) {
logger.error('❌ 健康监听服务初始化失败:', error);
}
};
// 后台任务详细状态检查(空闲时执行)
const checkBackgroundTaskStatus = async () => {
try {
logger.info('🔍 检查后台任务详细状态...');
const taskManager = BackgroundTaskManager.getInstance();
const status = await taskManager.getStatus();
const statusText = await taskManager.checkStatus();
logger.info(`📊 后台任务状态: ${status} (${statusText})`);
// 检查上次执行时间
const lastCheckTime = await taskManager.getLastBackgroundCheckTime();
if (lastCheckTime) {
const timeSinceLastCheck = Date.now() - lastCheckTime;
const hoursSinceLastCheck = timeSinceLastCheck / (1000 * 60 * 60);
logger.info(`⏱️ 上次执行: ${new Date(lastCheckTime).toLocaleString()} (${hoursSinceLastCheck.toFixed(1)}小时前)`);
if (hoursSinceLastCheck > 24) {
logger.warn('⚠️ 超过24小时未执行后台任务,请检查系统设置');
}
}
logger.info('✅ 后台任务状态检查完成');
} catch (error) {
logger.error('❌ 后台任务状态检查失败:', error);
}
};
// 执行权限相关初始化
initializePermissionServices();
// 交互完成后执行后台服务
initializeBackgroundServices();
// 空闲时执行非关键服务
initializeIdleServices();
}, [onboardingCompleted, profile.name]);
React.useEffect(() => {
const getPrivacyAgreed = async () => {
const str = await AsyncStorage.getItem(STORAGE_KEYS.privacyAgreed)
setShowPrivacyModal(str !== 'true');
}
getPrivacyAgreed();
}, []);
const handlePrivacyAgree = () => {
dispatch(setPrivacyAgreed());
setShowPrivacyModal(false);
};
const handlePrivacyDisagree = () => {
// RNExitApp.exitApp();
};
return (
{children}
);
}
export default function RootLayout() {
const [loaded] = useFonts({
SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
});
if (!loaded) {
// Async font loading only occurs in development.
return null;
}
return (
);
}