diff --git a/mobile/App.tsx b/mobile/App.tsx index 64709bc..4b452af 100644 --- a/mobile/App.tsx +++ b/mobile/App.tsx @@ -1,25 +1,38 @@ -import React from 'react'; -import { StyleSheet, Text, View } from 'react-native'; +import { useEffect } from 'react'; +import { NavigationContainer, DarkTheme } from '@react-navigation/native'; +import { GestureHandlerRootView } from 'react-native-gesture-handler'; import { StatusBar } from 'expo-status-bar'; +import { SafeAreaProvider } from 'react-native-safe-area-context'; +import { ThemeProvider } from './src/theme/ThemeContext'; +import { RootNavigator } from './src/navigation/RootNavigator'; export default function App() { + useEffect(() => { + (globalThis as { __appStartTime?: number }).__appStartTime = Date.now(); + }, []); + + const navigationTheme = { + ...DarkTheme, + colors: { + ...DarkTheme.colors, + background: '#0A0E1A', + card: '#0D1117', + text: '#E2E8F0', + border: '#1E293B', + primary: '#32B8C6', + }, + }; + return ( - - WiFi-DensePose - - + + + + + + + + + + ); } - -const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - backgroundColor: '#fff', - }, - title: { - fontSize: 24, - fontWeight: '600', - }, -}); diff --git a/mobile/src/components/ConnectionBanner.tsx b/mobile/src/components/ConnectionBanner.tsx index e69de29..9ab6601 100644 --- a/mobile/src/components/ConnectionBanner.tsx +++ b/mobile/src/components/ConnectionBanner.tsx @@ -0,0 +1,70 @@ +import { StyleSheet, View } from 'react-native'; +import { ThemedText } from './ThemedText'; + +type ConnectionState = 'connected' | 'simulated' | 'disconnected'; + +type ConnectionBannerProps = { + status: ConnectionState; +}; + +const resolveState = (status: ConnectionState) => { + if (status === 'connected') { + return { + label: 'LIVE STREAM', + backgroundColor: '#0F6B2A', + textColor: '#E2FFEA', + }; + } + + if (status === 'disconnected') { + return { + label: 'DISCONNECTED', + backgroundColor: '#8A1E2A', + textColor: '#FFE3E7', + }; + } + + return { + label: 'SIMULATED DATA', + backgroundColor: '#9A5F0C', + textColor: '#FFF3E1', + }; +}; + +export const ConnectionBanner = ({ status }: ConnectionBannerProps) => { + const state = resolveState(status); + + return ( + + + {state.label} + + + ); +}; + +const styles = StyleSheet.create({ + banner: { + position: 'absolute', + left: 0, + right: 0, + top: 0, + zIndex: 100, + paddingVertical: 6, + borderBottomWidth: 2, + alignItems: 'center', + justifyContent: 'center', + }, + text: { + letterSpacing: 2, + fontWeight: '700', + }, +}); diff --git a/mobile/src/components/ErrorBoundary.tsx b/mobile/src/components/ErrorBoundary.tsx index e69de29..9f1d03a 100644 --- a/mobile/src/components/ErrorBoundary.tsx +++ b/mobile/src/components/ErrorBoundary.tsx @@ -0,0 +1,66 @@ +import { Component, ErrorInfo, ReactNode } from 'react'; +import { Button, StyleSheet, View } from 'react-native'; +import { ThemedText } from './ThemedText'; +import { ThemedView } from './ThemedView'; + +type ErrorBoundaryProps = { + children: ReactNode; +}; + +type ErrorBoundaryState = { + hasError: boolean; + error?: Error; +}; + +export class ErrorBoundary extends Component { + constructor(props: ErrorBoundaryProps) { + super(props); + this.state = { hasError: false }; + } + + static getDerivedStateFromError(error: Error): ErrorBoundaryState { + return { hasError: true, error }; + } + + componentDidCatch(error: Error, errorInfo: ErrorInfo) { + console.error('ErrorBoundary caught an error', error, errorInfo); + } + + handleRetry = () => { + this.setState({ hasError: false, error: undefined }); + }; + + render() { + if (this.state.hasError) { + return ( + + Something went wrong + + {this.state.error?.message ?? 'An unexpected error occurred.'} + + +