From 28ef253020db367c69be57172bbe50dc84edeb73 Mon Sep 17 00:00:00 2001 From: Reckless_Satoshi <90936742+Reckless-Satoshi@users.noreply.github.com> Date: Thu, 20 Apr 2023 14:52:03 +0000 Subject: [PATCH] Refactor context (#427) * Move AppContext to top level * Refactor context --- frontend/src/App.tsx | 63 ++--- frontend/src/basic/BookPage/index.tsx | 4 +- frontend/src/basic/Main.tsx | 11 +- frontend/src/basic/MainDialogs/index.tsx | 16 +- frontend/src/basic/MakerPage/index.tsx | 4 +- frontend/src/basic/NavBar/MoreTooltip.tsx | 7 +- frontend/src/basic/NavBar/NavBar.tsx | 5 +- frontend/src/basic/OrderPage/index.tsx | 4 +- frontend/src/basic/RobotPage/RobotProfile.tsx | 4 +- frontend/src/basic/RobotPage/TokenInput.tsx | 15 +- frontend/src/basic/RobotPage/index.tsx | 11 +- frontend/src/basic/SettingsPage/index.tsx | 9 +- frontend/src/components/BookTable/index.tsx | 4 +- .../components/Charts/DepthChart/index.tsx | 4 +- frontend/src/components/Dialogs/NoRobot.tsx | 4 +- .../src/components/Dialogs/StoreToken.tsx | 4 +- frontend/src/components/ErrorBoundary.tsx | 2 + .../src/components/MakerForm/MakerForm.tsx | 4 +- .../components/OrderDetails/TakeButton.tsx | 1 - .../src/components/SettingsForm/index.tsx | 4 +- frontend/src/components/TorConnection.tsx | 4 +- frontend/src/components/UnsafeAlert.tsx | 4 +- .../{AppContext.tsx => AppContext.ts} | 266 ++++++------------ frontend/src/models/Garage.model.ts | 5 +- frontend/src/pro/Main.tsx | 4 +- frontend/src/pro/Widgets/Book.tsx | 4 +- frontend/src/pro/Widgets/Depth.tsx | 4 +- frontend/src/pro/Widgets/Maker.tsx | 4 +- frontend/src/pro/Widgets/Settings.tsx | 4 +- 29 files changed, 168 insertions(+), 311 deletions(-) rename frontend/src/contexts/{AppContext.tsx => AppContext.ts} (69%) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 86f6c647..256e702b 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,9 +1,8 @@ -import React, { Suspense, useState, useEffect } from 'react'; +import React, { StrictMode, Suspense } from 'react'; import ReactDOM from 'react-dom/client'; import Main from './basic/Main'; -import { CssBaseline } from '@mui/material'; -import { AppContextProvider } from './contexts/AppContext'; -import { ThemeProvider, createTheme, Theme } from '@mui/material/styles'; +import { CssBaseline, ThemeProvider } from '@mui/material'; +import { AppContext, useAppStore } from './contexts/AppContext'; import UnsafeAlert from './components/UnsafeAlert'; import TorConnectionBadge from './components/TorConnection'; @@ -11,53 +10,31 @@ import { I18nextProvider } from 'react-i18next'; import i18n from './i18n/Web'; import { systemClient } from './services/System'; -import { Settings } from './models'; import ErrorBoundary from './components/ErrorBoundary'; -const makeTheme = function (settings: Settings) { - const theme: Theme = createTheme({ - palette: { - mode: settings.mode, - background: { - default: settings.mode === 'dark' ? '#070707' : '#fff', - }, - }, - typography: { fontSize: settings.fontSize }, - }); - - return theme; -}; - const App = (): JSX.Element => { - const [theme, setTheme] = useState(makeTheme(new Settings())); - const [settings, setSettings] = useState(new Settings()); - - useEffect(() => { - setTheme(makeTheme(settings)); - }, [settings.fontSize, settings.mode]); - - useEffect(() => { - i18n.changeLanguage(settings.language); - }, []); - + const store = useAppStore(); return ( - - - - - - - {window.NativeRobosats === undefined ? : } -
- - - - - + + + + + + + + {window.NativeRobosats === undefined ? : } +
+ + + + + + ); }; const loadApp = () => { + // waits until the environment is ready for the Android WebView app if (systemClient.loading) { setTimeout(loadApp, 200); } else { diff --git a/frontend/src/basic/BookPage/index.tsx b/frontend/src/basic/BookPage/index.tsx index 0ad94cff..463fd4c0 100644 --- a/frontend/src/basic/BookPage/index.tsx +++ b/frontend/src/basic/BookPage/index.tsx @@ -10,7 +10,7 @@ import BookTable from '../../components/BookTable'; // Icons import { BarChart, FormatListBulleted } from '@mui/icons-material'; -import { AppContext, AppContextProps } from '../../contexts/AppContext'; +import { AppContext, UseAppStoreType } from '../../contexts/AppContext'; const BookPage = (): JSX.Element => { const { @@ -24,7 +24,7 @@ const BookPage = (): JSX.Element => { book, setDelay, setOrder, - } = useContext(AppContext); + } = useContext(AppContext); const { t } = useTranslation(); const navigate = useNavigate(); const [view, setView] = useState<'list' | 'depth'>('list'); diff --git a/frontend/src/basic/Main.tsx b/frontend/src/basic/Main.tsx index 95e98548..f36e265c 100644 --- a/frontend/src/basic/Main.tsx +++ b/frontend/src/basic/Main.tsx @@ -14,7 +14,7 @@ import RobotAvatar from '../components/RobotAvatar'; import { useTranslation } from 'react-i18next'; import Notifications from '../components/Notifications'; -import { AppContextProps, AppContext } from '../contexts/AppContext'; +import { UseAppStoreType, AppContext, closeAll } from '../contexts/AppContext'; const Router = window.NativeRobosats === undefined ? BrowserRouter : MemoryRouter; @@ -29,11 +29,10 @@ const Main = (): JSX.Element => { page, setPage, slideDirection, - closeAll, setOpen, windowSize, navbarHeight, - } = useContext(AppContext); + } = useContext(AppContext); return ( @@ -41,7 +40,11 @@ const Main = (): JSX.Element => { style={{ display: 'none' }} nickname={robot.nickname} baseUrl={baseUrl} - onLoad={() => setRobot({ ...robot, avatarLoaded: true })} + onLoad={() => + setRobot((robot) => { + return { ...robot, avatarLoaded: true }; + }) + } /> { - const { - open, - setOpen, - info, - limits, - closeAll, - robot, - setRobot, - setPage, - setCurrentOrder, - baseUrl, - } = useContext(AppContext); + const { open, setOpen, info, limits, robot, setRobot, setPage, setCurrentOrder, baseUrl } = + useContext(AppContext); const [maxAmount, setMaxAmount] = useState('...loading...'); diff --git a/frontend/src/basic/MakerPage/index.tsx b/frontend/src/basic/MakerPage/index.tsx index 1360209c..88c0aee7 100644 --- a/frontend/src/basic/MakerPage/index.tsx +++ b/frontend/src/basic/MakerPage/index.tsx @@ -8,7 +8,7 @@ import { filterOrders } from '../../utils'; import MakerForm from '../../components/MakerForm'; import BookTable from '../../components/BookTable'; -import { AppContext, AppContextProps } from '../../contexts/AppContext'; +import { AppContext, UseAppStoreType } from '../../contexts/AppContext'; import { NoRobotDialog } from '../../components/Dialogs'; const MakerPage = (): JSX.Element => { @@ -24,7 +24,7 @@ const MakerPage = (): JSX.Element => { setPage, setOrder, setDelay, - } = useContext(AppContext); + } = useContext(AppContext); const { t } = useTranslation(); const navigate = useNavigate(); diff --git a/frontend/src/basic/NavBar/MoreTooltip.tsx b/frontend/src/basic/NavBar/MoreTooltip.tsx index 276addd0..e2674689 100644 --- a/frontend/src/basic/NavBar/MoreTooltip.tsx +++ b/frontend/src/basic/NavBar/MoreTooltip.tsx @@ -1,8 +1,8 @@ -import React, { useEffect, useState } from 'react'; +import React from 'react'; import { useTranslation } from 'react-i18next'; import { useTheme, styled, Grid, IconButton } from '@mui/material'; import Tooltip, { TooltipProps, tooltipClasses } from '@mui/material/Tooltip'; - +import { closeAll } from '../../contexts/AppContext'; import { OpenDialogs } from '../MainDialogs'; import { BubbleChart, Info, People, PriceChange, School } from '@mui/icons-material'; @@ -22,11 +22,10 @@ const StyledTooltip = styled(({ className, ...props }: TooltipProps) => ( interface MoreTooltipProps { open: OpenDialogs; setOpen: (state: OpenDialogs) => void; - closeAll: OpenDialogs; children: JSX.Element; } -const MoreTooltip = ({ open, setOpen, closeAll, children }: MoreTooltipProps): JSX.Element => { +const MoreTooltip = ({ open, setOpen, children }: MoreTooltipProps): JSX.Element => { const { t } = useTranslation(); const theme = useTheme(); return ( diff --git a/frontend/src/basic/NavBar/NavBar.tsx b/frontend/src/basic/NavBar/NavBar.tsx index 75e0eee6..f8da58ea 100644 --- a/frontend/src/basic/NavBar/NavBar.tsx +++ b/frontend/src/basic/NavBar/NavBar.tsx @@ -15,7 +15,7 @@ import { MoreHoriz, } from '@mui/icons-material'; import RobotAvatar from '../../components/RobotAvatar'; -import { AppContext, AppContextProps } from '../../contexts/AppContext'; +import { AppContext, UseAppStoreType, closeAll } from '../../contexts/AppContext'; interface NavBarProps { width: number; @@ -31,10 +31,9 @@ const NavBar = ({ width, height }: NavBarProps): JSX.Element => { setSlideDirection, open, setOpen, - closeAll, currentOrder, baseUrl, - } = useContext(AppContext); + } = useContext(AppContext); const theme = useTheme(); const { t } = useTranslation(); diff --git a/frontend/src/basic/OrderPage/index.tsx b/frontend/src/basic/OrderPage/index.tsx index 827fe64d..2817a024 100644 --- a/frontend/src/basic/OrderPage/index.tsx +++ b/frontend/src/basic/OrderPage/index.tsx @@ -7,7 +7,7 @@ import TradeBox from '../../components/TradeBox'; import OrderDetails from '../../components/OrderDetails'; import { apiClient } from '../../services/api'; -import { AppContext, AppContextProps } from '../../contexts/AppContext'; +import { AppContext, UseAppStoreType } from '../../contexts/AppContext'; const OrderPage = (): JSX.Element => { const { @@ -23,7 +23,7 @@ const OrderPage = (): JSX.Element => { setPage, baseUrl, navbarHeight, - } = useContext(AppContext); + } = useContext(AppContext); const { t } = useTranslation(); const navigate = useNavigate(); const params = useParams(); diff --git a/frontend/src/basic/RobotPage/RobotProfile.tsx b/frontend/src/basic/RobotPage/RobotProfile.tsx index a32892bf..19bc0361 100644 --- a/frontend/src/basic/RobotPage/RobotProfile.tsx +++ b/frontend/src/basic/RobotPage/RobotProfile.tsx @@ -18,7 +18,7 @@ import RobotAvatar from '../../components/RobotAvatar'; import TokenInput from './TokenInput'; import { Page } from '../NavBar'; import { Slot, Robot } from '../../models'; -import { AppContext, AppContextProps } from '../../contexts/AppContext'; +import { AppContext, UseAppStoreType } from '../../contexts/AppContext'; import { genBase62Token } from '../../utils'; import { LoadingButton } from '@mui/lab'; @@ -53,7 +53,7 @@ const RobotProfile = ({ width, }: RobotProfileProps): JSX.Element => { const { currentSlot, garage, setCurrentSlot, windowSize } = - useContext(AppContext); + useContext(AppContext); const { t } = useTranslation(); const theme = useTheme(); const navigate = useNavigate(); diff --git a/frontend/src/basic/RobotPage/TokenInput.tsx b/frontend/src/basic/RobotPage/TokenInput.tsx index 1a2a0ec2..c443e849 100644 --- a/frontend/src/basic/RobotPage/TokenInput.tsx +++ b/frontend/src/basic/RobotPage/TokenInput.tsx @@ -8,7 +8,6 @@ import { systemClient } from '../../services/System'; interface TokenInputProps { robot: Robot; editable?: boolean; - showDownload?: boolean; fullWidth?: boolean; loading?: boolean; setRobot: (state: Robot) => void; @@ -42,16 +41,6 @@ const TokenInput = ({ setShowCopied(false); }, [inputToken]); - const createJsonFile = () => { - return { - token: robot.token, - token_shannon_entropy: robot.shannonEntropy, - token_bit_entropy: robot.bitsEntropy, - public_key: robot.pubKey, - encrypted_private_key: robot.encPrivKey, - }; - }; - if (loading) { return ; } else { @@ -84,7 +73,9 @@ const TokenInput = ({ systemClient.copyToClipboard(inputToken); setShowCopied(true); setTimeout(() => setShowCopied(false), 1000); - setRobot({ ...robot, copiedToken: true }); + setRobot((robot) => { + return { ...robot, copiedToken: true }; + }); }} > diff --git a/frontend/src/basic/RobotPage/index.tsx b/frontend/src/basic/RobotPage/index.tsx index e14c972b..28c739f5 100644 --- a/frontend/src/basic/RobotPage/index.tsx +++ b/frontend/src/basic/RobotPage/index.tsx @@ -13,18 +13,17 @@ import { import { useParams } from 'react-router-dom'; import { Robot } from '../../models'; -import { apiClient } from '../../services/api'; import Onboarding from './Onboarding'; import Welcome from './Welcome'; import RobotProfile from './RobotProfile'; import Recovery from './Recovery'; import { TorIcon } from '../../components/Icons'; import { genKey } from '../../pgp'; -import { AppContext, AppContextProps } from '../../contexts/AppContext'; +import { AppContext, UseAppStoreType } from '../../contexts/AppContext'; const RobotPage = (): JSX.Element => { const { robot, setRobot, setPage, setCurrentOrder, fetchRobot, torStatus, windowSize, baseUrl } = - useContext(AppContext); + useContext(AppContext); const { t } = useTranslation(); const params = useParams(); const refCode = params.refCode; @@ -138,7 +137,7 @@ const RobotPage = (): JSX.Element => { null} + setRobot={setRobot} badRequest={badRequest} inputToken={inputToken} setInputToken={setInputToken} @@ -152,7 +151,7 @@ const RobotPage = (): JSX.Element => { null} + setRobot={setRobot} setCurrentOrder={setCurrentOrder} badRequest={badRequest} getGenerateRobot={getGenerateRobot} @@ -170,7 +169,7 @@ const RobotPage = (): JSX.Element => { null} + setRobot={setRobot} badRequest={badRequest} inputToken={inputToken} setInputToken={setInputToken} diff --git a/frontend/src/basic/SettingsPage/index.tsx b/frontend/src/basic/SettingsPage/index.tsx index 9f941d75..c9828eb1 100644 --- a/frontend/src/basic/SettingsPage/index.tsx +++ b/frontend/src/basic/SettingsPage/index.tsx @@ -1,13 +1,10 @@ import React, { useContext } from 'react'; -import { useTranslation } from 'react-i18next'; -import { Grid, Paper, useTheme } from '@mui/material'; +import { Grid, Paper } from '@mui/material'; import SettingsForm from '../../components/SettingsForm'; -import { AppContextProps, AppContext } from '../../contexts/AppContext'; +import { UseAppStoreType, AppContext } from '../../contexts/AppContext'; const SettingsPage = (): JSX.Element => { - const { windowSize, navbarHeight } = useContext(AppContext); - const theme = useTheme(); - const { t } = useTranslation(); + const { windowSize, navbarHeight } = useContext(AppContext); const maxHeight = (windowSize.height - navbarHeight) * 0.85 - 3; return ( diff --git a/frontend/src/components/BookTable/index.tsx b/frontend/src/components/BookTable/index.tsx index ac05b188..0f1487e0 100644 --- a/frontend/src/components/BookTable/index.tsx +++ b/frontend/src/components/BookTable/index.tsx @@ -32,7 +32,7 @@ import RobotAvatar from '../RobotAvatar'; // Icons import { Fullscreen, FullscreenExit, Refresh } from '@mui/icons-material'; -import { AppContext, AppContextProps } from '../../contexts/AppContext'; +import { AppContext, UseAppStoreType } from '../../contexts/AppContext'; interface BookTableProps { orderList?: PublicOrder[]; @@ -63,7 +63,7 @@ const BookTable = ({ showNoResults = true, onOrderClicked = () => null, }: BookTableProps): JSX.Element => { - const { book, fetchBook, fav, setFav, baseUrl } = useContext(AppContext); + const { book, fetchBook, fav, setFav, baseUrl } = useContext(AppContext); const { t } = useTranslation(); const theme = useTheme(); diff --git a/frontend/src/components/Charts/DepthChart/index.tsx b/frontend/src/components/Charts/DepthChart/index.tsx index 1c75ac38..f465f114 100644 --- a/frontend/src/components/Charts/DepthChart/index.tsx +++ b/frontend/src/components/Charts/DepthChart/index.tsx @@ -26,7 +26,7 @@ import { amountToString, matchMedian, statusBadgeColor } from '../../../utils'; import currencyDict from '../../../../static/assets/currencies.json'; import { PaymentStringAsIcons } from '../../PaymentMethods'; import getNivoScheme from '../NivoScheme'; -import { AppContextProps, AppContext } from '../../../contexts/AppContext'; +import { UseAppStoreType, AppContext } from '../../../contexts/AppContext'; interface DepthChartProps { maxWidth: number; @@ -43,7 +43,7 @@ const DepthChart: React.FC = ({ elevation = 6, onOrderClicked = () => null, }) => { - const { book, fav, info, limits, baseUrl } = useContext(AppContext); + const { book, fav, info, limits, baseUrl } = useContext(AppContext); const { t } = useTranslation(); const theme = useTheme(); const [enrichedOrders, setEnrichedOrders] = useState([]); diff --git a/frontend/src/components/Dialogs/NoRobot.tsx b/frontend/src/components/Dialogs/NoRobot.tsx index 155b4e4d..130a18ed 100644 --- a/frontend/src/components/Dialogs/NoRobot.tsx +++ b/frontend/src/components/Dialogs/NoRobot.tsx @@ -9,7 +9,7 @@ import { Button, } from '@mui/material'; import { useNavigate } from 'react-router-dom'; -import { AppContext, AppContextProps } from '../../contexts/AppContext'; +import { AppContext, UseAppStoreType } from '../../contexts/AppContext'; interface Props { open: boolean; @@ -17,7 +17,7 @@ interface Props { } const NoRobotDialog = ({ open, onClose }: Props): JSX.Element => { - const { setPage } = useContext(AppContext); + const { setPage } = useContext(AppContext); const { t } = useTranslation(); const navigate = useNavigate(); diff --git a/frontend/src/components/Dialogs/StoreToken.tsx b/frontend/src/components/Dialogs/StoreToken.tsx index ce6eb067..2bd82b26 100644 --- a/frontend/src/components/Dialogs/StoreToken.tsx +++ b/frontend/src/components/Dialogs/StoreToken.tsx @@ -14,7 +14,7 @@ import { } from '@mui/material'; import { systemClient } from '../../services/System'; import ContentCopy from '@mui/icons-material/ContentCopy'; -import { AppContext, AppContextProps } from '../../contexts/AppContext'; +import { AppContext, UseAppStoreType } from '../../contexts/AppContext'; interface Props { open: boolean; @@ -24,7 +24,7 @@ interface Props { } const StoreTokenDialog = ({ open, onClose, onClickBack, onClickDone }: Props): JSX.Element => { - const { robot } = useContext(AppContext); + const { robot } = useContext(AppContext); const { t } = useTranslation(); return ( diff --git a/frontend/src/components/ErrorBoundary.tsx b/frontend/src/components/ErrorBoundary.tsx index 0dd77e30..2d8b123c 100644 --- a/frontend/src/components/ErrorBoundary.tsx +++ b/frontend/src/components/ErrorBoundary.tsx @@ -12,6 +12,8 @@ interface ErrorBoundaryState { } export default class ErrorBoundary extends Component { + // In case the app crashes this component will restart it in 10 seconds + // It will also print an obnoxious error message (useful for end users to grab a screenshot and report) constructor(props: ErrorBoundaryProps) { super(props); this.state = { diff --git a/frontend/src/components/MakerForm/MakerForm.tsx b/frontend/src/components/MakerForm/MakerForm.tsx index 4bf77265..553dd2fa 100644 --- a/frontend/src/components/MakerForm/MakerForm.tsx +++ b/frontend/src/components/MakerForm/MakerForm.tsx @@ -40,7 +40,7 @@ import { amountToString, computeSats, pn } from '../../utils'; import { SelfImprovement, Lock, HourglassTop, DeleteSweep, Edit } from '@mui/icons-material'; import { LoadingButton } from '@mui/lab'; -import { AppContext, AppContextProps } from '../../contexts/AppContext'; +import { AppContext, UseAppStoreType } from '../../contexts/AppContext'; interface MakerFormProps { disableRequest?: boolean; @@ -64,7 +64,7 @@ const MakerForm = ({ hasRobot = true, }: MakerFormProps): JSX.Element => { const { fav, setFav, limits, fetchLimits, info, maker, setMaker, setPage, baseUrl } = - useContext(AppContext); + useContext(AppContext); const { t } = useTranslation(); const theme = useTheme(); diff --git a/frontend/src/components/OrderDetails/TakeButton.tsx b/frontend/src/components/OrderDetails/TakeButton.tsx index 906c65b5..25657699 100644 --- a/frontend/src/components/OrderDetails/TakeButton.tsx +++ b/frontend/src/components/OrderDetails/TakeButton.tsx @@ -22,7 +22,6 @@ import { apiClient } from '../../services/api'; import { Order } from '../../models'; import { ConfirmationDialog } from '../Dialogs'; -import { Page } from '../../basic/NavBar'; import { LoadingButton } from '@mui/lab'; interface TakeButtonProps { diff --git a/frontend/src/components/SettingsForm/index.tsx b/frontend/src/components/SettingsForm/index.tsx index f8a03d26..06323fba 100644 --- a/frontend/src/components/SettingsForm/index.tsx +++ b/frontend/src/components/SettingsForm/index.tsx @@ -1,6 +1,6 @@ import React, { useContext } from 'react'; import { useTranslation } from 'react-i18next'; -import { AppContextProps, AppContext } from '../../contexts/AppContext'; +import { UseAppStoreType, AppContext } from '../../contexts/AppContext'; import { Grid, Paper, @@ -35,7 +35,7 @@ interface SettingsFormProps { } const SettingsForm = ({ dense = false, showNetwork = false }: SettingsFormProps): JSX.Element => { - const { fav, setFav, settings, setSettings } = useContext(AppContext); + const { fav, setFav, settings, setSettings } = useContext(AppContext); const theme = useTheme(); const { t } = useTranslation(); const fontSizes = [ diff --git a/frontend/src/components/TorConnection.tsx b/frontend/src/components/TorConnection.tsx index 19bfbd1e..d4e72d9a 100644 --- a/frontend/src/components/TorConnection.tsx +++ b/frontend/src/components/TorConnection.tsx @@ -2,7 +2,7 @@ import React, { useContext } from 'react'; import { Box, CircularProgress, Tooltip } from '@mui/material'; import { TorIcon } from './Icons'; import { useTranslation } from 'react-i18next'; -import { AppContext, AppContextProps } from '../contexts/AppContext'; +import { AppContext, UseAppStoreType } from '../contexts/AppContext'; interface TorIndicatorProps { color: 'inherit' | 'error' | 'warning' | 'success' | 'primary' | 'secondary' | 'info' | undefined; @@ -55,7 +55,7 @@ const TorIndicator = ({ }; const TorConnectionBadge = (): JSX.Element => { - const { torStatus } = useContext(AppContext); + const { torStatus } = useContext(AppContext); const { t } = useTranslation(); if (window?.NativeRobosats == null) { diff --git a/frontend/src/components/UnsafeAlert.tsx b/frontend/src/components/UnsafeAlert.tsx index b682a3b2..51a0019e 100644 --- a/frontend/src/components/UnsafeAlert.tsx +++ b/frontend/src/components/UnsafeAlert.tsx @@ -1,11 +1,11 @@ import React, { useState, useEffect, useContext } from 'react'; -import { AppContext, AppContextProps } from '../contexts/AppContext'; +import { AppContext, UseAppStoreType } from '../contexts/AppContext'; import { useTranslation, Trans } from 'react-i18next'; import { Paper, Alert, AlertTitle, Button, Link } from '@mui/material'; import { getHost } from '../utils'; const UnsafeAlert = (): JSX.Element => { - const { windowSize } = useContext(AppContext); + const { windowSize } = useContext(AppContext); const { t } = useTranslation(); const [show, setShow] = useState(true); diff --git a/frontend/src/contexts/AppContext.tsx b/frontend/src/contexts/AppContext.ts similarity index 69% rename from frontend/src/contexts/AppContext.tsx rename to frontend/src/contexts/AppContext.ts index a2ee5e7e..f8ba3dcc 100644 --- a/frontend/src/contexts/AppContext.tsx +++ b/frontend/src/contexts/AppContext.ts @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { createContext, useEffect, useState } from 'react'; import { Page } from '../basic/NavBar'; import { OpenDialogs } from '../basic/MainDialogs'; @@ -23,7 +23,8 @@ import { checkVer, getHost, tokenStrength } from '../utils'; import { sha256 } from 'js-sha256'; import defaultCoordinators from '../../static/federation.json'; -import { useTheme } from '@mui/material'; +import { createTheme, Theme } from '@mui/material/styles'; +import i18n from '../i18n/Web'; const getWindowSize = function (fontSize: number) { // returns window size in EM units @@ -72,61 +73,10 @@ export interface fetchRobotProps { export type TorStatus = 'NOTINIT' | 'STARTING' | '"Done"' | 'DONE'; -export interface AppContextProps { - torStatus: TorStatus; - federation: Coordinator[]; - setFederation: (state: Coordinator[]) => void; - settings: Settings; - setSettings: (state: Settings) => void; - book: Book; - info: Info; - garage: Garage; - setGarage: (state: Garage) => void; - currentSlot: number; - setCurrentSlot: (state: number) => void; - setBook: (state: Book) => void; - fetchBook: () => void; - limits: { list: LimitList; loading: boolean }; - setLimits: (state: { list: LimitList; loading: boolean }) => void; - fetchLimits: () => void; - maker: Maker; - setMaker: (state: Maker) => void; - clearOrder: () => void; - robot: Robot; - setRobot: (state: Robot) => void; - focusedCoordinator: number; - setFocusedCoordinator: (state: number) => void; - baseUrl: string; - setBaseUrl: (state: string) => void; - fav: Favorites; - setFav: (state: Favorites) => void; - order: Order | undefined; - setOrder: (state: Order | undefined) => void; - badOrder: string; - setBadOrder: (state: string | undefined) => void; - setDelay: (state: number) => void; - page: Page; - setPage: (state: Page) => void; - slideDirection: SlideDirection; - setSlideDirection: (state: SlideDirection) => void; - currentOrder: number | undefined; - setCurrentOrder: (state: number) => void; - navbarHeight: number; - closeAll: OpenDialogs; - open: OpenDialogs; - setOpen: (state: OpenDialogs) => void; - windowSize: { width: number; height: number }; - clientVersion: { - semver: Version; - short: string; - long: string; - }; -} - const entryPage: Page | '' = window.NativeRobosats === undefined ? window.location.pathname.split('/')[1] : ''; -const closeAll = { +export const closeAll = { more: false, learn: false, community: false, @@ -138,66 +88,40 @@ const closeAll = { profile: false, }; -// export const initialState = { -// federation: defaultFederation, -// setFederation: () => null, -// settings: new Settings(), -// setSettings: () => null, -// book: { orders: [], loading: true }, -// setBook: () => null, -// fetchBook: () => null, -// limits: { -// list: [], -// loading: true, -// }, -// setLimits:() => null, -// fetchLimits: ()=> null, -// maker: defaultMaker, -// setMaker: () => null, -// clearOrder: () => null, -// robot: new Robot(), -// setRobot: () => null, -// info: defaultExchange, -// setExchange: () => null, -// focusedCoordinator: 0, -// setFocusedCoordinator: () => null, -// baseUrl: '', -// setBaseUrl: () => null, -// fav: { type: null, currency: 0 }, -// setFav: () => null, -// order: undefined, -// setOrder: () => null, -// badOrder: '', -// setBadOrder: () => null, -// setDelay: () => null, -// page: entryPage == '' ? 'robot' : entryPage, -// setPage: () => null, -// slideDirection: { -// in: undefined, -// out: undefined, -// }, -// setSlideDirection: () => null, -// currentOrder: undefined, -// setCurrentOrder: () => null, -// navbarHeight: 2.5, -// closeAll, -// open: closeAll, -// setOpen: () => null, -// windowSize: getWindowSize(14), -// } +const makeTheme = function (settings: Settings) { + const theme: Theme = createTheme({ + palette: { + mode: settings.mode, + background: { + default: settings.mode === 'dark' ? '#070707' : '#fff', + }, + }, + typography: { fontSize: settings.fontSize }, + }); -export interface AppContextProviderProps { - children: React.ReactNode; - settings: Settings; - setSettings: (state: Settings) => void; -} + return theme; +}; -export const AppContextProvider = ({ - children, - settings, - setSettings, -}: AppContextProviderProps): JSX.Element => { - const theme = useTheme(); +const initialSettings = new Settings(); +const initialTheme = makeTheme(initialSettings); +const initialGarage = new Garage(); +const initialSlot = initialGarage.slots.length - 1; +const initialRobot = new Robot(initialGarage.slots[initialSlot].robot); + +export const useAppStore = () => { + // State provided right at the top level of the app. A chaotic bucket of everything. + // Contains app-wide state and functions. Triggers re-renders on the full tree often. + + const [theme, setTheme] = useState(initialTheme); + const [settings, setSettings] = useState(initialSettings); + + useEffect(() => { + setTheme(makeTheme(settings)); + }, [settings.fontSize, settings.mode]); + + useEffect(() => { + i18n.changeLanguage(settings.language); + }, []); // All app data structured const [torStatus, setTorStatus] = useState('NOTINIT'); @@ -206,13 +130,9 @@ export const AppContextProvider = ({ list: [], loading: true, }); - const [garage, setGarage] = useState(() => { - const initialState = { setGarage }; - const newGarage = new Garage(initialState); - return newGarage; - }); - const [currentSlot, setCurrentSlot] = useState(garage.slots.length - 1); - const [robot, setRobot] = useState(new Robot(garage.slots[currentSlot].robot)); + const [garage, setGarage] = useState(initialGarage); + const [currentSlot, setCurrentSlot] = useState(initialSlot); + const [robot, setRobot] = useState(initialRobot); const [maker, setMaker] = useState(defaultMaker); const [info, setInfo] = useState(defaultInfo); const [coordinators, setCoordinators] = useState(defaultCoordinators); @@ -224,8 +144,6 @@ export const AppContextProvider = ({ const [order, setOrder] = useState(undefined); const [badOrder, setBadOrder] = useState(undefined); - const entryPage: Page | '' = - window.NativeRobosats === undefined ? window.location.pathname.split('/')[1] : ''; const [page, setPage] = useState(entryPage == '' ? 'robot' : entryPage); const [slideDirection, setSlideDirection] = useState({ in: undefined, @@ -234,16 +152,6 @@ export const AppContextProvider = ({ const [currentOrder, setCurrentOrder] = useState(undefined); const navbarHeight = 2.5; - const closeAll = { - more: false, - learn: false, - community: false, - info: false, - coordinator: false, - stats: false, - update: false, - profile: false, - }; const [open, setOpen] = useState(closeAll); const [windowSize, setWindowSize] = useState<{ width: number; height: number }>( @@ -252,7 +160,7 @@ export const AppContextProvider = ({ useEffect(() => { window.addEventListener('torStatus', (event) => { - // UX improv: delay the "Conencted" status by 10 secs to avoid long waits for first requests + // Trick to improve UX on Android webview: delay the "Connected to TOR" status by 5 secs to avoid long waits on the first request. setTimeout(() => setTorStatus(event?.detail), event?.detail === '"Done"' ? 5000 : 0); }); }, []); @@ -486,54 +394,50 @@ export const AppContextProvider = ({ } }, [open.profile, baseUrl]); - return ( - - {children} - - ); + return { + theme, + torStatus, + settings, + setSettings, + book, + setBook, + garage, + setGarage, + currentSlot, + setCurrentSlot, + fetchBook, + limits, + info, + setLimits, + fetchLimits, + maker, + setMaker, + clearOrder, + robot, + setRobot, + fetchRobot, + baseUrl, + setBaseUrl, + fav, + setFav, + order, + setOrder, + badOrder, + setBadOrder, + setDelay, + page, + setPage, + slideDirection, + setSlideDirection, + currentOrder, + setCurrentOrder, + navbarHeight, + open, + setOpen, + windowSize, + }; }; -export const AppContext = React.createContext(); +export type UseAppStoreType = ReturnType; + +export const AppContext = createContext(null); diff --git a/frontend/src/models/Garage.model.ts b/frontend/src/models/Garage.model.ts index 89d03d95..228e0ed4 100644 --- a/frontend/src/models/Garage.model.ts +++ b/frontend/src/models/Garage.model.ts @@ -10,22 +10,19 @@ const emptySlot: Slot = { robot: new Robot(), order: null }; class Garage { constructor(initialState?: Garage) { - const slotsDump: string | undefined = systemClient.getItem('garage'); + const slotsDump: string = systemClient.getItem('garage'); if (initialState?.slots === undefined && slotsDump != '') { this.slots = JSON.parse(slotsDump); console.log('Robot Garage was loaded from local storage'); } else { this.slots = [emptySlot]; } - this.setGarage = initialState?.setGarage ?? (() => {}); } slots: Slot[] = [emptySlot]; - setGarage: (state: Garage) => void = () => {}; save = () => { systemClient.setItem('garage', JSON.stringify(this.slots)); - this.setGarage(new Garage(this)); }; delete = () => { diff --git a/frontend/src/pro/Main.tsx b/frontend/src/pro/Main.tsx index 123c0fda..595a91be 100644 --- a/frontend/src/pro/Main.tsx +++ b/frontend/src/pro/Main.tsx @@ -12,7 +12,7 @@ import { import ToolBar from '../pro/ToolBar'; import LandingDialog from '../pro/LandingDialog'; -import { AppContext, AppContextProps } from '../contexts/AppContext'; +import { AppContext, UseAppStoreType } from '../contexts/AppContext'; // To Do. Add dotted grid when layout is not frozen // ${freeze ? @@ -71,7 +71,7 @@ const Main = (): JSX.Element => { windowSize, badOrder, setBadOrder, - } = useContext(AppContext); + } = useContext(AppContext); const theme = useTheme(); const em: number = theme.typography.fontSize; diff --git a/frontend/src/pro/Widgets/Book.tsx b/frontend/src/pro/Widgets/Book.tsx index 7c2328bb..4a764ffc 100644 --- a/frontend/src/pro/Widgets/Book.tsx +++ b/frontend/src/pro/Widgets/Book.tsx @@ -1,5 +1,5 @@ import React, { useContext } from 'react'; -import { AppContext, AppContextProps } from '../../contexts/AppContext'; +import { AppContext, UseAppStoreType } from '../../contexts/AppContext'; import { Book, Favorites } from '../../models'; import { Paper } from '@mui/material'; @@ -34,7 +34,7 @@ const BookWidget = React.forwardRef( }: BookWidgetProps, ref, ) => { - const { book, windowSize, fav } = useContext(AppContext); + const { book, windowSize, fav } = useContext(AppContext); return React.useMemo(() => { return ( diff --git a/frontend/src/pro/Widgets/Depth.tsx b/frontend/src/pro/Widgets/Depth.tsx index 81af2ef1..b3ead134 100644 --- a/frontend/src/pro/Widgets/Depth.tsx +++ b/frontend/src/pro/Widgets/Depth.tsx @@ -1,5 +1,5 @@ import React, { useContext } from 'react'; -import { AppContext, AppContextProps } from '../../contexts/AppContext'; +import { AppContext, UseAppStoreType } from '../../contexts/AppContext'; import { Paper, useTheme } from '@mui/material'; import DepthChart from '../../components/Charts/DepthChart'; @@ -27,7 +27,7 @@ const DepthChartWidget = React.forwardRef( ref, ) => { const theme = useTheme(); - const { fav, book, limits } = useContext(AppContext); + const { fav, book, limits } = useContext(AppContext); return React.useMemo(() => { return ( diff --git a/frontend/src/pro/Widgets/Maker.tsx b/frontend/src/pro/Widgets/Maker.tsx index ee36f7fc..c1a26ab3 100644 --- a/frontend/src/pro/Widgets/Maker.tsx +++ b/frontend/src/pro/Widgets/Maker.tsx @@ -1,5 +1,5 @@ import React, { useContext } from 'react'; -import { AppContext, AppContextProps } from '../../contexts/AppContext'; +import { AppContext, UseAppStoreType } from '../../contexts/AppContext'; import MakerForm from '../../components/MakerForm'; import { LimitList, Maker, Favorites } from '../../models'; @@ -15,7 +15,7 @@ interface MakerWidgetProps { const MakerWidget = React.forwardRef( ({ style, className, onMouseDown, onMouseUp, onTouchEnd }: MakerWidgetProps, ref) => { - const { maker, fav, limits } = useContext(AppContext); + const { maker, fav, limits } = useContext(AppContext); return React.useMemo(() => { return ( { - const { settings } = useContext(AppContext); + const { settings } = useContext(AppContext); return React.useMemo(() => { return (