Files
firezone/rust/gui-client/src-frontend/components/App.tsx
Thomas Eizinger 1317bbb9e2 refactor(gui-client): replace tslink with tauri-specta (#10031)
Despite still being in development, the `tauri-specta` project already
proves to be quite useful. It allows us to generate TypeScript bindings
for our commands and events, creating a type-safe contract between the
frontend and the backend.

For example, this ensures that the TypeScript code calls a command
actually with the required parameters and thus avoids runtime failures.

Similarly, the frontend can listen on type-safe events without having to
use any magic strings.
2025-07-28 21:37:24 +00:00

175 lines
5.4 KiB
TypeScript

import {
Bars3Icon,
CogIcon,
DocumentMagnifyingGlassIcon,
HomeIcon,
InformationCircleIcon,
SwatchIcon,
WrenchScrewdriverIcon,
} from "@heroicons/react/24/solid";
import {
Sidebar,
SidebarCollapse,
SidebarItemGroup,
SidebarItems,
} from "flowbite-react";
import React, { useEffect, useState } from "react";
import { Route, Routes } from "react-router";
import About from "./AboutPage";
import AdvancedSettingsPage from "./AdvancedSettingsPage";
import ReactRouterSidebarItem from "./ReactRouterSidebarItem";
import ColorPalette from "./ColorPalettePage";
import Diagnostics from "./DiagnosticsPage";
import GeneralSettingsPage from "./GeneralSettingsPage";
import Overview from "./OverviewPage";
import {
AdvancedSettingsViewModel,
commands,
events,
FileCount,
GeneralSettingsViewModel,
SessionViewModel,
} from "../generated/bindings";
export default function App() {
const [session, setSession] = useState<SessionViewModel | null>(null);
const [logCount, setLogCount] = useState<FileCount | null>(null);
const [generalSettings, setGeneralSettings] =
useState<GeneralSettingsViewModel | null>(null);
const [advancedSettings, setAdvancedSettings] =
useState<AdvancedSettingsViewModel | null>(null);
useEffect(() => {
const sessionChangedUnlisten = events.sessionChanged.listen((e) => {
const session = e.payload;
console.log("session_changed", { session });
setSession(session);
});
const generalSettingsChangedUnlisten = events.generalSettingsChanged.listen(
(e) => {
const generalSettings = e.payload;
console.log("general_settings_changed", { settings: generalSettings });
setGeneralSettings(generalSettings);
}
);
const advancedSettingsChangedUnlisten =
events.advancedSettingsChanged.listen((e) => {
const advancedSettings = e.payload;
console.log("advanced_settings_changed", {
settings: advancedSettings,
});
setAdvancedSettings(advancedSettings);
});
const logsRecountedUnlisten = events.logsRecounted.listen((e) => {
const file_count = e.payload;
console.log("logs_recounted", { file_count });
setLogCount(file_count);
});
commands.updateState(); // Let the backend know that we (re)-initialised
return () => {
sessionChangedUnlisten.then((unlistenFn) => unlistenFn());
generalSettingsChangedUnlisten.then((unlistenFn) => unlistenFn());
advancedSettingsChangedUnlisten.then((unlistenFn) => unlistenFn());
logsRecountedUnlisten.then((unlistenFn) => unlistenFn());
};
}, []);
const isDev = import.meta.env.DEV;
return (
<div className="h-screen bg-neutral-50 flex flex-row">
<Sidebar
aria-label="Sidebar"
className="w-52 flex-shrink-0 border-r border-neutral-200"
>
<SidebarItems>
<SidebarItemGroup>
<ReactRouterSidebarItem icon={HomeIcon} href="/overview">
Overview
</ReactRouterSidebarItem>
<SidebarCollapse label="Settings" open={true} icon={Bars3Icon}>
<ReactRouterSidebarItem icon={CogIcon} href="/general-settings">
General
</ReactRouterSidebarItem>
<ReactRouterSidebarItem
icon={WrenchScrewdriverIcon}
href="/advanced-settings"
>
Advanced
</ReactRouterSidebarItem>
</SidebarCollapse>
<ReactRouterSidebarItem
icon={DocumentMagnifyingGlassIcon}
href="/diagnostics"
>
Diagnostics
</ReactRouterSidebarItem>
<ReactRouterSidebarItem icon={InformationCircleIcon} href="/about">
About
</ReactRouterSidebarItem>
</SidebarItemGroup>
{isDev && (
<SidebarItemGroup>
<ReactRouterSidebarItem icon={SwatchIcon} href="/colour-palette">
Color Palette
</ReactRouterSidebarItem>
</SidebarItemGroup>
)}
</SidebarItems>
</Sidebar>
<main className="flex-grow overflow-auto">
<Routes>
<Route
path="/overview"
element={
<Overview
session={session}
signIn={commands.signIn}
signOut={commands.signOut}
/>
}
/>
<Route
path="/general-settings"
element={
<GeneralSettingsPage
settings={generalSettings}
saveSettings={commands.applyGeneralSettings}
resetSettings={commands.resetGeneralSettings}
/>
}
/>
<Route
path="/advanced-settings"
element={
<AdvancedSettingsPage
settings={advancedSettings}
saveSettings={commands.applyAdvancedSettings}
resetSettings={commands.resetAdvancedSettings}
/>
}
/>
<Route
path="/diagnostics"
element={
<Diagnostics
logCount={logCount}
exportLogs={commands.exportLogs}
clearLogs={commands.clearLogs}
/>
}
/>
<Route path="/about" element={<About />} />
<Route path="/colour-palette" element={<ColorPalette />} />
</Routes>
</main>
</div>
);
}