1// SPDX-License-Identifier: GPL-2.0-only
2// Copyright (c) 2025-present FERS Contributors (see AUTHORS.md).
4import { Alert, Box, Snackbar } from '@mui/material';
5import { listen } from '@tauri-apps/api/event';
6import React, { useEffect, useState } from 'react';
7import AboutDialog from '@/components/AboutDialog';
8import AppRail from '@/components/AppRail';
9import LicensesDialog from '@/components/LicensesDialog';
10import RawLogDrawer from '@/components/RawLogDrawer';
11import SettingsDialog from '@/components/SettingsDialog';
15 getEffectiveLogDrawerWidth,
17} from '@/stores/fersLogStore';
18import { useScenarioStore } from '@/stores/scenarioStore';
19import { AssetLibraryView } from '@/views/AssetLibraryView';
20import { ScenarioView } from '@/views/ScenarioView';
21import { SimulationView } from '@/views/SimulationView';
22import { Vita49StreamingView } from '@/views/Vita49StreamingView';
24export function MainLayout() {
25 const [activeView, setActiveView] = useState('scenario');
26 const [settingsOpen, setSettingsOpen] = useState(false);
27 const [aboutOpen, setAboutOpen] = useState(false);
28 const [licensesOpen, setLicensesOpen] = useState(false);
29 const [viewportWidth, setViewportWidth] = useState(() =>
30 typeof window === 'undefined' ? 1440 : window.innerWidth
32 const [liveLogDrawerWidth, setLiveLogDrawerWidth] = useState<number | null>(
35 const logOpen = useFersLogStore((state) => state.isOpen);
36 const appendLog = useFersLogStore((state) => state.appendLog);
37 const preferredLogDrawerWidth = useFersLogStore(
38 (state) => state.drawerWidth
40 const setPreferredLogDrawerWidth = useFersLogStore(
41 (state) => state.setDrawerWidth
43 const { open, message, severity } = useScenarioStore(
44 (state) => state.notificationSnackbar
46 const hideNotification = useScenarioStore(
47 (state) => state.hideNotification
49 const advanceNotification = useScenarioStore(
50 (state) => state.advanceNotification
55 let unlistenLog: (() => void) | undefined;
57 listen<FersLogEntry>('fers-log', (event) => {
58 appendLog(event.payload);
59 }).then((unlisten) => {
61 unlistenLog = unlisten;
74 const handleResize = () => {
75 setViewportWidth(window.innerWidth);
79 window.addEventListener('resize', handleResize);
82 window.removeEventListener('resize', handleResize);
88 setLiveLogDrawerWidth(null);
92 const sidebarWidth = 60;
93 const requestedLogDrawerWidth =
94 liveLogDrawerWidth ?? preferredLogDrawerWidth;
95 const maxLogDrawerWidth = Math.max(0, viewportWidth - sidebarWidth);
96 const effectiveLogDrawerWidth = logOpen
97 ? getEffectiveLogDrawerWidth(requestedLogDrawerWidth, maxLogDrawerWidth)
100 const handleLogDrawerResize = (nextWidth: number) => {
101 setLiveLogDrawerWidth(clampLogDrawerWidth(nextWidth));
104 const handleLogDrawerResizeEnd = (nextWidth: number) => {
105 const clampedWidth = clampLogDrawerWidth(nextWidth);
106 setLiveLogDrawerWidth(null);
107 setPreferredLogDrawerWidth(clampedWidth);
117 position: 'fixed', // Ensure it stays in viewport
120 bgcolor: 'background.default',
124 activeView={activeView}
125 onViewChange={setActiveView}
126 onSettingsClick={() => setSettingsOpen(true)}
127 onAboutClick={() => setAboutOpen(true)}
129 {logOpen && effectiveLogDrawerWidth > 0 && (
131 leftOffset={sidebarWidth}
132 width={effectiveLogDrawerWidth}
133 onResize={handleLogDrawerResize}
134 onResizeEnd={handleLogDrawerResizeEnd}
141 minWidth: 0, // Allow shrinking below content size
143 overflow: 'hidden', // Prevent overflow
144 position: 'relative',
145 bgcolor: 'background.default',
148 {/* Render all views but only display the active one */}
151 display: activeView === 'scenario' ? 'flex' : 'none',
156 <ScenarioView isActive={activeView === 'scenario'} />
160 display: activeView === 'assets' ? 'block' : 'none',
169 display: activeView === 'simulation' ? 'block' : 'none',
178 display: activeView === 'vita49' ? 'block' : 'none',
183 <Vita49StreamingView />
188 onClose={() => setSettingsOpen(false)}
192 onClose={() => setAboutOpen(false)}
193 onLicensesClick={() => {
195 setLicensesOpen(true);
200 onClose={() => setLicensesOpen(false)}
204 autoHideDuration={6000}
205 onClose={hideNotification}
206 anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
207 TransitionProps={{ onExited: advanceNotification }}
210 onClose={hideNotification}
213 sx={{ width: '100%' }}