Files
digital-pilates/app/_layout.tsx
richarjiang 16c2351160 feat: 移除目标管理功能模块
删除了完整的目标管理功能,包括目标创建、编辑、任务管理等相关页面和组件。同时移除了相关的API服务、Redux状态管理、类型定义和通知功能。应用版本从1.0.20升级到1.0.21。
2025-10-31 08:49:22 +08:00

265 lines
9.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 { 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 } 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/backgroundTaskManager';
import { fetchChallenges } from '@/store/challengesSlice';
import AsyncStorage from '@/utils/kvStore';
import { Provider } from 'react-redux';
function Bootstrapper({ children }: { children: React.ReactNode }) {
const dispatch = useAppDispatch();
const { profile } = useAppSelector((state) => state.user);
const activeFastingSchedule = useAppSelector(selectActiveFastingSchedule);
const [showPrivacyModal, setShowPrivacyModal] = React.useState(false);
const { isLoggedIn } = useAuthGuard()
const fastingHydrationRequestedRef = 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) {
console.warn('恢复断食计划失败:', error);
}
};
hydrate();
return () => {
cancelled = true;
};
}, [dispatch, activeFastingSchedule]);
useEffect(() => {
if (isLoggedIn) {
dispatch(fetchChallenges());
}
}, [isLoggedIn]);
React.useEffect(() => {
const loadUserData = async () => {
// 数据已经在启动界面预加载,这里只需要快速同步到 Redux 状态
await dispatch(fetchMyProfile());
};
const initHealthPermissions = async () => {
// 初始化 HealthKit 权限管理系统
try {
console.log('初始化 HealthKit 权限管理系统...');
initializeHealthPermissions();
// 延迟请求权限,避免应用启动时弹窗
setTimeout(async () => {
try {
await ensureHealthPermissions();
console.log('HealthKit 权限请求完成');
} catch (error) {
console.warn('HealthKit 权限请求失败,可能在模拟器上运行:', error);
}
}, 2000);
console.log('HealthKit 权限管理初始化完成');
} catch (error) {
console.warn('HealthKit 权限管理初始化失败:', error);
}
}
const initializeNotifications = async () => {
try {
await BackgroundTaskManager.getInstance().initialize();
// 初始化通知服务
await notificationService.initialize();
console.log('通知服务初始化成功');
// 注册午餐提醒12:00
await NutritionNotificationHelpers.scheduleDailyLunchReminder(profile.name || '');
console.log('午餐提醒已注册');
// 注册晚餐提醒18:00
await NutritionNotificationHelpers.scheduleDailyDinnerReminder(profile.name || '');
console.log('晚餐提醒已注册');
// 注册心情提醒21:00
await MoodNotificationHelpers.scheduleDailyMoodReminder(profile.name || '');
console.log('心情提醒已注册');
// 初始化快捷动作
await setupQuickActions();
console.log('快捷动作初始化成功');
// 初始化喝水记录 bridge
initializeWaterRecordBridge();
console.log('喝水记录 Bridge 初始化成功');
// 初始化锻炼监听服务
const initializeWorkoutMonitoring = async () => {
try {
await workoutMonitorService.initialize();
console.log('锻炼监听服务初始化成功');
} catch (error) {
console.warn('锻炼监听服务初始化失败:', error);
}
};
initializeWorkoutMonitoring();
// 检查并同步Widget数据更改
const widgetSync = await syncPendingWidgetChanges();
if (widgetSync.hasPendingChanges && widgetSync.pendingRecords) {
console.log(`检测到 ${widgetSync.pendingRecords.length} 条待同步的水记录`);
// 将待同步的记录添加到 Redux store
for (const record of widgetSync.pendingRecords) {
try {
await store.dispatch(createWaterRecordAction({
amount: record.amount,
recordedAt: record.recordedAt,
source: WaterRecordSource.Auto, // 标记为自动添加来自Widget
})).unwrap();
console.log(`成功同步水记录: ${record.amount}ml at ${record.recordedAt}`);
} catch (error) {
console.error('同步水记录失败:', error);
}
}
// 清除已同步的记录
await clearPendingWaterRecords();
console.log('所有待同步的水记录已处理完成');
}
} catch (error) {
console.error('通知服务、后台任务管理器或快捷动作初始化失败:', error);
}
};
loadUserData();
initHealthPermissions();
initializeNotifications();
// 冷启动时清空 AI 教练会话缓存
clearAiCoachSessionCache();
}, [dispatch]);
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 (
<DialogProvider>
<MembershipModalProvider>
{children}
<PrivacyConsentModal
visible={showPrivacyModal}
onAgree={handlePrivacyAgree}
onDisagree={handlePrivacyDisagree}
/>
</MembershipModalProvider>
</DialogProvider>
);
}
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 (
<GestureHandlerRootView style={{ flex: 1 }}>
<Provider store={store}>
<Bootstrapper>
<ToastProvider>
<ThemeProvider value={DefaultTheme}>
<Stack screenOptions={{ headerShown: false }}>
<Stack.Screen name="(tabs)" />
<Stack.Screen name="challenge" options={{ headerShown: false }} />
<Stack.Screen name="training-plan" options={{ headerShown: false }} />
<Stack.Screen name="workout" options={{ headerShown: false }} />
<Stack.Screen name="profile/edit" />
<Stack.Screen name="fasting/[planId]" options={{ headerShown: false }} />
<Stack.Screen name="ai-posture-assessment" />
<Stack.Screen name="auth/login" options={{ headerShown: false }} />
<Stack.Screen name="legal/user-agreement" options={{ headerShown: true, title: '用户协议' }} />
<Stack.Screen name="legal/privacy-policy" options={{ headerShown: true, title: '隐私政策' }} />
<Stack.Screen name="article/[id]" options={{ headerShown: false }} />
<Stack.Screen name="water-detail" options={{ headerShown: false }} />
<Stack.Screen name="water-settings" options={{ headerShown: false }} />
<Stack.Screen name="workout/notification-settings" options={{ headerShown: false }} />
<Stack.Screen name="+not-found" />
</Stack>
<StatusBar style="dark" />
</ThemeProvider>
</ToastProvider>
</Bootstrapper>
</Provider>
</GestureHandlerRootView>
);
}