mirror of
https://github.com/Sea-Haven-Industries/proposal-system.git
synced 2026-09-30 20:33:14 +00:00
Pin React to 19.2.3 (exact version RN 0.85.3's bundled renderer requires — 19.2.6 caused "Cannot read property 'default' of undefined" crash at getPaperRenderer). Auth: fall back to parsing user info from Cognito ID token when backend API is unreachable, removing the hard dependency on localhost:5000 for credential login. UI fixes: - Safe area insets on Settings screen (top edge was missing) - Separate refresh indicator from filter-triggered loading on proposal queue (chips no longer trigger pull-to-refresh animation) - Dashboard welcome shows first name instead of full email - Dashboard/AdminDashboard remove duplicate top safe area when navigation header already provides it - Service category selector uses wrapping chips instead of cramped SegmentedButtons - Add ErrorBoundary to App root for crash visibility
98 lines
3.3 KiB
TypeScript
98 lines
3.3 KiB
TypeScript
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 } from './store/slices/authSlice';
|
|
import { processOfflineQueue } from './hooks/useOfflineDraft';
|
|
import { proposalsApi, CreateProposalRequest } from './lib/api/proposals';
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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(() => {
|
|
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(() => {});
|
|
}
|
|
});
|
|
return unsubscribe;
|
|
}, []);
|
|
|
|
return <>{children}</>;
|
|
}
|
|
|
|
export default function App() {
|
|
return (
|
|
<ErrorBoundary>
|
|
<ReduxProvider store={store}>
|
|
<QueryClientProvider client={queryClient}>
|
|
<PaperProvider theme={theme}>
|
|
<SafeAreaProvider>
|
|
<AuthBootstrap>
|
|
<RootNavigator />
|
|
</AuthBootstrap>
|
|
</SafeAreaProvider>
|
|
</PaperProvider>
|
|
</QueryClientProvider>
|
|
</ReduxProvider>
|
|
</ErrorBoundary>
|
|
);
|
|
}
|