import React, { useEffect, useState } from 'react'; import * as BackgroundFetch from 'expo-background-fetch'; import { BackgroundFetchResult } from 'expo-background-fetch'; import * as TaskManager from 'expo-task-manager'; import { NavigationContainer } from '@react-navigation/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { Text } from 'react-native'; import LoginScreen from './src/screens/Login'; import HomeScreen from './src/screens/Home'; import HabitsScreen from './src/screens/HabitsScreen'; import AnalyticsScreen from './src/screens/AnalyticsScreen'; import AchievementsScreen from './src/screens/AchievementsScreen'; import HabitDetailScreen from './src/screens/HabitDetailScreen'; import AddHabitScreen from './src/screens/AddHabitScreen'; export type RootStackParamList = { Login: undefined; MainTabs: undefined; HabitDetail: { habitId: number }; AddHabit: undefined; }; export type TabParamList = { Home: undefined; Habits: undefined; Analytics: undefined; Achievements: undefined; }; const Stack = createNativeStackNavigator(); const Tab = createBottomTabNavigator(); function TabIcon({ emoji }: { emoji: string }) { return {emoji}; } function MainTabs() { return ( , headerShown: true, title: 'LifeRPG', }} /> , headerShown: true, title: 'Habits', }} /> , headerShown: true, title: 'Analytics', }} /> , headerShown: true, title: 'Achievements', }} /> ); } export default function App() { const [initialRoute, setInitialRoute] = useState('Login'); useEffect(() => { // Lazy import to avoid circular import('./src/lib/auth').then(async ({ getTokens, refresh }) => { const t = await getTokens(); if (t?.accessToken) { // Attempt a refresh to ensure validity (non-blocking) try { await refresh(); } catch { } setInitialRoute('MainTabs'); } }); // Register background fetch const TASK = 'liferpg-sync-task'; TaskManager.defineTask(TASK, async () => { try { const [{ auth }, { openDb, pendingChanges, clearChangesUpTo, pushChanges }] = await Promise.all([ import('./src/lib/auth'), import('./src/lib/db'), ]); const tokens = await auth.getTokens(); const accessToken = tokens?.accessToken; if (!accessToken) return BackgroundFetchResult.NoData; const db = openDb(); const changes = await pendingChanges(db); if (changes.length === 0) return BackgroundFetchResult.NoData; const extra: any = (require('expo-constants').default.expoConfig?.extra) || {}; const apiBaseUrl = extra.apiBaseUrl; const res = await pushChanges(apiBaseUrl, accessToken, changes); if (res?.applied) await clearChangesUpTo(db, changes[changes.length - 1].id); return BackgroundFetchResult.NewData; } catch { return BackgroundFetchResult.Failed; } }); BackgroundFetch.registerTaskAsync(TASK, { minimumInterval: 15 * 60, stopOnTerminate: false, startOnBoot: true }).catch(() => { }); }, []); return ( ); }