1import React, {useState, useEffect} from 'react';
2import {createBrowserRouter, Outlet, RouterProvider} from 'react-router-dom';
3import '@fontsource/inter/300.css';
4import '@fontsource/inter/400.css';
5import '@fontsource/inter/500.css';
6import '@fontsource/inter/600.css';
7import '@fontsource/inter/700.css';
8import "@/common/styles/default.sass";
9import "@/common/styles/spinner.sass";
10import HeaderComponent from "./common/components/Header";
11import {SpeedtestProvider} from "./common/contexts/Speedtests";
12import {ConfigProvider} from "./common/contexts/Config";
13import {StatusProvider} from "./common/contexts/Status";
14import {AlertProvider} from "@/common/contexts/Alert";
15import {ThemeProvider} from "@/common/contexts/Theme";
16import {PreferencesProvider} from "@/common/contexts/Preferences";
17import i18n from './i18n';
18import Loading from "@/pages/Loading";
19import Error from "@/pages/Error";
20import RouteError from "@/pages/RouteError";
21import {ToastNotificationProvider} from "@/common/contexts/ToastNotification";
22import {NodeProvider} from "@/common/contexts/Node";
23import {library} from '@fortawesome/fontawesome-svg-core';
24import {faBell, faBellConcierge, faDatabase, faGlobe, faHeartPulse} from '@fortawesome/free-solid-svg-icons';
25import {faDiscord, faTelegram} from "@fortawesome/free-brands-svg-icons";
26import {PushOverIcon} from "@/common/assets/icons/pushover";
27import Nodes from "@/pages/Nodes";
28import Statistics from "@/pages/Statistics";
29import Home from "@/pages/Home";
30
31library.add(faBell, faBellConcierge, faDatabase, faGlobe, faHeartPulse, faDiscord, faTelegram);
32library.add(PushOverIcon);
33
34const Providers = ({children}) => (
35 <ThemeProvider>
36 <PreferencesProvider>
37 <AlertProvider>
38 <ToastNotificationProvider>
39 <ConfigProvider>
40 <NodeProvider>
41 <SpeedtestProvider>
42 <StatusProvider>
43 {children}
44 </StatusProvider>
45 </SpeedtestProvider>
46 </NodeProvider>
47 </ConfigProvider>
48 </ToastNotificationProvider>
49 </AlertProvider>
50 </PreferencesProvider>
51 </ThemeProvider>
52);
53
54const App = () => {
55 const [translationsLoaded, setTranslationsLoaded] = useState(false);
56 const [translationError, setTranslationError] = useState(false);
57
58 useEffect(() => {
59 i18n.on("initialized", () => setTranslationsLoaded(true));
60 i18n.on("failedLoading", () => setTranslationError(true));
61 }, []);
62
63 const router = createBrowserRouter([
64 {
65 path: "/",
66 element: (
67 <Providers>
68 <HeaderComponent/>
69 <main><Outlet/></main>
70 </Providers>
71 ),
72 errorElement: <RouteError />,
73 children: [
74 {path: "/", element: <Home/>},
75 {path: "/nodes", element: <Nodes/>},
76 {path: "/statistics", element: <Statistics/>}
77 ]
78 }
79 ]);
80
81 if (!translationsLoaded && !translationError) {
82 return <Loading/>;
83 }
84
85 if (translationError) {
86 return <Error text="Failed to load translations"/>;
87 }
88
89 return <RouterProvider router={router}/>;
90};
91
92export default App;
93