Refactor context (#427)

* Move AppContext to top level

* Refactor context
This commit is contained in:
Reckless_Satoshi 2023-04-20 14:52:03 +00:00 committed by GitHub
parent e3b7f1f268
commit 28ef253020
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 168 additions and 311 deletions

View File

@ -1,9 +1,8 @@
import React, { Suspense, useState, useEffect } from 'react'; import React, { StrictMode, Suspense } from 'react';
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client';
import Main from './basic/Main'; import Main from './basic/Main';
import { CssBaseline } from '@mui/material'; import { CssBaseline, ThemeProvider } from '@mui/material';
import { AppContextProvider } from './contexts/AppContext'; import { AppContext, useAppStore } from './contexts/AppContext';
import { ThemeProvider, createTheme, Theme } from '@mui/material/styles';
import UnsafeAlert from './components/UnsafeAlert'; import UnsafeAlert from './components/UnsafeAlert';
import TorConnectionBadge from './components/TorConnection'; import TorConnectionBadge from './components/TorConnection';
@ -11,53 +10,31 @@ import { I18nextProvider } from 'react-i18next';
import i18n from './i18n/Web'; import i18n from './i18n/Web';
import { systemClient } from './services/System'; import { systemClient } from './services/System';
import { Settings } from './models';
import ErrorBoundary from './components/ErrorBoundary'; 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 App = (): JSX.Element => {
const [theme, setTheme] = useState<Theme>(makeTheme(new Settings())); const store = useAppStore();
const [settings, setSettings] = useState<Settings>(new Settings());
useEffect(() => {
setTheme(makeTheme(settings));
}, [settings.fontSize, settings.mode]);
useEffect(() => {
i18n.changeLanguage(settings.language);
}, []);
return ( return (
<ErrorBoundary> <StrictMode>
<Suspense fallback='loading language'> <ErrorBoundary>
<I18nextProvider i18n={i18n}> <Suspense fallback='loading'>
<ThemeProvider theme={theme}> <I18nextProvider i18n={i18n}>
<AppContextProvider settings={settings} setSettings={setSettings}> <AppContext.Provider value={store}>
<CssBaseline /> <ThemeProvider theme={store.theme}>
{window.NativeRobosats === undefined ? <UnsafeAlert /> : <TorConnectionBadge />} <CssBaseline />
<Main /> {window.NativeRobosats === undefined ? <UnsafeAlert /> : <TorConnectionBadge />}
</AppContextProvider> <Main />
</ThemeProvider> </ThemeProvider>
</I18nextProvider> </AppContext.Provider>
</Suspense> </I18nextProvider>
</ErrorBoundary> </Suspense>
</ErrorBoundary>
</StrictMode>
); );
}; };
const loadApp = () => { const loadApp = () => {
// waits until the environment is ready for the Android WebView app
if (systemClient.loading) { if (systemClient.loading) {
setTimeout(loadApp, 200); setTimeout(loadApp, 200);
} else { } else {

View File

@ -10,7 +10,7 @@ import BookTable from '../../components/BookTable';
// Icons // Icons
import { BarChart, FormatListBulleted } from '@mui/icons-material'; import { BarChart, FormatListBulleted } from '@mui/icons-material';
import { AppContext, AppContextProps } from '../../contexts/AppContext'; import { AppContext, UseAppStoreType } from '../../contexts/AppContext';
const BookPage = (): JSX.Element => { const BookPage = (): JSX.Element => {
const { const {
@ -24,7 +24,7 @@ const BookPage = (): JSX.Element => {
book, book,
setDelay, setDelay,
setOrder, setOrder,
} = useContext<AppContextProps>(AppContext); } = useContext<UseAppStoreType>(AppContext);
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
const [view, setView] = useState<'list' | 'depth'>('list'); const [view, setView] = useState<'list' | 'depth'>('list');

View File

@ -14,7 +14,7 @@ import RobotAvatar from '../components/RobotAvatar';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import Notifications from '../components/Notifications'; 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; const Router = window.NativeRobosats === undefined ? BrowserRouter : MemoryRouter;
@ -29,11 +29,10 @@ const Main = (): JSX.Element => {
page, page,
setPage, setPage,
slideDirection, slideDirection,
closeAll,
setOpen, setOpen,
windowSize, windowSize,
navbarHeight, navbarHeight,
} = useContext<AppContextProps>(AppContext); } = useContext<UseAppStoreType>(AppContext);
return ( return (
<Router> <Router>
@ -41,7 +40,11 @@ const Main = (): JSX.Element => {
style={{ display: 'none' }} style={{ display: 'none' }}
nickname={robot.nickname} nickname={robot.nickname}
baseUrl={baseUrl} baseUrl={baseUrl}
onLoad={() => setRobot({ ...robot, avatarLoaded: true })} onLoad={() =>
setRobot((robot) => {
return { ...robot, avatarLoaded: true };
})
}
/> />
<Notifications <Notifications
order={order} order={order}

View File

@ -9,7 +9,7 @@ import {
UpdateClientDialog, UpdateClientDialog,
} from '../../components/Dialogs'; } from '../../components/Dialogs';
import { pn } from '../../utils'; import { pn } from '../../utils';
import { AppContext, AppContextProps } from '../../contexts/AppContext'; import { AppContext, UseAppStoreType, closeAll } from '../../contexts/AppContext';
export interface OpenDialogs { export interface OpenDialogs {
more: boolean; more: boolean;
@ -23,18 +23,8 @@ export interface OpenDialogs {
} }
const MainDialogs = (): JSX.Element => { const MainDialogs = (): JSX.Element => {
const { const { open, setOpen, info, limits, robot, setRobot, setPage, setCurrentOrder, baseUrl } =
open, useContext<UseAppStoreType>(AppContext);
setOpen,
info,
limits,
closeAll,
robot,
setRobot,
setPage,
setCurrentOrder,
baseUrl,
} = useContext<AppContextProps>(AppContext);
const [maxAmount, setMaxAmount] = useState<string>('...loading...'); const [maxAmount, setMaxAmount] = useState<string>('...loading...');

View File

@ -8,7 +8,7 @@ import { filterOrders } from '../../utils';
import MakerForm from '../../components/MakerForm'; import MakerForm from '../../components/MakerForm';
import BookTable from '../../components/BookTable'; import BookTable from '../../components/BookTable';
import { AppContext, AppContextProps } from '../../contexts/AppContext'; import { AppContext, UseAppStoreType } from '../../contexts/AppContext';
import { NoRobotDialog } from '../../components/Dialogs'; import { NoRobotDialog } from '../../components/Dialogs';
const MakerPage = (): JSX.Element => { const MakerPage = (): JSX.Element => {
@ -24,7 +24,7 @@ const MakerPage = (): JSX.Element => {
setPage, setPage,
setOrder, setOrder,
setDelay, setDelay,
} = useContext<AppContextProps>(AppContext); } = useContext<UseAppStoreType>(AppContext);
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();

View File

@ -1,8 +1,8 @@
import React, { useEffect, useState } from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useTheme, styled, Grid, IconButton } from '@mui/material'; import { useTheme, styled, Grid, IconButton } from '@mui/material';
import Tooltip, { TooltipProps, tooltipClasses } from '@mui/material/Tooltip'; import Tooltip, { TooltipProps, tooltipClasses } from '@mui/material/Tooltip';
import { closeAll } from '../../contexts/AppContext';
import { OpenDialogs } from '../MainDialogs'; import { OpenDialogs } from '../MainDialogs';
import { BubbleChart, Info, People, PriceChange, School } from '@mui/icons-material'; import { BubbleChart, Info, People, PriceChange, School } from '@mui/icons-material';
@ -22,11 +22,10 @@ const StyledTooltip = styled(({ className, ...props }: TooltipProps) => (
interface MoreTooltipProps { interface MoreTooltipProps {
open: OpenDialogs; open: OpenDialogs;
setOpen: (state: OpenDialogs) => void; setOpen: (state: OpenDialogs) => void;
closeAll: OpenDialogs;
children: JSX.Element; children: JSX.Element;
} }
const MoreTooltip = ({ open, setOpen, closeAll, children }: MoreTooltipProps): JSX.Element => { const MoreTooltip = ({ open, setOpen, children }: MoreTooltipProps): JSX.Element => {
const { t } = useTranslation(); const { t } = useTranslation();
const theme = useTheme(); const theme = useTheme();
return ( return (

View File

@ -15,7 +15,7 @@ import {
MoreHoriz, MoreHoriz,
} from '@mui/icons-material'; } from '@mui/icons-material';
import RobotAvatar from '../../components/RobotAvatar'; import RobotAvatar from '../../components/RobotAvatar';
import { AppContext, AppContextProps } from '../../contexts/AppContext'; import { AppContext, UseAppStoreType, closeAll } from '../../contexts/AppContext';
interface NavBarProps { interface NavBarProps {
width: number; width: number;
@ -31,10 +31,9 @@ const NavBar = ({ width, height }: NavBarProps): JSX.Element => {
setSlideDirection, setSlideDirection,
open, open,
setOpen, setOpen,
closeAll,
currentOrder, currentOrder,
baseUrl, baseUrl,
} = useContext<AppContextProps>(AppContext); } = useContext<UseAppStoreType>(AppContext);
const theme = useTheme(); const theme = useTheme();
const { t } = useTranslation(); const { t } = useTranslation();

View File

@ -7,7 +7,7 @@ import TradeBox from '../../components/TradeBox';
import OrderDetails from '../../components/OrderDetails'; import OrderDetails from '../../components/OrderDetails';
import { apiClient } from '../../services/api'; import { apiClient } from '../../services/api';
import { AppContext, AppContextProps } from '../../contexts/AppContext'; import { AppContext, UseAppStoreType } from '../../contexts/AppContext';
const OrderPage = (): JSX.Element => { const OrderPage = (): JSX.Element => {
const { const {
@ -23,7 +23,7 @@ const OrderPage = (): JSX.Element => {
setPage, setPage,
baseUrl, baseUrl,
navbarHeight, navbarHeight,
} = useContext<AppContextProps>(AppContext); } = useContext<UseAppStoreType>(AppContext);
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
const params = useParams(); const params = useParams();

View File

@ -18,7 +18,7 @@ import RobotAvatar from '../../components/RobotAvatar';
import TokenInput from './TokenInput'; import TokenInput from './TokenInput';
import { Page } from '../NavBar'; import { Page } from '../NavBar';
import { Slot, Robot } from '../../models'; import { Slot, Robot } from '../../models';
import { AppContext, AppContextProps } from '../../contexts/AppContext'; import { AppContext, UseAppStoreType } from '../../contexts/AppContext';
import { genBase62Token } from '../../utils'; import { genBase62Token } from '../../utils';
import { LoadingButton } from '@mui/lab'; import { LoadingButton } from '@mui/lab';
@ -53,7 +53,7 @@ const RobotProfile = ({
width, width,
}: RobotProfileProps): JSX.Element => { }: RobotProfileProps): JSX.Element => {
const { currentSlot, garage, setCurrentSlot, windowSize } = const { currentSlot, garage, setCurrentSlot, windowSize } =
useContext<AppContextProps>(AppContext); useContext<UseAppStoreType>(AppContext);
const { t } = useTranslation(); const { t } = useTranslation();
const theme = useTheme(); const theme = useTheme();
const navigate = useNavigate(); const navigate = useNavigate();

View File

@ -8,7 +8,6 @@ import { systemClient } from '../../services/System';
interface TokenInputProps { interface TokenInputProps {
robot: Robot; robot: Robot;
editable?: boolean; editable?: boolean;
showDownload?: boolean;
fullWidth?: boolean; fullWidth?: boolean;
loading?: boolean; loading?: boolean;
setRobot: (state: Robot) => void; setRobot: (state: Robot) => void;
@ -42,16 +41,6 @@ const TokenInput = ({
setShowCopied(false); setShowCopied(false);
}, [inputToken]); }, [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) { if (loading) {
return <LinearProgress sx={{ height: '0.8em' }} />; return <LinearProgress sx={{ height: '0.8em' }} />;
} else { } else {
@ -84,7 +73,9 @@ const TokenInput = ({
systemClient.copyToClipboard(inputToken); systemClient.copyToClipboard(inputToken);
setShowCopied(true); setShowCopied(true);
setTimeout(() => setShowCopied(false), 1000); setTimeout(() => setShowCopied(false), 1000);
setRobot({ ...robot, copiedToken: true }); setRobot((robot) => {
return { ...robot, copiedToken: true };
});
}} }}
> >
<ContentCopy sx={{ width: '1em', height: '1em' }} /> <ContentCopy sx={{ width: '1em', height: '1em' }} />

View File

@ -13,18 +13,17 @@ import {
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { Robot } from '../../models'; import { Robot } from '../../models';
import { apiClient } from '../../services/api';
import Onboarding from './Onboarding'; import Onboarding from './Onboarding';
import Welcome from './Welcome'; import Welcome from './Welcome';
import RobotProfile from './RobotProfile'; import RobotProfile from './RobotProfile';
import Recovery from './Recovery'; import Recovery from './Recovery';
import { TorIcon } from '../../components/Icons'; import { TorIcon } from '../../components/Icons';
import { genKey } from '../../pgp'; import { genKey } from '../../pgp';
import { AppContext, AppContextProps } from '../../contexts/AppContext'; import { AppContext, UseAppStoreType } from '../../contexts/AppContext';
const RobotPage = (): JSX.Element => { const RobotPage = (): JSX.Element => {
const { robot, setRobot, setPage, setCurrentOrder, fetchRobot, torStatus, windowSize, baseUrl } = const { robot, setRobot, setPage, setCurrentOrder, fetchRobot, torStatus, windowSize, baseUrl } =
useContext<AppContextProps>(AppContext); useContext<UseAppStoreType>(AppContext);
const { t } = useTranslation(); const { t } = useTranslation();
const params = useParams(); const params = useParams();
const refCode = params.refCode; const refCode = params.refCode;
@ -138,7 +137,7 @@ const RobotPage = (): JSX.Element => {
<Onboarding <Onboarding
setView={setView} setView={setView}
robot={robot} robot={robot}
setRobot={() => null} setRobot={setRobot}
badRequest={badRequest} badRequest={badRequest}
inputToken={inputToken} inputToken={inputToken}
setInputToken={setInputToken} setInputToken={setInputToken}
@ -152,7 +151,7 @@ const RobotPage = (): JSX.Element => {
<RobotProfile <RobotProfile
setView={setView} setView={setView}
robot={robot} robot={robot}
setRobot={() => null} setRobot={setRobot}
setCurrentOrder={setCurrentOrder} setCurrentOrder={setCurrentOrder}
badRequest={badRequest} badRequest={badRequest}
getGenerateRobot={getGenerateRobot} getGenerateRobot={getGenerateRobot}
@ -170,7 +169,7 @@ const RobotPage = (): JSX.Element => {
<Recovery <Recovery
setView={setView} setView={setView}
robot={robot} robot={robot}
setRobot={() => null} setRobot={setRobot}
badRequest={badRequest} badRequest={badRequest}
inputToken={inputToken} inputToken={inputToken}
setInputToken={setInputToken} setInputToken={setInputToken}

View File

@ -1,13 +1,10 @@
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { useTranslation } from 'react-i18next'; import { Grid, Paper } from '@mui/material';
import { Grid, Paper, useTheme } from '@mui/material';
import SettingsForm from '../../components/SettingsForm'; import SettingsForm from '../../components/SettingsForm';
import { AppContextProps, AppContext } from '../../contexts/AppContext'; import { UseAppStoreType, AppContext } from '../../contexts/AppContext';
const SettingsPage = (): JSX.Element => { const SettingsPage = (): JSX.Element => {
const { windowSize, navbarHeight } = useContext<AppContextProps>(AppContext); const { windowSize, navbarHeight } = useContext<UseAppStoreType>(AppContext);
const theme = useTheme();
const { t } = useTranslation();
const maxHeight = (windowSize.height - navbarHeight) * 0.85 - 3; const maxHeight = (windowSize.height - navbarHeight) * 0.85 - 3;
return ( return (

View File

@ -32,7 +32,7 @@ import RobotAvatar from '../RobotAvatar';
// Icons // Icons
import { Fullscreen, FullscreenExit, Refresh } from '@mui/icons-material'; import { Fullscreen, FullscreenExit, Refresh } from '@mui/icons-material';
import { AppContext, AppContextProps } from '../../contexts/AppContext'; import { AppContext, UseAppStoreType } from '../../contexts/AppContext';
interface BookTableProps { interface BookTableProps {
orderList?: PublicOrder[]; orderList?: PublicOrder[];
@ -63,7 +63,7 @@ const BookTable = ({
showNoResults = true, showNoResults = true,
onOrderClicked = () => null, onOrderClicked = () => null,
}: BookTableProps): JSX.Element => { }: BookTableProps): JSX.Element => {
const { book, fetchBook, fav, setFav, baseUrl } = useContext<AppContextProps>(AppContext); const { book, fetchBook, fav, setFav, baseUrl } = useContext<UseAppStoreType>(AppContext);
const { t } = useTranslation(); const { t } = useTranslation();
const theme = useTheme(); const theme = useTheme();

View File

@ -26,7 +26,7 @@ import { amountToString, matchMedian, statusBadgeColor } from '../../../utils';
import currencyDict from '../../../../static/assets/currencies.json'; import currencyDict from '../../../../static/assets/currencies.json';
import { PaymentStringAsIcons } from '../../PaymentMethods'; import { PaymentStringAsIcons } from '../../PaymentMethods';
import getNivoScheme from '../NivoScheme'; import getNivoScheme from '../NivoScheme';
import { AppContextProps, AppContext } from '../../../contexts/AppContext'; import { UseAppStoreType, AppContext } from '../../../contexts/AppContext';
interface DepthChartProps { interface DepthChartProps {
maxWidth: number; maxWidth: number;
@ -43,7 +43,7 @@ const DepthChart: React.FC<DepthChartProps> = ({
elevation = 6, elevation = 6,
onOrderClicked = () => null, onOrderClicked = () => null,
}) => { }) => {
const { book, fav, info, limits, baseUrl } = useContext<AppContextProps>(AppContext); const { book, fav, info, limits, baseUrl } = useContext<UseAppStoreType>(AppContext);
const { t } = useTranslation(); const { t } = useTranslation();
const theme = useTheme(); const theme = useTheme();
const [enrichedOrders, setEnrichedOrders] = useState<Order[]>([]); const [enrichedOrders, setEnrichedOrders] = useState<Order[]>([]);

View File

@ -9,7 +9,7 @@ import {
Button, Button,
} from '@mui/material'; } from '@mui/material';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { AppContext, AppContextProps } from '../../contexts/AppContext'; import { AppContext, UseAppStoreType } from '../../contexts/AppContext';
interface Props { interface Props {
open: boolean; open: boolean;
@ -17,7 +17,7 @@ interface Props {
} }
const NoRobotDialog = ({ open, onClose }: Props): JSX.Element => { const NoRobotDialog = ({ open, onClose }: Props): JSX.Element => {
const { setPage } = useContext<AppContextProps>(AppContext); const { setPage } = useContext<UseAppStoreType>(AppContext);
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();

View File

@ -14,7 +14,7 @@ import {
} from '@mui/material'; } from '@mui/material';
import { systemClient } from '../../services/System'; import { systemClient } from '../../services/System';
import ContentCopy from '@mui/icons-material/ContentCopy'; import ContentCopy from '@mui/icons-material/ContentCopy';
import { AppContext, AppContextProps } from '../../contexts/AppContext'; import { AppContext, UseAppStoreType } from '../../contexts/AppContext';
interface Props { interface Props {
open: boolean; open: boolean;
@ -24,7 +24,7 @@ interface Props {
} }
const StoreTokenDialog = ({ open, onClose, onClickBack, onClickDone }: Props): JSX.Element => { const StoreTokenDialog = ({ open, onClose, onClickBack, onClickDone }: Props): JSX.Element => {
const { robot } = useContext<AppContextProps>(AppContext); const { robot } = useContext<UseAppStoreType>(AppContext);
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (

View File

@ -12,6 +12,8 @@ interface ErrorBoundaryState {
} }
export default class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> { export default class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
// 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) { constructor(props: ErrorBoundaryProps) {
super(props); super(props);
this.state = { this.state = {

View File

@ -40,7 +40,7 @@ import { amountToString, computeSats, pn } from '../../utils';
import { SelfImprovement, Lock, HourglassTop, DeleteSweep, Edit } from '@mui/icons-material'; import { SelfImprovement, Lock, HourglassTop, DeleteSweep, Edit } from '@mui/icons-material';
import { LoadingButton } from '@mui/lab'; import { LoadingButton } from '@mui/lab';
import { AppContext, AppContextProps } from '../../contexts/AppContext'; import { AppContext, UseAppStoreType } from '../../contexts/AppContext';
interface MakerFormProps { interface MakerFormProps {
disableRequest?: boolean; disableRequest?: boolean;
@ -64,7 +64,7 @@ const MakerForm = ({
hasRobot = true, hasRobot = true,
}: MakerFormProps): JSX.Element => { }: MakerFormProps): JSX.Element => {
const { fav, setFav, limits, fetchLimits, info, maker, setMaker, setPage, baseUrl } = const { fav, setFav, limits, fetchLimits, info, maker, setMaker, setPage, baseUrl } =
useContext<AppContextProps>(AppContext); useContext<UseAppStoreType>(AppContext);
const { t } = useTranslation(); const { t } = useTranslation();
const theme = useTheme(); const theme = useTheme();

View File

@ -22,7 +22,6 @@ import { apiClient } from '../../services/api';
import { Order } from '../../models'; import { Order } from '../../models';
import { ConfirmationDialog } from '../Dialogs'; import { ConfirmationDialog } from '../Dialogs';
import { Page } from '../../basic/NavBar';
import { LoadingButton } from '@mui/lab'; import { LoadingButton } from '@mui/lab';
interface TakeButtonProps { interface TakeButtonProps {

View File

@ -1,6 +1,6 @@
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { AppContextProps, AppContext } from '../../contexts/AppContext'; import { UseAppStoreType, AppContext } from '../../contexts/AppContext';
import { import {
Grid, Grid,
Paper, Paper,
@ -35,7 +35,7 @@ interface SettingsFormProps {
} }
const SettingsForm = ({ dense = false, showNetwork = false }: SettingsFormProps): JSX.Element => { const SettingsForm = ({ dense = false, showNetwork = false }: SettingsFormProps): JSX.Element => {
const { fav, setFav, settings, setSettings } = useContext<AppContextProps>(AppContext); const { fav, setFav, settings, setSettings } = useContext<UseAppStoreType>(AppContext);
const theme = useTheme(); const theme = useTheme();
const { t } = useTranslation(); const { t } = useTranslation();
const fontSizes = [ const fontSizes = [

View File

@ -2,7 +2,7 @@ import React, { useContext } from 'react';
import { Box, CircularProgress, Tooltip } from '@mui/material'; import { Box, CircularProgress, Tooltip } from '@mui/material';
import { TorIcon } from './Icons'; import { TorIcon } from './Icons';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { AppContext, AppContextProps } from '../contexts/AppContext'; import { AppContext, UseAppStoreType } from '../contexts/AppContext';
interface TorIndicatorProps { interface TorIndicatorProps {
color: 'inherit' | 'error' | 'warning' | 'success' | 'primary' | 'secondary' | 'info' | undefined; color: 'inherit' | 'error' | 'warning' | 'success' | 'primary' | 'secondary' | 'info' | undefined;
@ -55,7 +55,7 @@ const TorIndicator = ({
}; };
const TorConnectionBadge = (): JSX.Element => { const TorConnectionBadge = (): JSX.Element => {
const { torStatus } = useContext<AppContextProps>(AppContext); const { torStatus } = useContext<UseAppStoreType>(AppContext);
const { t } = useTranslation(); const { t } = useTranslation();
if (window?.NativeRobosats == null) { if (window?.NativeRobosats == null) {

View File

@ -1,11 +1,11 @@
import React, { useState, useEffect, useContext } from 'react'; 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 { useTranslation, Trans } from 'react-i18next';
import { Paper, Alert, AlertTitle, Button, Link } from '@mui/material'; import { Paper, Alert, AlertTitle, Button, Link } from '@mui/material';
import { getHost } from '../utils'; import { getHost } from '../utils';
const UnsafeAlert = (): JSX.Element => { const UnsafeAlert = (): JSX.Element => {
const { windowSize } = useContext<AppContextProps>(AppContext); const { windowSize } = useContext<UseAppStoreType>(AppContext);
const { t } = useTranslation(); const { t } = useTranslation();
const [show, setShow] = useState<boolean>(true); const [show, setShow] = useState<boolean>(true);

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'; import React, { createContext, useEffect, useState } from 'react';
import { Page } from '../basic/NavBar'; import { Page } from '../basic/NavBar';
import { OpenDialogs } from '../basic/MainDialogs'; import { OpenDialogs } from '../basic/MainDialogs';
@ -23,7 +23,8 @@ import { checkVer, getHost, tokenStrength } from '../utils';
import { sha256 } from 'js-sha256'; import { sha256 } from 'js-sha256';
import defaultCoordinators from '../../static/federation.json'; 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) { const getWindowSize = function (fontSize: number) {
// returns window size in EM units // returns window size in EM units
@ -72,61 +73,10 @@ export interface fetchRobotProps {
export type TorStatus = 'NOTINIT' | 'STARTING' | '"Done"' | 'DONE'; 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 | '' = const entryPage: Page | '' =
window.NativeRobosats === undefined ? window.location.pathname.split('/')[1] : ''; window.NativeRobosats === undefined ? window.location.pathname.split('/')[1] : '';
const closeAll = { export const closeAll = {
more: false, more: false,
learn: false, learn: false,
community: false, community: false,
@ -138,66 +88,40 @@ const closeAll = {
profile: false, profile: false,
}; };
// export const initialState = { const makeTheme = function (settings: Settings) {
// federation: defaultFederation, const theme: Theme = createTheme({
// setFederation: () => null, palette: {
// settings: new Settings(), mode: settings.mode,
// setSettings: () => null, background: {
// book: { orders: [], loading: true }, default: settings.mode === 'dark' ? '#070707' : '#fff',
// setBook: () => null, },
// fetchBook: () => null, },
// limits: { typography: { fontSize: settings.fontSize },
// 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),
// }
export interface AppContextProviderProps { return theme;
children: React.ReactNode; };
settings: Settings;
setSettings: (state: Settings) => void;
}
export const AppContextProvider = ({ const initialSettings = new Settings();
children, const initialTheme = makeTheme(initialSettings);
settings, const initialGarage = new Garage();
setSettings, const initialSlot = initialGarage.slots.length - 1;
}: AppContextProviderProps): JSX.Element => { const initialRobot = new Robot(initialGarage.slots[initialSlot].robot);
const theme = useTheme();
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<Theme>(initialTheme);
const [settings, setSettings] = useState<Settings>(initialSettings);
useEffect(() => {
setTheme(makeTheme(settings));
}, [settings.fontSize, settings.mode]);
useEffect(() => {
i18n.changeLanguage(settings.language);
}, []);
// All app data structured // All app data structured
const [torStatus, setTorStatus] = useState<TorStatus>('NOTINIT'); const [torStatus, setTorStatus] = useState<TorStatus>('NOTINIT');
@ -206,13 +130,9 @@ export const AppContextProvider = ({
list: [], list: [],
loading: true, loading: true,
}); });
const [garage, setGarage] = useState<Garage>(() => { const [garage, setGarage] = useState<Garage>(initialGarage);
const initialState = { setGarage }; const [currentSlot, setCurrentSlot] = useState<number>(initialSlot);
const newGarage = new Garage(initialState); const [robot, setRobot] = useState<Robot>(initialRobot);
return newGarage;
});
const [currentSlot, setCurrentSlot] = useState<number>(garage.slots.length - 1);
const [robot, setRobot] = useState<Robot>(new Robot(garage.slots[currentSlot].robot));
const [maker, setMaker] = useState<Maker>(defaultMaker); const [maker, setMaker] = useState<Maker>(defaultMaker);
const [info, setInfo] = useState<Info>(defaultInfo); const [info, setInfo] = useState<Info>(defaultInfo);
const [coordinators, setCoordinators] = useState<Coordinator[]>(defaultCoordinators); const [coordinators, setCoordinators] = useState<Coordinator[]>(defaultCoordinators);
@ -224,8 +144,6 @@ export const AppContextProvider = ({
const [order, setOrder] = useState<Order | undefined>(undefined); const [order, setOrder] = useState<Order | undefined>(undefined);
const [badOrder, setBadOrder] = useState<string | undefined>(undefined); const [badOrder, setBadOrder] = useState<string | undefined>(undefined);
const entryPage: Page | '' =
window.NativeRobosats === undefined ? window.location.pathname.split('/')[1] : '';
const [page, setPage] = useState<Page>(entryPage == '' ? 'robot' : entryPage); const [page, setPage] = useState<Page>(entryPage == '' ? 'robot' : entryPage);
const [slideDirection, setSlideDirection] = useState<SlideDirection>({ const [slideDirection, setSlideDirection] = useState<SlideDirection>({
in: undefined, in: undefined,
@ -234,16 +152,6 @@ export const AppContextProvider = ({
const [currentOrder, setCurrentOrder] = useState<number | undefined>(undefined); const [currentOrder, setCurrentOrder] = useState<number | undefined>(undefined);
const navbarHeight = 2.5; 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<OpenDialogs>(closeAll); const [open, setOpen] = useState<OpenDialogs>(closeAll);
const [windowSize, setWindowSize] = useState<{ width: number; height: number }>( const [windowSize, setWindowSize] = useState<{ width: number; height: number }>(
@ -252,7 +160,7 @@ export const AppContextProvider = ({
useEffect(() => { useEffect(() => {
window.addEventListener('torStatus', (event) => { 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); setTimeout(() => setTorStatus(event?.detail), event?.detail === '"Done"' ? 5000 : 0);
}); });
}, []); }, []);
@ -486,54 +394,50 @@ export const AppContextProvider = ({
} }
}, [open.profile, baseUrl]); }, [open.profile, baseUrl]);
return ( return {
<AppContext.Provider theme,
value={{ torStatus,
torStatus, settings,
settings, setSettings,
setSettings, book,
book, setBook,
setBook, garage,
garage, setGarage,
setGarage, currentSlot,
currentSlot, setCurrentSlot,
setCurrentSlot, fetchBook,
fetchBook, limits,
limits, info,
info, setLimits,
setLimits, fetchLimits,
fetchLimits, maker,
maker, setMaker,
setMaker, clearOrder,
clearOrder, robot,
robot, setRobot,
setRobot, fetchRobot,
fetchRobot, baseUrl,
baseUrl, setBaseUrl,
setBaseUrl, fav,
fav, setFav,
setFav, order,
order, setOrder,
setOrder, badOrder,
badOrder, setBadOrder,
setBadOrder, setDelay,
setDelay, page,
page, setPage,
setPage, slideDirection,
slideDirection, setSlideDirection,
setSlideDirection, currentOrder,
currentOrder, setCurrentOrder,
setCurrentOrder, navbarHeight,
navbarHeight, open,
closeAll, setOpen,
open, windowSize,
setOpen, };
windowSize,
}}
>
{children}
</AppContext.Provider>
);
}; };
export const AppContext = React.createContext(); export type UseAppStoreType = ReturnType<typeof useAppStore>;
export const AppContext = createContext<UseAppStoreType | null>(null);

View File

@ -10,22 +10,19 @@ const emptySlot: Slot = { robot: new Robot(), order: null };
class Garage { class Garage {
constructor(initialState?: Garage) { constructor(initialState?: Garage) {
const slotsDump: string | undefined = systemClient.getItem('garage'); const slotsDump: string = systemClient.getItem('garage');
if (initialState?.slots === undefined && slotsDump != '') { if (initialState?.slots === undefined && slotsDump != '') {
this.slots = JSON.parse(slotsDump); this.slots = JSON.parse(slotsDump);
console.log('Robot Garage was loaded from local storage'); console.log('Robot Garage was loaded from local storage');
} else { } else {
this.slots = [emptySlot]; this.slots = [emptySlot];
} }
this.setGarage = initialState?.setGarage ?? (() => {});
} }
slots: Slot[] = [emptySlot]; slots: Slot[] = [emptySlot];
setGarage: (state: Garage) => void = () => {};
save = () => { save = () => {
systemClient.setItem('garage', JSON.stringify(this.slots)); systemClient.setItem('garage', JSON.stringify(this.slots));
this.setGarage(new Garage(this));
}; };
delete = () => { delete = () => {

View File

@ -12,7 +12,7 @@ import {
import ToolBar from '../pro/ToolBar'; import ToolBar from '../pro/ToolBar';
import LandingDialog from '../pro/LandingDialog'; 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 // To Do. Add dotted grid when layout is not frozen
// ${freeze ? // ${freeze ?
@ -71,7 +71,7 @@ const Main = (): JSX.Element => {
windowSize, windowSize,
badOrder, badOrder,
setBadOrder, setBadOrder,
} = useContext<AppContextProps>(AppContext); } = useContext<UseAppStoreType>(AppContext);
const theme = useTheme(); const theme = useTheme();
const em: number = theme.typography.fontSize; const em: number = theme.typography.fontSize;

View File

@ -1,5 +1,5 @@
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { AppContext, AppContextProps } from '../../contexts/AppContext'; import { AppContext, UseAppStoreType } from '../../contexts/AppContext';
import { Book, Favorites } from '../../models'; import { Book, Favorites } from '../../models';
import { Paper } from '@mui/material'; import { Paper } from '@mui/material';
@ -34,7 +34,7 @@ const BookWidget = React.forwardRef(
}: BookWidgetProps, }: BookWidgetProps,
ref, ref,
) => { ) => {
const { book, windowSize, fav } = useContext<AppContextProps>(AppContext); const { book, windowSize, fav } = useContext<UseAppStoreType>(AppContext);
return React.useMemo(() => { return React.useMemo(() => {
return ( return (
<Paper elevation={3} style={{ width: '100%', height: '100%' }}> <Paper elevation={3} style={{ width: '100%', height: '100%' }}>

View File

@ -1,5 +1,5 @@
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { AppContext, AppContextProps } from '../../contexts/AppContext'; import { AppContext, UseAppStoreType } from '../../contexts/AppContext';
import { Paper, useTheme } from '@mui/material'; import { Paper, useTheme } from '@mui/material';
import DepthChart from '../../components/Charts/DepthChart'; import DepthChart from '../../components/Charts/DepthChart';
@ -27,7 +27,7 @@ const DepthChartWidget = React.forwardRef(
ref, ref,
) => { ) => {
const theme = useTheme(); const theme = useTheme();
const { fav, book, limits } = useContext<AppContextProps>(AppContext); const { fav, book, limits } = useContext<UseAppStoreType>(AppContext);
return React.useMemo(() => { return React.useMemo(() => {
return ( return (
<Paper elevation={3} style={{ width: '100%', height: '100%' }}> <Paper elevation={3} style={{ width: '100%', height: '100%' }}>

View File

@ -1,5 +1,5 @@
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { AppContext, AppContextProps } from '../../contexts/AppContext'; import { AppContext, UseAppStoreType } from '../../contexts/AppContext';
import MakerForm from '../../components/MakerForm'; import MakerForm from '../../components/MakerForm';
import { LimitList, Maker, Favorites } from '../../models'; import { LimitList, Maker, Favorites } from '../../models';
@ -15,7 +15,7 @@ interface MakerWidgetProps {
const MakerWidget = React.forwardRef( const MakerWidget = React.forwardRef(
({ style, className, onMouseDown, onMouseUp, onTouchEnd }: MakerWidgetProps, ref) => { ({ style, className, onMouseDown, onMouseUp, onTouchEnd }: MakerWidgetProps, ref) => {
const { maker, fav, limits } = useContext<AppContextProps>(AppContext); const { maker, fav, limits } = useContext<UseAppStoreType>(AppContext);
return React.useMemo(() => { return React.useMemo(() => {
return ( return (
<Paper <Paper

View File

@ -1,5 +1,5 @@
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { AppContextProps, AppContext } from '../../contexts/AppContext'; import { UseAppStoreType, AppContext } from '../../contexts/AppContext';
import { Settings } from '../../models'; import { Settings } from '../../models';
import { Paper } from '@mui/material'; import { Paper } from '@mui/material';
import SettingsForm from '../../components/SettingsForm'; import SettingsForm from '../../components/SettingsForm';
@ -14,7 +14,7 @@ interface SettingsWidgetProps {
const SettingsWidget = React.forwardRef( const SettingsWidget = React.forwardRef(
({ style, className, onMouseDown, onMouseUp, onTouchEnd }: SettingsWidgetProps, ref) => { ({ style, className, onMouseDown, onMouseUp, onTouchEnd }: SettingsWidgetProps, ref) => {
const { settings } = useContext<AppContextProps>(AppContext); const { settings } = useContext<UseAppStoreType>(AppContext);
return React.useMemo(() => { return React.useMemo(() => {
return ( return (
<Paper <Paper