2026-05-20 13:34:08 -04:00
|
|
|
import React, { useEffect, Component, ErrorInfo, ReactNode } from 'react';
|
Add mobile core: API client, auth, storage, state, and offline support
Keychain token storage, Axios client with async token injection, Cognito
PKCE auth via react-native-app-auth, Redux auth slice, TanStack Query
config, offline draft queue with NetInfo auto-submit, and SHOC theme.
2026-05-17 15:09:23 -04:00
|
|
|
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';
|
2026-05-20 13:34:08 -04:00
|
|
|
import { Alert, Text, View } from 'react-native';
|
Add mobile core: API client, auth, storage, state, and offline support
Keychain token storage, Axios client with async token injection, Cognito
PKCE auth via react-native-app-auth, Redux auth slice, TanStack Query
config, offline draft queue with NetInfo auto-submit, and SHOC theme.
2026-05-17 15:09:23 -04:00
|
|
|
|
|
|
|
|
import { store } from './store';
|
|
|
|
|
import { theme } from './theme';
|
|
|
|
|
import { queryClient } from './lib/queryClient';
|
|
|
|
|
import { RootNavigator } from './navigation/RootNavigator';
|
|
|
|
|
import { tokenStorage, userStorage } from './lib/storage';
|
2026-05-20 19:07:49 -04:00
|
|
|
import { setUser, setLoading, logout as logoutAction } from './store/slices/authSlice';
|
Add mobile core: API client, auth, storage, state, and offline support
Keychain token storage, Axios client with async token injection, Cognito
PKCE auth via react-native-app-auth, Redux auth slice, TanStack Query
config, offline draft queue with NetInfo auto-submit, and SHOC theme.
2026-05-17 15:09:23 -04:00
|
|
|
import { processOfflineQueue } from './hooks/useOfflineDraft';
|
|
|
|
|
import { proposalsApi, CreateProposalRequest } from './lib/api/proposals';
|
2026-05-20 19:07:49 -04:00
|
|
|
import { onSessionExpired } from './lib/api/client';
|
Add mobile core: API client, auth, storage, state, and offline support
Keychain token storage, Axios client with async token injection, Cognito
PKCE auth via react-native-app-auth, Redux auth slice, TanStack Query
config, offline draft queue with NetInfo auto-submit, and SHOC theme.
2026-05-17 15:09:23 -04:00
|
|
|
|
2026-05-20 13:34:08 -04:00
|
|
|
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 (
|
|
|
|
|
<View style={{ flex: 1, justifyContent: 'center', padding: 32, backgroundColor: '#fff' }}>
|
|
|
|
|
<Text style={{ fontSize: 18, fontWeight: 'bold', color: 'red', marginBottom: 8 }}>App Error</Text>
|
|
|
|
|
<Text style={{ fontSize: 14, color: '#333' }}>{this.state.error.message}</Text>
|
|
|
|
|
<Text style={{ fontSize: 12, color: '#666', marginTop: 8 }}>{this.state.error.stack?.slice(0, 500)}</Text>
|
|
|
|
|
</View>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return this.props.children;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Add mobile core: API client, auth, storage, state, and offline support
Keychain token storage, Axios client with async token injection, Cognito
PKCE auth via react-native-app-auth, Redux auth slice, TanStack Query
config, offline draft queue with NetInfo auto-submit, and SHOC theme.
2026-05-17 15:09:23 -04:00
|
|
|
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]);
|
2026-05-20 19:07:49 -04:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
return onSessionExpired(() => {
|
|
|
|
|
dispatch(logoutAction());
|
|
|
|
|
});
|
|
|
|
|
}, [dispatch]);
|
Add mobile core: API client, auth, storage, state, and offline support
Keychain token storage, Axios client with async token injection, Cognito
PKCE auth via react-native-app-auth, Redux auth slice, TanStack Query
config, offline draft queue with NetInfo auto-submit, and SHOC theme.
2026-05-17 15:09:23 -04:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const unsubscribe = NetInfo.addEventListener((state) => {
|
|
|
|
|
if (state.isConnected) {
|
|
|
|
|
processOfflineQueue((data) =>
|
|
|
|
|
proposalsApi.create(data as CreateProposalRequest),
|
2026-05-19 19:54:13 -04:00
|
|
|
)
|
|
|
|
|
.then((count) => {
|
|
|
|
|
if (count > 0) {
|
|
|
|
|
Alert.alert(
|
|
|
|
|
'Synced',
|
|
|
|
|
`${count} offline proposal${count > 1 ? 's' : ''} submitted.`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(() => {});
|
Add mobile core: API client, auth, storage, state, and offline support
Keychain token storage, Axios client with async token injection, Cognito
PKCE auth via react-native-app-auth, Redux auth slice, TanStack Query
config, offline draft queue with NetInfo auto-submit, and SHOC theme.
2026-05-17 15:09:23 -04:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return unsubscribe;
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
return <>{children}</>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default function App() {
|
|
|
|
|
return (
|
2026-05-20 13:34:08 -04:00
|
|
|
<ErrorBoundary>
|
|
|
|
|
<ReduxProvider store={store}>
|
|
|
|
|
<QueryClientProvider client={queryClient}>
|
|
|
|
|
<PaperProvider theme={theme}>
|
|
|
|
|
<SafeAreaProvider>
|
|
|
|
|
<AuthBootstrap>
|
|
|
|
|
<RootNavigator />
|
|
|
|
|
</AuthBootstrap>
|
|
|
|
|
</SafeAreaProvider>
|
|
|
|
|
</PaperProvider>
|
|
|
|
|
</QueryClientProvider>
|
|
|
|
|
</ReduxProvider>
|
|
|
|
|
</ErrorBoundary>
|
Add mobile core: API client, auth, storage, state, and offline support
Keychain token storage, Axios client with async token injection, Cognito
PKCE auth via react-native-app-auth, Redux auth slice, TanStack Query
config, offline draft queue with NetInfo auto-submit, and SHOC theme.
2026-05-17 15:09:23 -04:00
|
|
|
);
|
|
|
|
|
}
|