ghost-dao-interface/src/App.jsx
2025-08-03 17:33:35 +03:00

217 lines
9.2 KiB
JavaScript

import "./style.scss";
import { useMediaQuery } from "@mui/material";
import CssBaseline from "@mui/material/CssBaseline";
import { styled, ThemeProvider } from "@mui/material/styles";
import { lazy, Suspense, useCallback, useEffect, useState } from "react";
import toast, { Toaster } from "react-hot-toast";
import { Navigate, Route, Routes, useLocation } from "react-router-dom";
import { useAccount, useConnect, useChainId, useConfig, usePublicClient, injected } from "wagmi";
import Messages from "./components/Messages/Messages";
import NavDrawer from "./components/Sidebar/NavDrawer";
import Sidebar from "./components/Sidebar/Sidebar";
import TopBar from "./components/TopBar/TopBar";
import { shouldTriggerSafetyCheck } from "./helpers";
import { isNetworkAvailable } from "./constants";
import useTheme from "./hooks/useTheme";
import { dark as darkTheme } from "./themes/dark.js";
import { girth as gTheme } from "./themes/girth.js";
import { light as lightTheme } from "./themes/light.js";
// Dynamic Imports for code splitting
const Bonds = lazy(() => import("./containers/Bond/Bonds"));
const BondModalContainer = lazy(() => import("./containers/Bond/BondModal"));
const StakeContainer = lazy(() => import("./containers/Stake/StakeContainer"));
const TreasuryDashboard = lazy(() => import("./containers/TreasuryDashboard/TreasuryDashboard"));
const Faucet = lazy(() => import("./containers/Faucet/Faucet"));
const Dex = lazy(() => import("./containers/Dex/Dex"));
const Bridge = lazy(() => import("./containers/Bridge/Bridge"));
const NotFound = lazy(() => import("./containers/NotFound/NotFound"));
const PREFIX = "App";
const classes = {
drawer: `${PREFIX}-drawer`,
content: `${PREFIX}-content`,
contentShift: `${PREFIX}-contentShift`,
toolbar: `${PREFIX}-toolbar`,
drawerPaper: `${PREFIX}-drawerPaper`,
notification: `${PREFIX}-notification`,
};
const StyledDiv = styled("div")(({ theme }) => ({
[`& .${classes.drawer}`]: {
[theme.breakpoints.up("md")]: {
width: drawerWidth,
flexShrink: 0,
},
},
[`& .${classes.content}`]: {
flexGrow: 1,
padding: "15px",
transition: theme.transitions.create("margin", {
easing: theme.transitions.easing.sharp,
duration: transitionDuration,
}),
marginLeft: drawerWidth,
marginTop: "-48.5px",
},
[`& .${classes.contentShift}`]: {
transition: theme.transitions.create("margin", {
easing: theme.transitions.easing.easeOut,
duration: transitionDuration,
}),
marginLeft: 0,
},
// necessary for content to be below app bar
[`& .${classes.toolbar}`]: theme.mixins.toolbar,
[`& .${classes.drawerPaper}`]: {
width: drawerWidth,
},
[`& .${classes.notification}`]: {
marginLeft: "264px",
},
}));
// 😬 Sorry for all the console logging
const DEBUG = false;
// 🛰 providers
if (DEBUG) console.log("📡 Connecting to Mainnet Ethereum");
// 🔭 block explorer URL
// const blockExplorer = targetNetwork.blockExplorer;
const drawerWidth = 264;
const transitionDuration = 969;
function App() {
const location = useLocation();
const [theme, toggleTheme] = useTheme();
const config = useConfig();
const { connect, error: errorMessage } = useConnect();
const tryConnectInjected = () => connect({ connector: injected() });
const bondIndexes = [];
const [wrongNetworkToastId, setWrongNetworkToastId] = useState(null);
const [isSidebarExpanded, setIsSidebarExpanded] = useState(false);
const [mobileOpen, setMobileOpen] = useState(false);
const { address = "", chainId: addressChainId, isConnected, isReconnecting } = useAccount();
const provider = usePublicClient();
const chainId = useChainId();
const isSmallerScreen = useMediaQuery("(max-width: 1047px)");
const isSmallScreen = useMediaQuery("(max-width: 600px)");
useEffect(() => {
if (shouldTriggerSafetyCheck()) {
toast.success("Safety Check: Always verify you're on app.dao.ghostchain.io!", { duration: 5000 });
}
}, []);
useEffect(() => {
if (isConnected && chainId !== addressChainId) {
const toastId = toast.loading("You are connected to wrong network. Use one of the deployed networks please.", {
position: 'bottom-right'
});
setWrongNetworkToastId(toastId);
} else {
if (wrongNetworkToastId) {
toast.dismiss(wrongNetworkToastId);
setWrongNetworkToastId(null);
}
}
}, [chainId, addressChainId, isConnected])
useEffect(() => {
if (errorMessage) {
toast.error("No injected connectors found in your browser. Download Metamask extension or visit EIP-1193 for more details.", { duration: 5000 });
}
}, [errorMessage]);
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen);
};
const handleSidebarClose = () => {
setIsSidebarExpanded(false);
};
const themeMode = theme === "light" ? lightTheme : theme === "dark" ? darkTheme : gTheme;
const chainExists = isNetworkAvailable(chainId, addressChainId);
useEffect(() => {
if (isSidebarExpanded) handleSidebarClose();
}, [location]);
return (
<StyledDiv>
<ThemeProvider theme={themeMode}>
<CssBaseline />
<div className={`app ${isSmallerScreen && "tablet"} ${isSmallScreen && "mobile"} ${theme}`}>
<Toaster>{t => <Messages toast={t} />}</Toaster>
<TopBar
wrongNetworkToastId={wrongNetworkToastId}
setWrongNetworkToastId={setWrongNetworkToastId}
address={address}
chainId={addressChainId ? addressChainId : chainId}
chainExists={chainExists}
handleDrawerToggle={handleDrawerToggle}
connect={tryConnectInjected}
/>
<nav className={classes.drawer}>
{isSmallerScreen ? (
<NavDrawer chainId={chainId} addressChainId={addressChainId} mobileOpen={mobileOpen} handleDrawerToggle={handleDrawerToggle} />
) : (
<Sidebar chainId={chainId} addressChainId={addressChainId} />
)}
</nav>
<div className={`${classes.content} ${isSmallerScreen && classes.contentShift}`}>
<Suspense fallback={<div></div>}>
<Routes>
<Route path="/" element={<Navigate to={chainExists ? "/dashboard" : "/empty"} />} />
{chainExists &&
<>
<Route path="/dashboard" element={<TreasuryDashboard chainId={addressChainId ? addressChainId : chainId} />} />
<Route path="/bonds" element={<Bonds connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} />
<Route path="/bonds/:id" element={<BondModalContainer connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} />
<Route path="/stake" element={<StakeContainer connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId}/>} />
<Route path="/faucet" element={<Faucet config={config} connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} />
<Route path="/bridge" element={<Bridge config={config} connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} />
<Route path="/dex/:name" element={<Dex connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} />
</>
}
<Route path="/empty" element={<NotFound
wrongNetworkToastId={wrongNetworkToastId}
setWrongNetworkToastId={setWrongNetworkToastId}
chainId={addressChainId ? addressChainId : chainId}
chainExists={chainExists}
/>} />
<Route path="*" element={<NotFound
wrongNetworkToastId={wrongNetworkToastId}
setWrongNetworkToastId={setWrongNetworkToastId}
chainId={addressChainId ? addressChainId : chainId}
chainExists={chainExists}
/>} />
</Routes>
</Suspense>
</div>
</div>
</ThemeProvider>
</StyledDiv>
);
}
export default App;