import React, { useEffect, Component, ErrorInfo, ReactNode } from 'react'; import { Provider as ReduxProvider, useDispatch } from 'react-redux'; import { PaperProvider } from 'react-native-paper'; import { QueryClientProvider } from '@tanstack/react-query'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import NetInfo from '@react-native-community/netinfo'; import { Alert, Text, View } from 'react-native'; import { store } from './store'; import { theme } from './theme'; import { queryClient } from './lib/queryClient'; import { RootNavigator } from './navigation/RootNavigator'; import { tokenStorage, userStorage } from './lib/storage'; import { setUser, setLoading, logout as logoutAction } from './store/slices/authSlice'; import { processOfflineQueue } from './hooks/useOfflineDraft'; import { proposalsApi, CreateProposalRequest } from './lib/api/proposals'; import { onSessionExpired } from './lib/api/client'; class ErrorBoundary extends Component<{ children: ReactNode }, { error: Error | null }> { state = { error: null as Error | null }; static getDerivedStateFromError(error: Error) { return { error }; } componentDidCatch(error: Error, info: ErrorInfo) { console.error('ErrorBoundary caught:', error, info.componentStack); } render() { if (this.state.error) { return ( App Error {this.state.error.message} {this.state.error.stack?.slice(0, 500)} ); } return this.props.children; } } function AuthBootstrap({ children }: { children: React.ReactNode }) { const dispatch = useDispatch(); useEffect(() => { (async () => { try { const tokens = await tokenStorage.get(); const user = await userStorage.get(); if (tokens && user && new Date(tokens.expiresAt) > new Date()) { dispatch(setUser(user)); } else { if (tokens) await tokenStorage.clear(); dispatch(setLoading(false)); } } catch { dispatch(setLoading(false)); } })(); }, [dispatch]); useEffect(() => { return onSessionExpired(() => { dispatch(logoutAction()); }); }, [dispatch]); useEffect(() => { const unsubscribe = NetInfo.addEventListener((state) => { if (state.isConnected) { processOfflineQueue((data) => proposalsApi.create(data as CreateProposalRequest), ) .then((count) => { if (count > 0) { Alert.alert( 'Synced', `${count} offline proposal${count > 1 ? 's' : ''} submitted.`, ); } }) .catch((err) => console.error('Offline queue sync failed:', err)); } }); return unsubscribe; }, []); return <>{children}; } export default function App() { return ( ); }