FERS 1.0.0
The Flexible Extensible Radar Simulator
Loading...
Searching...
No Matches
MainLayout.tsx
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-only
2// Copyright (c) 2025-present FERS Contributors (see AUTHORS.md).
3
4import { Alert, Box, Snackbar } from '@mui/material';
5import { listen } from '@tauri-apps/api/event';
6import React, { useEffect, useState } from 'react';
7import AppRail from '@/components/AppRail';
8import RawLogDrawer from '@/components/RawLogDrawer';
9import SettingsDialog from '@/components/SettingsDialog';
10import { type FersLogEntry, useFersLogStore } from '@/stores/fersLogStore';
11import { useScenarioStore } from '@/stores/scenarioStore';
12import { AssetLibraryView } from '@/views/AssetLibraryView';
13import { ScenarioView } from '@/views/ScenarioView';
14import { SimulationView } from '@/views/SimulationView';
15
16export function MainLayout() {
17 const [activeView, setActiveView] = useState('scenario');
18 const [settingsOpen, setSettingsOpen] = useState(false);
19 const logOpen = useFersLogStore((state) => state.isOpen);
20 const appendLog = useFersLogStore((state) => state.appendLog);
21 const { open, message, severity } = useScenarioStore(
22 (state) => state.notificationSnackbar
23 );
24 const hideNotification = useScenarioStore(
25 (state) => state.hideNotification
26 );
27 const advanceNotification = useScenarioStore(
28 (state) => state.advanceNotification
29 );
30
31 useEffect(() => {
32 let active = true;
33 let unlistenLog: (() => void) | undefined;
34
35 listen<FersLogEntry>('fers-log', (event) => {
36 appendLog(event.payload);
37 }).then((unlisten) => {
38 if (active) {
39 unlistenLog = unlisten;
40 } else {
41 unlisten();
42 }
43 });
44
45 return () => {
46 active = false;
47 unlistenLog?.();
48 };
49 }, [appendLog]);
50
51 return (
52 <Box
53 sx={{
54 display: 'flex',
55 height: '100vh',
56 width: '100vw',
57 overflow: 'hidden',
58 position: 'fixed', // Ensure it stays in viewport
59 top: 0,
60 left: 0,
61 }}
62 >
63 <AppRail
64 activeView={activeView}
65 onViewChange={setActiveView}
66 onSettingsClick={() => setSettingsOpen(true)}
67 />
68 {logOpen && <RawLogDrawer />}
69 <Box
70 component="main"
71 sx={{
72 flexGrow: 1,
73 minWidth: 0, // Allow shrinking below content size
74 height: '100%',
75 overflow: 'hidden', // Prevent overflow
76 position: 'relative',
77 }}
78 >
79 {/* Render all views but only display the active one */}
80 <Box
81 sx={{
82 display: activeView === 'scenario' ? 'flex' : 'none',
83 height: '100%',
84 width: '100%',
85 }}
86 >
87 <ScenarioView isActive={activeView === 'scenario'} />
88 </Box>
89 <Box
90 sx={{
91 display: activeView === 'assets' ? 'block' : 'none',
92 height: '100%',
93 width: '100%',
94 }}
95 >
96 <AssetLibraryView />
97 </Box>
98 <Box
99 sx={{
100 display: activeView === 'simulation' ? 'block' : 'none',
101 height: '100%',
102 width: '100%',
103 }}
104 >
105 <SimulationView />
106 </Box>
107 </Box>
108 <SettingsDialog
109 open={settingsOpen}
110 onClose={() => setSettingsOpen(false)}
111 />
112 <Snackbar
113 open={open}
114 autoHideDuration={6000}
115 onClose={hideNotification}
116 anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
117 TransitionProps={{ onExited: advanceNotification }}
118 >
119 <Alert
120 onClose={hideNotification}
121 severity={severity}
122 variant="filled"
123 sx={{ width: '100%' }}
124 >
125 {message}
126 </Alert>
127 </Snackbar>
128 </Box>
129 );
130}