233 lines
7.3 KiB
TypeScript
233 lines
7.3 KiB
TypeScript
import React, { useEffect } from 'react';
|
|
import { NavigationContainer, useNavigation } from '@react-navigation/native';
|
|
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
|
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
|
import { Provider as PaperProvider, MD3LightTheme } from 'react-native-paper';
|
|
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
|
import { setNavigationRef } from '../api/client';
|
|
import { useAuthStore } from '../store/authStore';
|
|
import { useAppStore } from '../store/appStore';
|
|
import { wsManager } from '../utils/ws';
|
|
import type { RootStackParamList, MainTabParamList } from '../types';
|
|
|
|
import LoginScreen from '../screens/LoginScreen';
|
|
import DashboardScreen from '../screens/DashboardScreen';
|
|
import RoomsScreen from '../screens/RoomsScreen';
|
|
import RoomDetailScreen from '../screens/RoomDetailScreen';
|
|
import DevicesScreen from '../screens/DevicesScreen';
|
|
import DeviceControlScreen from '../screens/DeviceControlScreen';
|
|
import AlertsScreen from '../screens/AlertsScreen';
|
|
import ThresholdsScreen from '../screens/ThresholdsScreen';
|
|
import VideoScreen from '../screens/VideoScreen';
|
|
import VideoPlayerScreen from '../screens/VideoPlayerScreen';
|
|
import SettingsScreen from '../screens/SettingsScreen';
|
|
|
|
const Stack = createNativeStackNavigator<RootStackParamList>();
|
|
const Tab = createBottomTabNavigator<MainTabParamList>();
|
|
|
|
const TabIcon: React.FC<{ name: string; activeName: string; color: string; size: number; focused: boolean }> = ({ name, activeName, color, focused }) => (
|
|
<Icon name={focused ? activeName : name} size={focused ? 26 : 22} color={color} />
|
|
);
|
|
|
|
function MainTabs() {
|
|
const navigation = useNavigation<any>();
|
|
|
|
return (
|
|
<Tab.Navigator
|
|
screenOptions={{
|
|
headerShown: true,
|
|
tabBarActiveTintColor: '#5C7D60',
|
|
tabBarInactiveTintColor: '#C4BFB6',
|
|
tabBarStyle: {
|
|
backgroundColor: '#FFFFFF',
|
|
borderTopColor: '#F0EDE8',
|
|
borderTopWidth: 1,
|
|
paddingBottom: 6,
|
|
paddingTop: 6,
|
|
height: 60,
|
|
},
|
|
tabBarLabelStyle: {
|
|
fontSize: 15,
|
|
fontWeight: '700',
|
|
marginTop: 2,
|
|
},
|
|
tabBarIconStyle: {
|
|
marginBottom: 2,
|
|
},
|
|
headerStyle: {
|
|
backgroundColor: '#FAF8F5',
|
|
},
|
|
headerTitleStyle: {
|
|
color: '#2D2A26',
|
|
fontSize: 18,
|
|
fontWeight: '500',
|
|
},
|
|
headerTintColor: '#5C7D60',
|
|
headerRight: () => (
|
|
<Icon
|
|
name="cog-outline"
|
|
size={24}
|
|
color="#8C8780"
|
|
style={{ marginRight: 12 }}
|
|
onPress={() => navigation.navigate('Settings')}
|
|
/>
|
|
),
|
|
}}
|
|
>
|
|
<Tab.Screen
|
|
name="Dashboard"
|
|
component={DashboardScreen}
|
|
options={{
|
|
title: '仪表盘',
|
|
tabBarIcon: ({ color, size, focused }) => <TabIcon name="view-dashboard-outline" activeName="view-dashboard" color={color} size={size} focused={focused} />,
|
|
}}
|
|
/>
|
|
<Tab.Screen
|
|
name="Rooms"
|
|
component={RoomsScreen}
|
|
options={{
|
|
title: '蚕房',
|
|
tabBarIcon: ({ color, size, focused }) => <TabIcon name="home-outline" activeName="home-variant" color={color} size={size} focused={focused} />,
|
|
}}
|
|
/>
|
|
<Tab.Screen
|
|
name="Devices"
|
|
component={DevicesScreen}
|
|
options={{
|
|
title: '设备',
|
|
tabBarIcon: ({ color, size, focused }) => <TabIcon name="router-wireless" activeName="router-wireless" color={color} size={size} focused={focused} />,
|
|
}}
|
|
/>
|
|
<Tab.Screen
|
|
name="Alerts"
|
|
component={AlertsScreen}
|
|
options={{
|
|
title: '告警',
|
|
tabBarIcon: ({ color, size, focused }) => <TabIcon name="bell-alert-outline" activeName="bell-alert" color={color} size={size} focused={focused} />,
|
|
}}
|
|
/>
|
|
<Tab.Screen
|
|
name="Video"
|
|
component={VideoScreen}
|
|
options={{
|
|
title: '视频',
|
|
tabBarIcon: ({ color, size, focused }) => <TabIcon name="video-outline" activeName="video" color={color} size={size} focused={focused} />,
|
|
}}
|
|
/>
|
|
</Tab.Navigator>
|
|
);
|
|
}
|
|
|
|
function AppContent() {
|
|
const token = useAuthStore((s) => s.token);
|
|
const restoreSession = useAuthStore((s) => s.restoreSession);
|
|
const setRealtimeData = useAppStore((s) => s.setRealtimeData);
|
|
const [isReady, setIsReady] = React.useState(false);
|
|
|
|
useEffect(() => {
|
|
(async () => {
|
|
await restoreSession();
|
|
setIsReady(true);
|
|
})();
|
|
}, [restoreSession]);
|
|
|
|
// Subscribe to WebSocket telemetry
|
|
useEffect(() => {
|
|
const unsub = wsManager.subscribe((data) => {
|
|
if (data?.type === 'telemetry' || data?.metric) {
|
|
const telemetry = useAppStore.getState().realtimeData;
|
|
setRealtimeData([...telemetry, data].slice(-100));
|
|
}
|
|
});
|
|
return unsub;
|
|
}, [setRealtimeData]);
|
|
|
|
if (!isReady) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<NavigationContainer
|
|
ref={(ref) => {
|
|
if (ref) {
|
|
setNavigationRef({
|
|
navigate: (screen: string) => {
|
|
(ref as any).navigate(screen);
|
|
},
|
|
});
|
|
}
|
|
}}
|
|
>
|
|
<Stack.Navigator screenOptions={{ headerShown: false }} initialRouteName={token ? 'Main' : 'Login'}>
|
|
<Stack.Screen name="Login" component={LoginScreen} />
|
|
<Stack.Screen name="Main" component={MainTabs} />
|
|
<Stack.Screen
|
|
name="RoomDetail"
|
|
component={RoomDetailScreen}
|
|
options={{ headerShown: true, title: '蚕房详情', headerBackTitle: '返回' }}
|
|
/>
|
|
<Stack.Screen
|
|
name="DeviceControl"
|
|
component={DeviceControlScreen}
|
|
options={{ headerShown: true, title: '设备控制', headerBackTitle: '返回' }}
|
|
/>
|
|
<Stack.Screen
|
|
name="VideoPlayer"
|
|
component={VideoPlayerScreen}
|
|
options={{ headerShown: false }}
|
|
/>
|
|
<Stack.Screen
|
|
name="Thresholds"
|
|
component={ThresholdsScreen}
|
|
options={{ headerShown: true, title: '阈值管理', headerBackTitle: '返回' }}
|
|
/>
|
|
<Stack.Screen
|
|
name="Settings"
|
|
component={SettingsScreen}
|
|
options={{ headerShown: true, title: '设置', headerBackTitle: '返回' }}
|
|
/>
|
|
</Stack.Navigator>
|
|
</NavigationContainer>
|
|
);
|
|
}
|
|
|
|
export default function AppNavigator() {
|
|
const silkTheme = {
|
|
...MD3LightTheme,
|
|
colors: {
|
|
...MD3LightTheme.colors,
|
|
primary: '#7A9E7E',
|
|
primaryContainer: '#E8F0E9',
|
|
onPrimary: '#FFFFFF',
|
|
onPrimaryContainer: '#5C7D60',
|
|
secondary: '#6B8F71',
|
|
secondaryContainer: '#F0EDE8',
|
|
onSecondary: '#FFFFFF',
|
|
onSecondaryContainer: '#5C7D60',
|
|
error: '#D4847A',
|
|
errorContainer: '#F9E8E5',
|
|
onError: '#FFFFFF',
|
|
background: '#FAF8F5',
|
|
onBackground: '#2D2A26',
|
|
surface: '#FFFFFF',
|
|
onSurface: '#2D2A26',
|
|
surfaceVariant: '#F5F0E8',
|
|
onSurfaceVariant: '#8C8780',
|
|
outline: '#E8E4DE',
|
|
outlineVariant: '#F0EDE8',
|
|
elevation: {
|
|
...MD3LightTheme.colors.elevation,
|
|
level0: 'transparent',
|
|
level1: '#FFFFFF',
|
|
level2: '#FFFFFF',
|
|
},
|
|
},
|
|
};
|
|
|
|
return (
|
|
<PaperProvider theme={silkTheme}>
|
|
<AppContent />
|
|
</PaperProvider>
|
|
);
|
|
}
|