mirror of
https://github.com/RoboSats/robosats.git
synced 2025-01-18 20:21:35 +00:00
Add App context (#369)
* Refactor fetchRobot * Add appcontext * Robot recovery fixes * Add usecontext on maker and settings forms * Add usecontext to booktable * Add useContext to order page and main dialogs * Small fixes
This commit is contained in:
parent
d815fb8a8e
commit
d88c2a5eff
7
.github/pull_request_template.md
vendored
7
.github/pull_request_template.md
vendored
@ -1,5 +1,8 @@
|
|||||||
## What does this PR do?
|
## What does this PR do?
|
||||||
Fixes #<PR_NUMBER/>
|
Fixes #<ISSUE_NUMBER>
|
||||||
|
|
||||||
|
This PR introduces/refactors/...
|
||||||
|
|
||||||
## Checklist before merging
|
## Checklist before merging
|
||||||
- [ ] Make sure you have installed and initialized [pre-commit](https://pre-commit.com). `pip install pre-commit` and `pre-commit install`
|
- [ ] If it's a frontend feature, I have ran prettier `cd frontend; npm run format`. If it's a mobile app feature I ran `cd mobile; npm run format`.
|
||||||
|
- [ ] If I added new phrases to the user interface, I have ran prettier `cd frontend/static/locales; python collect_phrases.py` to collect them for translation.
|
@ -21,17 +21,15 @@ repos:
|
|||||||
- id: check-docstring-first
|
- id: check-docstring-first
|
||||||
- repo: local
|
- repo: local
|
||||||
hooks:
|
hooks:
|
||||||
- id: collect-phrases
|
# - id: collect-phrases
|
||||||
name: collect-phrases
|
# name: Collect i18n phrases
|
||||||
stages:
|
# stages:
|
||||||
- commit
|
# - commit
|
||||||
- merge-commit
|
# - merge-commit
|
||||||
language: system
|
# language: system
|
||||||
files: ^frontend/src/
|
# files: ^frontend/
|
||||||
types_or: [javascript, jsx, ts, tsx] # uses https://github.com/pre-commit/identify
|
# types_or: [javascript, jsx, ts, tsx] # uses https://github.com/pre-commit/identify
|
||||||
entry: bash -c 'cd frontend/static/locales && python3 collect_phrases.py'
|
# entry: bash -c 'cd frontend/static/locales && python3 collect_phrases.py'
|
||||||
- repo: local
|
|
||||||
hooks:
|
|
||||||
- id: prettier-frontend
|
- id: prettier-frontend
|
||||||
name: prettier-frontend
|
name: prettier-frontend
|
||||||
stages:
|
stages:
|
||||||
|
@ -759,9 +759,8 @@ class UserView(APIView):
|
|||||||
if last_order:
|
if last_order:
|
||||||
context["last_order_id"] = last_order.id
|
context["last_order_id"] = last_order.id
|
||||||
|
|
||||||
# Sends the welcome back message, only if created +3 mins ago
|
# Sends the welcome back message.
|
||||||
if request.user.date_joined < (timezone.now() - timedelta(minutes=3)):
|
context["found"] = "We found your Robot avatar. Welcome back!"
|
||||||
context["found"] = "We found your Robot avatar. Welcome back!"
|
|
||||||
return Response(context, status=status.HTTP_202_ACCEPTED)
|
return Response(context, status=status.HTTP_202_ACCEPTED)
|
||||||
else:
|
else:
|
||||||
# It is unlikely, but maybe the nickname is taken (1 in 20 Billion chance)
|
# It is unlikely, but maybe the nickname is taken (1 in 20 Billion chance)
|
||||||
|
@ -2,6 +2,7 @@ import React, { Suspense, useState, useEffect } 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 } from '@mui/material';
|
||||||
|
import { AppContextProvider } from './contexts/AppContext';
|
||||||
import { ThemeProvider, createTheme, Theme } from '@mui/material/styles';
|
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';
|
||||||
@ -29,14 +30,6 @@ const makeTheme = function (settings: Settings) {
|
|||||||
const App = (): JSX.Element => {
|
const App = (): JSX.Element => {
|
||||||
const [theme, setTheme] = useState<Theme>(makeTheme(new Settings()));
|
const [theme, setTheme] = useState<Theme>(makeTheme(new Settings()));
|
||||||
const [settings, setSettings] = useState<Settings>(new Settings());
|
const [settings, setSettings] = useState<Settings>(new Settings());
|
||||||
const [torStatus, setTorStatus] = useState<string>('NOTINIT');
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
window.addEventListener('torStatus', (event) => {
|
|
||||||
// UX improv: delay the "Conencted" status by 10 secs to avoid long waits for first requests
|
|
||||||
setTimeout(() => setTorStatus(event?.detail), event?.detail === '"Done"' ? 10000 : 0);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTheme(makeTheme(settings));
|
setTheme(makeTheme(settings));
|
||||||
@ -50,13 +43,15 @@ const App = (): JSX.Element => {
|
|||||||
<Suspense fallback='loading language'>
|
<Suspense fallback='loading language'>
|
||||||
<I18nextProvider i18n={i18n}>
|
<I18nextProvider i18n={i18n}>
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<CssBaseline />
|
<AppContextProvider settings={settings} setSettings={setSettings}>
|
||||||
{window.NativeRobosats === undefined ? (
|
<CssBaseline />
|
||||||
<UnsafeAlert settings={settings} setSettings={setSettings} />
|
{window.NativeRobosats === undefined ? (
|
||||||
) : (
|
<UnsafeAlert settings={settings} setSettings={setSettings} />
|
||||||
<TorConnectionBadge torStatus={torStatus} />
|
) : (
|
||||||
)}
|
<TorConnectionBadge />
|
||||||
<Main settings={settings} setSettings={setSettings} torStatus={torStatus} />
|
)}
|
||||||
|
<Main />
|
||||||
|
</AppContextProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</I18nextProvider>
|
</I18nextProvider>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
@ -147,56 +147,36 @@ const BookPage = ({
|
|||||||
>
|
>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
<BookTable
|
<BookTable
|
||||||
clickRefresh={() => fetchBook()}
|
|
||||||
book={book}
|
|
||||||
fav={fav}
|
|
||||||
setFav={setFav}
|
|
||||||
maxWidth={maxBookTableWidth} // EM units
|
maxWidth={maxBookTableWidth} // EM units
|
||||||
maxHeight={windowSize.height * 0.825 - 5} // EM units
|
maxHeight={windowSize.height * 0.825 - 5} // EM units
|
||||||
fullWidth={windowSize.width} // EM units
|
fullWidth={windowSize.width} // EM units
|
||||||
fullHeight={windowSize.height} // EM units
|
fullHeight={windowSize.height} // EM units
|
||||||
defaultFullscreen={false}
|
defaultFullscreen={false}
|
||||||
onOrderClicked={onOrderClicked}
|
onOrderClicked={onOrderClicked}
|
||||||
baseUrl={baseUrl}
|
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
<DepthChart
|
<DepthChart
|
||||||
orders={book.orders}
|
|
||||||
lastDayPremium={lastDayPremium}
|
|
||||||
currency={fav.currency}
|
|
||||||
limits={limits.list}
|
|
||||||
maxWidth={chartWidthEm} // EM units
|
maxWidth={chartWidthEm} // EM units
|
||||||
maxHeight={windowSize.height * 0.825 - 5} // EM units
|
maxHeight={windowSize.height * 0.825 - 5} // EM units
|
||||||
onOrderClicked={onOrderClicked}
|
onOrderClicked={onOrderClicked}
|
||||||
baseUrl={baseUrl}
|
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
) : view === 'depth' ? (
|
) : view === 'depth' ? (
|
||||||
<DepthChart
|
<DepthChart
|
||||||
orders={book.orders}
|
|
||||||
lastDayPremium={lastDayPremium}
|
|
||||||
currency={fav.currency}
|
|
||||||
limits={limits.list}
|
|
||||||
maxWidth={windowSize.width * 0.8} // EM units
|
maxWidth={windowSize.width * 0.8} // EM units
|
||||||
maxHeight={windowSize.height * 0.825 - 5} // EM units
|
maxHeight={windowSize.height * 0.825 - 5} // EM units
|
||||||
onOrderClicked={onOrderClicked}
|
onOrderClicked={onOrderClicked}
|
||||||
baseUrl={baseUrl}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<BookTable
|
<BookTable
|
||||||
book={book}
|
|
||||||
clickRefresh={() => fetchBook()}
|
|
||||||
fav={fav}
|
|
||||||
setFav={setFav}
|
|
||||||
maxWidth={windowSize.width * 0.97} // EM units
|
maxWidth={windowSize.width * 0.97} // EM units
|
||||||
maxHeight={windowSize.height * 0.825 - 5} // EM units
|
maxHeight={windowSize.height * 0.825 - 5} // EM units
|
||||||
fullWidth={windowSize.width} // EM units
|
fullWidth={windowSize.width} // EM units
|
||||||
fullHeight={windowSize.height} // EM units
|
fullHeight={windowSize.height} // EM units
|
||||||
defaultFullscreen={false}
|
defaultFullscreen={false}
|
||||||
onOrderClicked={onOrderClicked}
|
onOrderClicked={onOrderClicked}
|
||||||
baseUrl={baseUrl}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -1,312 +1,63 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { HashRouter, BrowserRouter, Switch, Route } from 'react-router-dom';
|
import { HashRouter, BrowserRouter, Switch, Route } from 'react-router-dom';
|
||||||
import { useTheme, Box, Slide, Typography } from '@mui/material';
|
import { Box, Slide, Typography } from '@mui/material';
|
||||||
|
|
||||||
import RobotPage from './RobotPage';
|
import RobotPage from './RobotPage';
|
||||||
import MakerPage from './MakerPage';
|
import MakerPage from './MakerPage';
|
||||||
import BookPage from './BookPage';
|
import BookPage from './BookPage';
|
||||||
import OrderPage from './OrderPage';
|
import OrderPage from './OrderPage';
|
||||||
import SettingsPage from './SettingsPage';
|
import SettingsPage from './SettingsPage';
|
||||||
import NavBar, { Page } from './NavBar';
|
import NavBar from './NavBar';
|
||||||
import MainDialogs, { OpenDialogs } from './MainDialogs';
|
import MainDialogs from './MainDialogs';
|
||||||
|
|
||||||
import RobotAvatar from '../components/RobotAvatar';
|
import RobotAvatar from '../components/RobotAvatar';
|
||||||
import {
|
|
||||||
Book,
|
|
||||||
LimitList,
|
|
||||||
Maker,
|
|
||||||
Robot,
|
|
||||||
Info,
|
|
||||||
Settings,
|
|
||||||
Favorites,
|
|
||||||
defaultMaker,
|
|
||||||
defaultInfo,
|
|
||||||
Coordinator,
|
|
||||||
Order,
|
|
||||||
} from '../models';
|
|
||||||
|
|
||||||
import { apiClient } from '../services/api';
|
|
||||||
import { checkVer, getHost } from '../utils';
|
|
||||||
import { sha256 } from 'js-sha256';
|
|
||||||
|
|
||||||
import defaultCoordinators from '../../static/federation.json';
|
|
||||||
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';
|
||||||
|
|
||||||
const getWindowSize = function (fontSize: number) {
|
const Main = (): JSX.Element => {
|
||||||
// returns window size in EM units
|
|
||||||
return {
|
|
||||||
width: window.innerWidth / fontSize,
|
|
||||||
height: window.innerHeight / fontSize,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Refresh delays (ms) according to Order status
|
|
||||||
const statusToDelay = [
|
|
||||||
3000, // 'Waiting for maker bond'
|
|
||||||
35000, // 'Public'
|
|
||||||
180000, // 'Paused'
|
|
||||||
3000, // 'Waiting for taker bond'
|
|
||||||
999999, // 'Cancelled'
|
|
||||||
999999, // 'Expired'
|
|
||||||
8000, // 'Waiting for trade collateral and buyer invoice'
|
|
||||||
8000, // 'Waiting only for seller trade collateral'
|
|
||||||
8000, // 'Waiting only for buyer invoice'
|
|
||||||
10000, // 'Sending fiat - In chatroom'
|
|
||||||
10000, // 'Fiat sent - In chatroom'
|
|
||||||
100000, // 'In dispute'
|
|
||||||
999999, // 'Collaboratively cancelled'
|
|
||||||
10000, // 'Sending satoshis to buyer'
|
|
||||||
999999, // 'Sucessful trade'
|
|
||||||
30000, // 'Failed lightning network routing'
|
|
||||||
300000, // 'Wait for dispute resolution'
|
|
||||||
300000, // 'Maker lost dispute'
|
|
||||||
300000, // 'Taker lost dispute'
|
|
||||||
];
|
|
||||||
|
|
||||||
interface SlideDirection {
|
|
||||||
in: 'left' | 'right' | undefined;
|
|
||||||
out: 'left' | 'right' | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MainProps {
|
|
||||||
settings: Settings;
|
|
||||||
torStatus: 'NOTINIT' | 'STARTING' | '"Done"' | 'DONE';
|
|
||||||
setSettings: (state: Settings) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Main = ({ torStatus, settings, setSettings }: MainProps): JSX.Element => {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const theme = useTheme();
|
const {
|
||||||
|
book,
|
||||||
// All app data structured
|
fetchBook,
|
||||||
const [book, setBook] = useState<Book>({ orders: [], loading: true });
|
maker,
|
||||||
const [limits, setLimits] = useState<{ list: LimitList; loading: boolean }>({
|
setMaker,
|
||||||
list: [],
|
clearOrder,
|
||||||
loading: true,
|
torStatus,
|
||||||
});
|
settings,
|
||||||
const [robot, setRobot] = useState<Robot>(new Robot());
|
limits,
|
||||||
const [maker, setMaker] = useState<Maker>(defaultMaker);
|
fetchLimits,
|
||||||
const [info, setInfo] = useState<Info>(defaultInfo);
|
robot,
|
||||||
const [coordinators, setCoordinators] = useState<Coordinator[]>(defaultCoordinators);
|
setRobot,
|
||||||
const [baseUrl, setBaseUrl] = useState<string>('');
|
fetchRobot,
|
||||||
const [fav, setFav] = useState<Favorites>({ type: null, mode: 'fiat', currency: 0 });
|
setOrder,
|
||||||
|
setDelay,
|
||||||
const [delay, setDelay] = useState<number>(60000);
|
info,
|
||||||
const [timer, setTimer] = useState<NodeJS.Timer | undefined>(setInterval(() => null, delay));
|
fav,
|
||||||
const [order, setOrder] = useState<Order | undefined>(undefined);
|
setFav,
|
||||||
const [badOrder, setBadOrder] = useState<string | undefined>(undefined);
|
baseUrl,
|
||||||
|
order,
|
||||||
|
page,
|
||||||
|
setPage,
|
||||||
|
slideDirection,
|
||||||
|
setSlideDirection,
|
||||||
|
currentOrder,
|
||||||
|
setCurrentOrder,
|
||||||
|
closeAll,
|
||||||
|
open,
|
||||||
|
setOpen,
|
||||||
|
windowSize,
|
||||||
|
badOrder,
|
||||||
|
navbarHeight,
|
||||||
|
setBadOrder,
|
||||||
|
} = useContext<AppContextProps>(AppContext);
|
||||||
|
|
||||||
const Router = window.NativeRobosats === undefined ? BrowserRouter : HashRouter;
|
const Router = window.NativeRobosats === undefined ? BrowserRouter : HashRouter;
|
||||||
const basename = window.NativeRobosats === undefined ? '' : window.location.pathname;
|
const basename = window.NativeRobosats === undefined ? '' : window.location.pathname;
|
||||||
const entryPage: Page | '' =
|
|
||||||
window.NativeRobosats === undefined ? window.location.pathname.split('/')[1] : '';
|
|
||||||
const [page, setPage] = useState<Page>(entryPage == '' ? 'robot' : entryPage);
|
|
||||||
const [slideDirection, setSlideDirection] = useState<SlideDirection>({
|
|
||||||
in: undefined,
|
|
||||||
out: undefined,
|
|
||||||
});
|
|
||||||
|
|
||||||
const [currentOrder, setCurrentOrder] = useState<number | undefined>(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<OpenDialogs>(closeAll);
|
|
||||||
|
|
||||||
const [windowSize, setWindowSize] = useState<{ width: number; height: number }>(
|
|
||||||
getWindowSize(theme.typography.fontSize),
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (typeof window !== undefined) {
|
|
||||||
window.addEventListener('resize', onResize);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (baseUrl != '') {
|
|
||||||
fetchBook();
|
|
||||||
fetchLimits();
|
|
||||||
}
|
|
||||||
return () => {
|
|
||||||
if (typeof window !== undefined) {
|
|
||||||
window.removeEventListener('resize', onResize);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, [baseUrl]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
let host = '';
|
|
||||||
if (window.NativeRobosats === undefined) {
|
|
||||||
host = getHost();
|
|
||||||
} else {
|
|
||||||
host =
|
|
||||||
settings.network === 'mainnet'
|
|
||||||
? coordinators[0].mainnetOnion
|
|
||||||
: coordinators[0].testnetOnion;
|
|
||||||
}
|
|
||||||
setBaseUrl(`${location.protocol}//${host}`);
|
|
||||||
}, [settings.network]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setWindowSize(getWindowSize(theme.typography.fontSize));
|
|
||||||
}, [theme.typography.fontSize]);
|
|
||||||
|
|
||||||
const onResize = function () {
|
|
||||||
setWindowSize(getWindowSize(theme.typography.fontSize));
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchBook = function () {
|
|
||||||
setBook({ ...book, loading: true });
|
|
||||||
apiClient.get(baseUrl, '/api/book/').then((data: any) =>
|
|
||||||
setBook({
|
|
||||||
loading: false,
|
|
||||||
orders: data.not_found ? [] : data,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchLimits = async () => {
|
|
||||||
setLimits({ ...limits, loading: true });
|
|
||||||
const data = apiClient.get(baseUrl, '/api/limits/').then((data) => {
|
|
||||||
setLimits({ list: data ?? [], loading: false });
|
|
||||||
return data;
|
|
||||||
});
|
|
||||||
return await data;
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchInfo = function () {
|
|
||||||
setInfo({ ...info, loading: true });
|
|
||||||
apiClient.get(baseUrl, '/api/info/').then((data: Info) => {
|
|
||||||
const versionInfo: any = checkVer(data.version.major, data.version.minor, data.version.patch);
|
|
||||||
setInfo({
|
|
||||||
...data,
|
|
||||||
openUpdateClient: versionInfo.updateAvailable,
|
|
||||||
coordinatorVersion: versionInfo.coordinatorVersion,
|
|
||||||
clientVersion: versionInfo.clientVersion,
|
|
||||||
loading: false,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (open.stats || open.coordinator || info.coordinatorVersion == 'v?.?.?') {
|
|
||||||
fetchInfo();
|
|
||||||
}
|
|
||||||
}, [open.stats, open.coordinator]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// Sets Setting network from coordinator API param if accessing via web
|
|
||||||
if (settings.network == undefined && info.network) {
|
|
||||||
setSettings((settings: Settings) => {
|
|
||||||
return { ...settings, network: info.network };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [info]);
|
|
||||||
|
|
||||||
const fetchRobot = function ({ keys = false }) {
|
|
||||||
const requestBody = {
|
|
||||||
token_sha256: sha256(robot.token),
|
|
||||||
};
|
|
||||||
if (keys) {
|
|
||||||
requestBody.pub_key = robot.pubKey;
|
|
||||||
requestBody.enc_priv_key = robot.encPrivKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
setRobot({ ...robot, loading: true });
|
|
||||||
apiClient.post(baseUrl, '/api/user/', requestBody).then((data: any) => {
|
|
||||||
setCurrentOrder(
|
|
||||||
data.active_order_id
|
|
||||||
? data.active_order_id
|
|
||||||
: data.last_order_id
|
|
||||||
? data.last_order_id
|
|
||||||
: null,
|
|
||||||
);
|
|
||||||
setRobot({
|
|
||||||
...robot,
|
|
||||||
nickname: data.nickname,
|
|
||||||
token: robot.token,
|
|
||||||
loading: false,
|
|
||||||
avatarLoaded: robot.nickname === data.nickname,
|
|
||||||
activeOrderId: data.active_order_id ? data.active_order_id : null,
|
|
||||||
lastOrderId: data.last_order_id ? data.last_order_id : null,
|
|
||||||
referralCode: data.referral_code,
|
|
||||||
earnedRewards: data.earned_rewards ?? 0,
|
|
||||||
stealthInvoices: data.wants_stealth,
|
|
||||||
tgEnabled: data.tg_enabled,
|
|
||||||
tgBotName: data.tg_bot_name,
|
|
||||||
tgToken: data.tg_token,
|
|
||||||
bitsEntropy: data.token_bits_entropy,
|
|
||||||
shannonEntropy: data.token_shannon_entropy,
|
|
||||||
pubKey: data.public_key,
|
|
||||||
encPrivKey: data.encrypted_private_key,
|
|
||||||
copiedToken: data.found ? true : robot.copiedToken,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (baseUrl != '' && page != 'robot') {
|
|
||||||
if (open.profile || (robot.token && robot.nickname === null)) {
|
|
||||||
fetchRobot({ keys: false }); // fetch existing robot
|
|
||||||
} else if (robot.token && robot.encPrivKey && robot.pubKey) {
|
|
||||||
fetchRobot({ keys: true }); // create new robot with existing token and keys (on network and coordinator change)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [open.profile, baseUrl]);
|
|
||||||
|
|
||||||
// Fetch current order at load and in a loop
|
|
||||||
useEffect(() => {
|
|
||||||
if (currentOrder != undefined && (page == 'order' || (order == badOrder) == undefined)) {
|
|
||||||
fetchOrder();
|
|
||||||
}
|
|
||||||
}, [currentOrder, page]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
clearInterval(timer);
|
|
||||||
setTimer(setInterval(fetchOrder, delay));
|
|
||||||
return () => clearInterval(timer);
|
|
||||||
}, [delay, currentOrder, page, badOrder]);
|
|
||||||
|
|
||||||
const orderReceived = function (data: any) {
|
|
||||||
if (data.bad_request != undefined) {
|
|
||||||
setBadOrder(data.bad_request);
|
|
||||||
setDelay(99999999);
|
|
||||||
setOrder(undefined);
|
|
||||||
} else {
|
|
||||||
setDelay(
|
|
||||||
data.status >= 0 && data.status <= 18
|
|
||||||
? page == 'order'
|
|
||||||
? statusToDelay[data.status]
|
|
||||||
: statusToDelay[data.status] * 5
|
|
||||||
: 99999999,
|
|
||||||
);
|
|
||||||
setOrder(data);
|
|
||||||
setBadOrder(undefined);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchOrder = function () {
|
|
||||||
if (currentOrder != undefined) {
|
|
||||||
apiClient.get(baseUrl, '/api/order/?order_id=' + currentOrder).then(orderReceived);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const clearOrder = function () {
|
|
||||||
setOrder(undefined);
|
|
||||||
setBadOrder(undefined);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Router basename={basename}>
|
<Router basename={basename}>
|
||||||
{/* load robot avatar image, set avatarLoaded: true */}
|
|
||||||
<RobotAvatar
|
<RobotAvatar
|
||||||
style={{ display: 'none' }}
|
style={{ display: 'none' }}
|
||||||
nickname={robot.nickname}
|
nickname={robot.nickname}
|
||||||
@ -353,6 +104,7 @@ const Main = ({ torStatus, settings, setSettings }: MainProps): JSX.Element => {
|
|||||||
<RobotPage
|
<RobotPage
|
||||||
setPage={setPage}
|
setPage={setPage}
|
||||||
torStatus={torStatus}
|
torStatus={torStatus}
|
||||||
|
fetchRobot={fetchRobot}
|
||||||
setCurrentOrder={setCurrentOrder}
|
setCurrentOrder={setCurrentOrder}
|
||||||
windowSize={windowSize}
|
windowSize={windowSize}
|
||||||
robot={robot}
|
robot={robot}
|
||||||
@ -403,21 +155,7 @@ const Main = ({ torStatus, settings, setSettings }: MainProps): JSX.Element => {
|
|||||||
appear={slideDirection.in != undefined}
|
appear={slideDirection.in != undefined}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<MakerPage
|
<MakerPage hasRobot={robot.avatarLoaded} />
|
||||||
book={book}
|
|
||||||
limits={limits}
|
|
||||||
fetchLimits={fetchLimits}
|
|
||||||
maker={maker}
|
|
||||||
setMaker={setMaker}
|
|
||||||
clearOrder={clearOrder}
|
|
||||||
setPage={setPage}
|
|
||||||
setCurrentOrder={setCurrentOrder}
|
|
||||||
fav={fav}
|
|
||||||
setFav={setFav}
|
|
||||||
windowSize={{ ...windowSize, height: windowSize.height - navbarHeight }}
|
|
||||||
hasRobot={robot.avatarLoaded}
|
|
||||||
baseUrl={baseUrl}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</Slide>
|
</Slide>
|
||||||
</Route>
|
</Route>
|
||||||
@ -432,17 +170,8 @@ const Main = ({ torStatus, settings, setSettings }: MainProps): JSX.Element => {
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<OrderPage
|
<OrderPage
|
||||||
baseUrl={baseUrl}
|
|
||||||
order={order}
|
|
||||||
settings={settings}
|
|
||||||
setOrder={setOrder}
|
|
||||||
setCurrentOrder={setCurrentOrder}
|
|
||||||
badOrder={badOrder}
|
|
||||||
locationOrderId={props.match.params.orderId}
|
locationOrderId={props.match.params.orderId}
|
||||||
setBadOrder={setBadOrder}
|
|
||||||
hasRobot={robot.avatarLoaded}
|
hasRobot={robot.avatarLoaded}
|
||||||
windowSize={{ ...windowSize, height: windowSize.height - navbarHeight }}
|
|
||||||
setPage={setPage}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Slide>
|
</Slide>
|
||||||
@ -456,34 +185,14 @@ const Main = ({ torStatus, settings, setSettings }: MainProps): JSX.Element => {
|
|||||||
appear={slideDirection.in != undefined}
|
appear={slideDirection.in != undefined}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<SettingsPage
|
<SettingsPage />
|
||||||
fav={fav}
|
|
||||||
setFav={setFav}
|
|
||||||
settings={settings}
|
|
||||||
setSettings={setSettings}
|
|
||||||
windowSize={{ ...windowSize, height: windowSize.height - navbarHeight }}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</Slide>
|
</Slide>
|
||||||
</Route>
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
</Box>
|
</Box>
|
||||||
<div style={{ alignContent: 'center', display: 'flex' }}>
|
<div style={{ alignContent: 'center', display: 'flex' }}>
|
||||||
<NavBar
|
<NavBar width={windowSize.width} height={navbarHeight} hasRobot={robot.avatarLoaded} />
|
||||||
nickname={robot.avatarLoaded ? robot.nickname : null}
|
|
||||||
color={settings.network === 'mainnet' ? 'primary' : 'secondary'}
|
|
||||||
width={windowSize.width}
|
|
||||||
height={navbarHeight}
|
|
||||||
page={page}
|
|
||||||
setPage={setPage}
|
|
||||||
open={open}
|
|
||||||
setOpen={setOpen}
|
|
||||||
closeAll={closeAll}
|
|
||||||
setSlideDirection={setSlideDirection}
|
|
||||||
currentOrder={currentOrder}
|
|
||||||
hasRobot={robot.avatarLoaded}
|
|
||||||
baseUrl={baseUrl}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<MainDialogs
|
<MainDialogs
|
||||||
open={open}
|
open={open}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useState, useContext, useEffect } from 'react';
|
||||||
import { Info, Robot } from '../../models';
|
|
||||||
import {
|
import {
|
||||||
CommunityDialog,
|
CommunityDialog,
|
||||||
CoordinatorSummaryDialog,
|
CoordinatorSummaryDialog,
|
||||||
@ -9,7 +8,8 @@ import {
|
|||||||
StatsDialog,
|
StatsDialog,
|
||||||
UpdateClientDialog,
|
UpdateClientDialog,
|
||||||
} from '../../components/Dialogs';
|
} from '../../components/Dialogs';
|
||||||
import { Page } from '../NavBar';
|
import { pn } from '../../utils';
|
||||||
|
import { AppContext, AppContextProps } from '../../contexts/AppContext';
|
||||||
|
|
||||||
export interface OpenDialogs {
|
export interface OpenDialogs {
|
||||||
more: boolean;
|
more: boolean;
|
||||||
@ -19,32 +19,31 @@ export interface OpenDialogs {
|
|||||||
coordinator: boolean;
|
coordinator: boolean;
|
||||||
stats: boolean;
|
stats: boolean;
|
||||||
update: boolean;
|
update: boolean;
|
||||||
profile: boolean; // temporary until new Robot Page is ready
|
profile: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MainDialogsProps {
|
const MainDialogs = (): JSX.Element => {
|
||||||
open: OpenDialogs;
|
const {
|
||||||
setOpen: (state: OpenDialogs) => void;
|
open,
|
||||||
info: Info;
|
setOpen,
|
||||||
robot: Robot;
|
info,
|
||||||
setRobot: (state: Robot) => void;
|
limits,
|
||||||
setPage: (state: Page) => void;
|
closeAll,
|
||||||
setCurrentOrder: (state: number) => void;
|
robot,
|
||||||
closeAll: OpenDialogs;
|
setRobot,
|
||||||
baseUrl: string;
|
setPage,
|
||||||
}
|
setCurrentOrder,
|
||||||
|
baseUrl,
|
||||||
|
} = useContext<AppContextProps>(AppContext);
|
||||||
|
|
||||||
|
const [maxAmount, setMaxAmount] = useState<string>('...loading...');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (limits.list[1000]) {
|
||||||
|
setMaxAmount(pn(limits.list[1000].max_amount * 100000000));
|
||||||
|
}
|
||||||
|
}, [limits.list]);
|
||||||
|
|
||||||
const MainDialogs = ({
|
|
||||||
open,
|
|
||||||
setOpen,
|
|
||||||
info,
|
|
||||||
closeAll,
|
|
||||||
robot,
|
|
||||||
setRobot,
|
|
||||||
setPage,
|
|
||||||
setCurrentOrder,
|
|
||||||
baseUrl,
|
|
||||||
}: MainDialogsProps): JSX.Element => {
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (info.openUpdateClient) {
|
if (info.openUpdateClient) {
|
||||||
setOpen({ ...closeAll, update: true });
|
setOpen({ ...closeAll, update: true });
|
||||||
@ -61,7 +60,7 @@ const MainDialogs = ({
|
|||||||
/>
|
/>
|
||||||
<InfoDialog
|
<InfoDialog
|
||||||
open={open.info}
|
open={open.info}
|
||||||
maxAmount='4,000,000'
|
maxAmount={maxAmount}
|
||||||
onClose={() => setOpen({ ...open, info: false })}
|
onClose={() => setOpen({ ...open, info: false })}
|
||||||
/>
|
/>
|
||||||
<LearnDialog open={open.learn} onClose={() => setOpen({ ...open, learn: false })} />
|
<LearnDialog open={open.learn} onClose={() => setOpen({ ...open, learn: false })} />
|
||||||
|
@ -1,52 +1,26 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useContext, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { Grid, Paper, Collapse, Typography } from '@mui/material';
|
import { Grid, Paper, Collapse, Typography } from '@mui/material';
|
||||||
|
|
||||||
import { LimitList, Maker, Book, Favorites, Order } from '../../models';
|
|
||||||
|
|
||||||
import { filterOrders } from '../../utils';
|
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 { Page } from '../NavBar';
|
import { AppContext, AppContextProps } from '../../contexts/AppContext';
|
||||||
|
|
||||||
interface MakerPageProps {
|
interface MakerPageProps {
|
||||||
limits: { list: LimitList; loading: boolean };
|
|
||||||
fetchLimits: () => void;
|
|
||||||
book: Book;
|
|
||||||
fav: Favorites;
|
|
||||||
maker: Maker;
|
|
||||||
setFav: (state: Favorites) => void;
|
|
||||||
setMaker: (state: Maker) => void;
|
|
||||||
clearOrder: () => void;
|
|
||||||
windowSize: { width: number; height: number };
|
|
||||||
hasRobot: boolean;
|
hasRobot: boolean;
|
||||||
setCurrentOrder: (state: number) => void;
|
|
||||||
setPage: (state: Page) => void;
|
|
||||||
baseUrl: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const MakerPage = ({
|
const MakerPage = ({ hasRobot = false }: MakerPageProps): JSX.Element => {
|
||||||
limits,
|
const { book, fav, maker, clearOrder, windowSize, setCurrentOrder, navbarHeight, setPage } =
|
||||||
fetchLimits,
|
useContext<AppContextProps>(AppContext);
|
||||||
book,
|
|
||||||
fav,
|
|
||||||
maker,
|
|
||||||
setFav,
|
|
||||||
setMaker,
|
|
||||||
clearOrder,
|
|
||||||
windowSize,
|
|
||||||
setCurrentOrder,
|
|
||||||
setPage,
|
|
||||||
hasRobot = false,
|
|
||||||
baseUrl,
|
|
||||||
}: MakerPageProps): JSX.Element => {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
const maxHeight = windowSize.height * 0.85 - 3;
|
const maxHeight = (windowSize.height - navbarHeight) * 0.85 - 3;
|
||||||
const [showMatches, setShowMatches] = useState<boolean>(false);
|
const [showMatches, setShowMatches] = useState<boolean>(false);
|
||||||
|
|
||||||
const matches = filterOrders({
|
const matches = filterOrders({
|
||||||
@ -71,14 +45,13 @@ const MakerPage = ({
|
|||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
<BookTable
|
<BookTable
|
||||||
book={{ orders: matches, loading: book.loading }}
|
orderList={matches}
|
||||||
maxWidth={Math.min(windowSize.width, 60)} // EM units
|
maxWidth={Math.min(windowSize.width, 60)} // EM units
|
||||||
maxHeight={Math.min(matches.length * 3.25 + 3, 16)} // EM units
|
maxHeight={Math.min(matches.length * 3.25 + 3, 16)} // EM units
|
||||||
defaultFullscreen={false}
|
defaultFullscreen={false}
|
||||||
showControls={false}
|
showControls={false}
|
||||||
showFooter={false}
|
showFooter={false}
|
||||||
showNoResults={false}
|
showNoResults={false}
|
||||||
baseUrl={baseUrl}
|
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
@ -95,12 +68,6 @@ const MakerPage = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MakerForm
|
<MakerForm
|
||||||
limits={limits}
|
|
||||||
fetchLimits={fetchLimits}
|
|
||||||
fav={fav}
|
|
||||||
setFav={setFav}
|
|
||||||
maker={maker}
|
|
||||||
setMaker={setMaker}
|
|
||||||
onOrderCreated={(id) => {
|
onOrderCreated={(id) => {
|
||||||
clearOrder();
|
clearOrder();
|
||||||
setCurrentOrder(id);
|
setCurrentOrder(id);
|
||||||
@ -113,8 +80,6 @@ const MakerPage = ({
|
|||||||
onSubmit={() => setShowMatches(matches.length > 0)}
|
onSubmit={() => setShowMatches(matches.length > 0)}
|
||||||
onReset={() => setShowMatches(false)}
|
onReset={() => setShowMatches(false)}
|
||||||
submitButtonLabel={matches.length > 0 && !showMatches ? 'Submit' : 'Create order'}
|
submitButtonLabel={matches.length > 0 && !showMatches ? 'Submit' : 'Create order'}
|
||||||
setPage={setPage}
|
|
||||||
baseUrl={baseUrl}
|
|
||||||
/>
|
/>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -21,19 +21,12 @@ const StyledTooltip = styled(({ className, ...props }: TooltipProps) => (
|
|||||||
|
|
||||||
interface MoreTooltipProps {
|
interface MoreTooltipProps {
|
||||||
open: OpenDialogs;
|
open: OpenDialogs;
|
||||||
nickname: string | null;
|
|
||||||
setOpen: (state: OpenDialogs) => void;
|
setOpen: (state: OpenDialogs) => void;
|
||||||
closeAll: OpenDialogs;
|
closeAll: OpenDialogs;
|
||||||
children: JSX.Element;
|
children: JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MoreTooltip = ({
|
const MoreTooltip = ({ open, setOpen, closeAll, children }: MoreTooltipProps): JSX.Element => {
|
||||||
open,
|
|
||||||
setOpen,
|
|
||||||
closeAll,
|
|
||||||
nickname,
|
|
||||||
children,
|
|
||||||
}: MoreTooltipProps): JSX.Element => {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
return (
|
return (
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useContext, useEffect } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { Tabs, Tab, Paper, useTheme } from '@mui/material';
|
import { Tabs, Tab, Paper, useTheme } from '@mui/material';
|
||||||
import MoreTooltip from './MoreTooltip';
|
import MoreTooltip from './MoreTooltip';
|
||||||
|
|
||||||
import { OpenDialogs } from '../MainDialogs';
|
|
||||||
|
|
||||||
import { Page } from '.';
|
import { Page } from '.';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -17,47 +15,35 @@ 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';
|
||||||
type Direction = 'left' | 'right' | undefined;
|
|
||||||
|
|
||||||
interface NavBarProps {
|
interface NavBarProps {
|
||||||
page: Page;
|
|
||||||
nickname?: string | null;
|
|
||||||
setPage: (state: Page) => void;
|
|
||||||
setSlideDirection: (state: { in: Direction; out: Direction }) => void;
|
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
open: OpenDialogs;
|
|
||||||
setOpen: (state: OpenDialogs) => void;
|
|
||||||
closeAll: OpenDialogs;
|
|
||||||
currentOrder: number | undefined;
|
|
||||||
hasRobot: boolean;
|
hasRobot: boolean;
|
||||||
baseUrl: string;
|
|
||||||
color: 'primary' | 'secondary';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const NavBar = ({
|
const NavBar = ({ width, height, hasRobot = false }: NavBarProps): JSX.Element => {
|
||||||
page,
|
const {
|
||||||
setPage,
|
page,
|
||||||
setSlideDirection,
|
settings,
|
||||||
open,
|
setPage,
|
||||||
nickname = null,
|
setSlideDirection,
|
||||||
setOpen,
|
open,
|
||||||
closeAll,
|
robot,
|
||||||
width,
|
setOpen,
|
||||||
height,
|
closeAll,
|
||||||
currentOrder,
|
currentOrder,
|
||||||
hasRobot = false,
|
baseUrl,
|
||||||
baseUrl,
|
} = useContext<AppContextProps>(AppContext);
|
||||||
color,
|
|
||||||
}: NavBarProps): JSX.Element => {
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const smallBar = width < 50;
|
const smallBar = width < 50;
|
||||||
|
|
||||||
const tabSx = smallBar
|
const tabSx = smallBar
|
||||||
? { position: 'relative', bottom: nickname ? '1em' : '0em', minWidth: '1em' }
|
? { position: 'relative', bottom: robot.nickname ? '1em' : '0em', minWidth: '1em' }
|
||||||
: { position: 'relative', bottom: '1em', minWidth: '2em' };
|
: { position: 'relative', bottom: '1em', minWidth: '2em' };
|
||||||
const pagesPosition = {
|
const pagesPosition = {
|
||||||
robot: 1,
|
robot: 1,
|
||||||
@ -102,21 +88,21 @@ const NavBar = ({
|
|||||||
TabIndicatorProps={{ sx: { height: '0.3em', position: 'absolute', top: 0 } }}
|
TabIndicatorProps={{ sx: { height: '0.3em', position: 'absolute', top: 0 } }}
|
||||||
variant='fullWidth'
|
variant='fullWidth'
|
||||||
value={page}
|
value={page}
|
||||||
indicatorColor={color}
|
indicatorColor={settings.network === 'mainnet' ? 'primary' : 'secondary'}
|
||||||
textColor={color}
|
textColor={settings.network === 'mainnet' ? 'primary' : 'secondary'}
|
||||||
onChange={changePage}
|
onChange={changePage}
|
||||||
>
|
>
|
||||||
<Tab
|
<Tab
|
||||||
sx={{ ...tabSx, minWidth: '2.5em', width: '2.5em', maxWidth: '4em' }}
|
sx={{ ...tabSx, minWidth: '2.5em', width: '2.5em', maxWidth: '4em' }}
|
||||||
value='none'
|
value='none'
|
||||||
disabled={nickname === null}
|
disabled={robot.nickname === null}
|
||||||
onClick={() => setOpen({ ...closeAll, profile: !open.profile })}
|
onClick={() => setOpen({ ...closeAll, profile: !open.profile })}
|
||||||
icon={
|
icon={
|
||||||
nickname ? (
|
robot.nickname && robot.avatarLoaded ? (
|
||||||
<RobotAvatar
|
<RobotAvatar
|
||||||
style={{ width: '2.3em', height: '2.3em', position: 'relative', top: '0.2em' }}
|
style={{ width: '2.3em', height: '2.3em', position: 'relative', top: '0.2em' }}
|
||||||
avatarClass={theme.palette.mode === 'dark' ? 'navBarAvatarDark' : 'navBarAvatar'}
|
avatarClass={theme.palette.mode === 'dark' ? 'navBarAvatarDark' : 'navBarAvatar'}
|
||||||
nickname={nickname}
|
nickname={robot.nickname}
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
@ -171,7 +157,7 @@ const NavBar = ({
|
|||||||
open.more ? null : setOpen({ ...open, more: true });
|
open.more ? null : setOpen({ ...open, more: true });
|
||||||
}}
|
}}
|
||||||
icon={
|
icon={
|
||||||
<MoreTooltip open={open} nickname={nickname} setOpen={setOpen} closeAll={closeAll}>
|
<MoreTooltip open={open} setOpen={setOpen} closeAll={closeAll}>
|
||||||
<MoreHoriz />
|
<MoreHoriz />
|
||||||
</MoreTooltip>
|
</MoreTooltip>
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Tab, Tabs, Paper, CircularProgress, Grid, Typography, Box } from '@mui/material';
|
import { Tab, Tabs, Paper, CircularProgress, Grid, Typography, Box } from '@mui/material';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
@ -9,40 +9,31 @@ import OrderDetails from '../../components/OrderDetails';
|
|||||||
import { Page } from '../NavBar';
|
import { Page } from '../NavBar';
|
||||||
import { Order, Settings } from '../../models';
|
import { Order, Settings } from '../../models';
|
||||||
import { apiClient } from '../../services/api';
|
import { apiClient } from '../../services/api';
|
||||||
|
import { AppContext, AppContextProps } from '../../contexts/AppContext';
|
||||||
|
|
||||||
interface OrderPageProps {
|
interface OrderPageProps {
|
||||||
windowSize: { width: number; height: number };
|
|
||||||
order: Order;
|
|
||||||
settings: Settings;
|
|
||||||
setOrder: (state: Order) => void;
|
|
||||||
setCurrentOrder: (state: number) => void;
|
|
||||||
fetchOrder: () => void;
|
|
||||||
badOrder: string | undefined;
|
|
||||||
setBadOrder: (state: string | undefined) => void;
|
|
||||||
hasRobot: boolean;
|
hasRobot: boolean;
|
||||||
setPage: (state: Page) => void;
|
|
||||||
baseUrl: string;
|
|
||||||
locationOrderId: number;
|
locationOrderId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const OrderPage = ({
|
const OrderPage = ({ hasRobot = false, locationOrderId }: OrderPageProps): JSX.Element => {
|
||||||
windowSize,
|
const {
|
||||||
order,
|
windowSize,
|
||||||
settings,
|
order,
|
||||||
setOrder,
|
settings,
|
||||||
setCurrentOrder,
|
setOrder,
|
||||||
badOrder,
|
setCurrentOrder,
|
||||||
setBadOrder,
|
badOrder,
|
||||||
setPage,
|
setBadOrder,
|
||||||
hasRobot = false,
|
setPage,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
locationOrderId,
|
navbarHeight,
|
||||||
}: OrderPageProps): JSX.Element => {
|
} = useContext<AppContextProps>(AppContext);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
const doublePageWidth: number = 50;
|
const doublePageWidth: number = 50;
|
||||||
const maxHeight: number = windowSize.height * 0.85 - 3;
|
const maxHeight: number = (windowSize.height - navbarHeight) * 0.85 - 3;
|
||||||
|
|
||||||
const [tab, setTab] = useState<'order' | 'contract'>('contract');
|
const [tab, setTab] = useState<'order' | 'contract'>('contract');
|
||||||
|
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Button, Collapse, Grid, Typography, useTheme } from '@mui/material';
|
import { Button, Grid, Typography, useTheme } from '@mui/material';
|
||||||
import { useParams } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { Page } from '../NavBar';
|
|
||||||
import { Robot } from '../../models';
|
import { Robot } from '../../models';
|
||||||
import { Casino, Download, ContentCopy, SmartToy, Bolt } from '@mui/icons-material';
|
|
||||||
import RobotAvatar from '../../components/RobotAvatar';
|
|
||||||
import TokenInput from './TokenInput';
|
import TokenInput from './TokenInput';
|
||||||
import Key from '@mui/icons-material/Key';
|
import Key from '@mui/icons-material/Key';
|
||||||
|
|
||||||
@ -17,8 +12,6 @@ interface RecoveryProps {
|
|||||||
inputToken: string;
|
inputToken: string;
|
||||||
setInputToken: (state: string) => void;
|
setInputToken: (state: string) => void;
|
||||||
getGenerateRobot: (token: string) => void;
|
getGenerateRobot: (token: string) => void;
|
||||||
setPage: (state: Page) => void;
|
|
||||||
baseUrl: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Recovery = ({
|
const Recovery = ({
|
||||||
@ -28,11 +21,8 @@ const Recovery = ({
|
|||||||
setView,
|
setView,
|
||||||
setInputToken,
|
setInputToken,
|
||||||
getGenerateRobot,
|
getGenerateRobot,
|
||||||
setPage,
|
|
||||||
baseUrl,
|
|
||||||
}: RecoveryProps): JSX.Element => {
|
}: RecoveryProps): JSX.Element => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
const recoveryDisabled = () => {
|
const recoveryDisabled = () => {
|
||||||
return !(inputToken.length > 20);
|
return !(inputToken.length > 20);
|
||||||
@ -47,11 +37,14 @@ const Recovery = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container direction='column' alignItems='center' spacing={1} padding={2}>
|
<Grid container direction='column' alignItems='center' spacing={1} padding={2}>
|
||||||
|
<Grid item>
|
||||||
|
<Typography variant='h5' align='center'>
|
||||||
|
{t('Robot recovery')}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
<Typography align='center'>
|
<Typography align='center'>
|
||||||
{t(
|
{t('Enter your robot token to re-build your robot and gain access to its trades.')}
|
||||||
'Please, introduce your robot token to re-build your robot and gain access to its trades.',
|
|
||||||
)}
|
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
|
@ -7,7 +7,6 @@ import RobotAvatar from '../../components/RobotAvatar';
|
|||||||
import TokenInput from './TokenInput';
|
import TokenInput from './TokenInput';
|
||||||
import { Page } from '../NavBar';
|
import { Page } from '../NavBar';
|
||||||
import { Robot } from '../../models';
|
import { Robot } from '../../models';
|
||||||
import { genBase62Token } from '../../utils';
|
|
||||||
|
|
||||||
interface RobotProfileProps {
|
interface RobotProfileProps {
|
||||||
robot: Robot;
|
robot: Robot;
|
||||||
@ -21,7 +20,6 @@ interface RobotProfileProps {
|
|||||||
setPage: (state: Page) => void;
|
setPage: (state: Page) => void;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
badRequest: string;
|
badRequest: string;
|
||||||
robotFound: boolean;
|
|
||||||
width: number;
|
width: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +35,6 @@ const RobotProfile = ({
|
|||||||
setView,
|
setView,
|
||||||
badRequest,
|
badRequest,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
robotFound,
|
|
||||||
width,
|
width,
|
||||||
}: RobotProfileProps): JSX.Element => {
|
}: RobotProfileProps): JSX.Element => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -104,15 +101,13 @@ const RobotProfile = ({
|
|||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{/* {robotFound ? (
|
{robot.found ? (
|
||||||
<Grid item>
|
<Typography align='center' variant='h6'>
|
||||||
<Typography variant='h6'>
|
{t('Welcome back!')}
|
||||||
{t('Welcome back!')}
|
</Typography>
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
) : (
|
) : (
|
||||||
<></>
|
<></>
|
||||||
)} */}
|
)}
|
||||||
|
|
||||||
{robot.activeOrderId ? (
|
{robot.activeOrderId ? (
|
||||||
<Grid item>
|
<Grid item>
|
||||||
@ -184,7 +179,6 @@ const RobotProfile = ({
|
|||||||
|
|
||||||
<Grid item>
|
<Grid item>
|
||||||
<Button
|
<Button
|
||||||
disabled={!(robot.avatarLoaded && robot.nickname)}
|
|
||||||
size='small'
|
size='small'
|
||||||
color='primary'
|
color='primary'
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -59,7 +59,7 @@ const TokenInput = ({
|
|||||||
required={true}
|
required={true}
|
||||||
label={label || undefined}
|
label={label || undefined}
|
||||||
value={inputToken}
|
value={inputToken}
|
||||||
autoFocus={autoFocusTarget == 'texfield'}
|
autoFocus={autoFocusTarget == 'textfield'}
|
||||||
fullWidth={fullWidth}
|
fullWidth={fullWidth}
|
||||||
sx={{ borderColor: 'primary' }}
|
sx={{ borderColor: 'primary' }}
|
||||||
variant={editable ? 'outlined' : 'filled'}
|
variant={editable ? 'outlined' : 'filled'}
|
||||||
|
@ -24,7 +24,7 @@ const Welcome = ({ setView, width, getGenerateRobot }: WelcomeProps): JSX.Elemen
|
|||||||
paddingTop={2.2}
|
paddingTop={2.2}
|
||||||
padding={0.5}
|
padding={0.5}
|
||||||
>
|
>
|
||||||
<Grid item>
|
<Grid item style={{ paddingTop: '2em', paddingBottom: '1.5em' }}>
|
||||||
<svg width={0} height={0}>
|
<svg width={0} height={0}>
|
||||||
<linearGradient id='linearColors' x1={1} y1={0} x2={1} y2={1}>
|
<linearGradient id='linearColors' x1={1} y1={0} x2={1} y2={1}>
|
||||||
<stop offset={0} stopColor={theme.palette.primary.main} />
|
<stop offset={0} stopColor={theme.palette.primary.main} />
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
Paper,
|
Paper,
|
||||||
@ -14,36 +14,19 @@ import { useParams } from 'react-router-dom';
|
|||||||
|
|
||||||
import { Page } from '../NavBar';
|
import { Page } from '../NavBar';
|
||||||
import { Robot } from '../../models';
|
import { Robot } from '../../models';
|
||||||
import { tokenStrength } from '../../utils';
|
|
||||||
import { systemClient } from '../../services/System';
|
import { systemClient } from '../../services/System';
|
||||||
import { apiClient } from '../../services/api';
|
import { apiClient } from '../../services/api';
|
||||||
import { genKey } from '../../pgp';
|
|
||||||
import { sha256 } from 'js-sha256';
|
|
||||||
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 { AppContext, AppContextProps } from '../../contexts/AppContext';
|
||||||
|
|
||||||
interface RobotPageProps {
|
const RobotPage = (): JSX.Element => {
|
||||||
setPage: (state: Page) => void;
|
const { setPage, setCurrentOrder, fetchRobot, torStatus, windowSize, robot, setRobot, baseUrl } =
|
||||||
setCurrentOrder: (state: number) => void;
|
useContext<AppContextProps>(AppContext);
|
||||||
torStatus: 'NOTINIT' | 'STARTING' | '"Done"' | 'DONE';
|
|
||||||
robot: Robot;
|
|
||||||
setRobot: (state: Robot) => void;
|
|
||||||
windowSize: { width: number; height: number };
|
|
||||||
baseUrl: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const RobotPage = ({
|
|
||||||
setPage,
|
|
||||||
setCurrentOrder,
|
|
||||||
torStatus,
|
|
||||||
windowSize,
|
|
||||||
robot,
|
|
||||||
setRobot,
|
|
||||||
baseUrl,
|
|
||||||
}: RobotPageProps): JSX.Element => {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const refCode = params.refCode;
|
const refCode = params.refCode;
|
||||||
@ -51,7 +34,6 @@ const RobotPage = ({
|
|||||||
const maxHeight = windowSize.height * 0.85 - 3;
|
const maxHeight = windowSize.height * 0.85 - 3;
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const [robotFound, setRobotFound] = useState<boolean>(false);
|
|
||||||
const [badRequest, setBadRequest] = useState<string | undefined>(undefined);
|
const [badRequest, setBadRequest] = useState<string | undefined>(undefined);
|
||||||
const [inputToken, setInputToken] = useState<string>('');
|
const [inputToken, setInputToken] = useState<string>('');
|
||||||
const [view, setView] = useState<'welcome' | 'onboarding' | 'recovery' | 'profile'>(
|
const [view, setView] = useState<'welcome' | 'onboarding' | 'recovery' | 'profile'>(
|
||||||
@ -63,82 +45,24 @@ const RobotPage = ({
|
|||||||
setInputToken(robot.token);
|
setInputToken(robot.token);
|
||||||
}
|
}
|
||||||
if (robot.nickname == null && robot.token) {
|
if (robot.nickname == null && robot.token) {
|
||||||
getGenerateRobot(robot.token);
|
fetchRobot({ action: 'login' });
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getGenerateRobot = (token: string) => {
|
const getGenerateRobot = (token: string) => {
|
||||||
const strength = tokenStrength(token);
|
|
||||||
setRobot({ ...robot, loading: true, avatarLoaded: false });
|
|
||||||
setInputToken(token);
|
setInputToken(token);
|
||||||
|
genKey(token).then(function (key) {
|
||||||
const requestBody = genKey(token).then(function (key) {
|
fetchRobot({
|
||||||
return {
|
action: 'generate',
|
||||||
token_sha256: sha256(token),
|
newKeys: {
|
||||||
public_key: key.publicKeyArmored,
|
pubKey: key.publicKeyArmored,
|
||||||
encrypted_private_key: key.encryptedPrivateKeyArmored,
|
encPrivKey: key.encryptedPrivateKeyArmored,
|
||||||
unique_values: strength.uniqueValues,
|
},
|
||||||
counts: strength.counts,
|
newToken: token,
|
||||||
length: token.length,
|
refCode,
|
||||||
ref_code: refCode,
|
setBadRequest,
|
||||||
};
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
requestBody.then(
|
|
||||||
async (body) =>
|
|
||||||
await apiClient.post(baseUrl, '/api/user/', body).then((data: any) => {
|
|
||||||
setRobotFound(data?.found);
|
|
||||||
setBadRequest(data?.bad_request);
|
|
||||||
setCurrentOrder(
|
|
||||||
data.active_order_id
|
|
||||||
? data.active_order_id
|
|
||||||
: data.last_order_id
|
|
||||||
? data.last_order_id
|
|
||||||
: null,
|
|
||||||
);
|
|
||||||
// Add nick and token to App state (token only if not a bad request)
|
|
||||||
data.bad_request
|
|
||||||
? setRobot({
|
|
||||||
...robot,
|
|
||||||
avatarLoaded: true,
|
|
||||||
loading: false,
|
|
||||||
nickname: data.nickname ?? robot.nickname,
|
|
||||||
activeOrderId: data.active_order_id ?? null,
|
|
||||||
referralCode: data.referral_code ?? robot.referralCode,
|
|
||||||
earnedRewards: data.earned_rewards ?? robot.earnedRewards,
|
|
||||||
lastOrderId: data.last_order_id ?? robot.lastOrderId,
|
|
||||||
stealthInvoices: data.wants_stealth ?? robot.stealthInvoices,
|
|
||||||
tgEnabled: data.tg_enabled,
|
|
||||||
tgBotName: data.tg_bot_name,
|
|
||||||
tgToken: data.tg_token,
|
|
||||||
})
|
|
||||||
: setRobot({
|
|
||||||
...robot,
|
|
||||||
nickname: data.nickname,
|
|
||||||
token,
|
|
||||||
loading: false,
|
|
||||||
activeOrderId: data.active_order_id ?? null,
|
|
||||||
lastOrderId: data.last_order_id ?? null,
|
|
||||||
referralCode: data.referral_code,
|
|
||||||
earnedRewards: data.earned_rewards ?? 0,
|
|
||||||
stealthInvoices: data.wants_stealth,
|
|
||||||
tgEnabled: data.tg_enabled,
|
|
||||||
tgBotName: data.tg_bot_name,
|
|
||||||
tgToken: data.tg_token,
|
|
||||||
bitsEntropy: data.token_bits_entropy,
|
|
||||||
shannonEntropy: data.token_shannon_entropy,
|
|
||||||
pubKey: data.public_key,
|
|
||||||
encPrivKey: data.encrypted_private_key,
|
|
||||||
copiedToken: data.found ? true : robot.copiedToken,
|
|
||||||
}) &
|
|
||||||
systemClient.setItem('robot_token', token) &
|
|
||||||
systemClient.setItem('pub_key', data.public_key.split('\n').join('\\')) &
|
|
||||||
systemClient.setItem(
|
|
||||||
'enc_priv_key',
|
|
||||||
data.encrypted_private_key.split('\n').join('\\'),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteRobot = () => {
|
const deleteRobot = () => {
|
||||||
@ -148,7 +72,6 @@ const RobotPage = ({
|
|||||||
|
|
||||||
const logoutRobot = () => {
|
const logoutRobot = () => {
|
||||||
setInputToken('');
|
setInputToken('');
|
||||||
setRobotFound(false);
|
|
||||||
systemClient.deleteCookie('sessionid');
|
systemClient.deleteCookie('sessionid');
|
||||||
systemClient.deleteItem('robot_token');
|
systemClient.deleteItem('robot_token');
|
||||||
systemClient.deleteItem('pub_key');
|
systemClient.deleteItem('pub_key');
|
||||||
@ -237,7 +160,6 @@ const RobotPage = ({
|
|||||||
<RobotProfile
|
<RobotProfile
|
||||||
setView={setView}
|
setView={setView}
|
||||||
robot={robot}
|
robot={robot}
|
||||||
robotFound={robotFound}
|
|
||||||
setRobot={setRobot}
|
setRobot={setRobot}
|
||||||
setCurrentOrder={setCurrentOrder}
|
setCurrentOrder={setCurrentOrder}
|
||||||
badRequest={badRequest}
|
badRequest={badRequest}
|
||||||
|
@ -1,27 +1,14 @@
|
|||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Grid, Paper, useTheme } from '@mui/material';
|
import { Grid, Paper, useTheme } from '@mui/material';
|
||||||
import SettingsForm from '../../components/SettingsForm';
|
import SettingsForm from '../../components/SettingsForm';
|
||||||
import { Settings, Favorites } from '../../models';
|
import { AppContextProps, AppContext } from '../../contexts/AppContext';
|
||||||
|
|
||||||
interface SettingsPageProps {
|
const SettingsPage = (): JSX.Element => {
|
||||||
fav: Favorites;
|
const { windowSize, navbarHeight } = useContext<AppContextProps>(AppContext);
|
||||||
setFav: (state: Favorites) => void;
|
|
||||||
settings: Settings;
|
|
||||||
setSettings: (state: Settings) => void;
|
|
||||||
windowSize: { width: number; height: number };
|
|
||||||
}
|
|
||||||
|
|
||||||
const SettingsPage = ({
|
|
||||||
fav,
|
|
||||||
setFav,
|
|
||||||
settings,
|
|
||||||
setSettings,
|
|
||||||
windowSize,
|
|
||||||
}: SettingsPageProps): JSX.Element => {
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const maxHeight = windowSize.height * 0.85 - 3;
|
const maxHeight = (windowSize.height - navbarHeight) * 0.85 - 3;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper
|
<Paper
|
||||||
@ -36,13 +23,7 @@ const SettingsPage = ({
|
|||||||
>
|
>
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
<SettingsForm
|
<SettingsForm showNetwork={!(window.NativeRobosats === undefined)} />
|
||||||
fav={fav}
|
|
||||||
setFav={setFav}
|
|
||||||
settings={settings}
|
|
||||||
setSettings={setSettings}
|
|
||||||
showNetwork={!(window.NativeRobosats === undefined)}
|
|
||||||
/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
@ -17,7 +17,7 @@ import {
|
|||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { DataGrid, GridPagination } from '@mui/x-data-grid';
|
import { DataGrid, GridPagination } from '@mui/x-data-grid';
|
||||||
import currencyDict from '../../../static/assets/currencies.json';
|
import currencyDict from '../../../static/assets/currencies.json';
|
||||||
import { Book, Favorites } from '../../models';
|
import { PublicOrder } from '../../models';
|
||||||
import { filterOrders, hexToRgb, statusBadgeColor, pn, amountToString } from '../../utils';
|
import { filterOrders, hexToRgb, statusBadgeColor, pn, amountToString } from '../../utils';
|
||||||
import BookControl from './BookControl';
|
import BookControl from './BookControl';
|
||||||
|
|
||||||
@ -27,11 +27,10 @@ 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';
|
||||||
|
|
||||||
interface BookTableProps {
|
interface BookTableProps {
|
||||||
clickRefresh?: () => void;
|
orderList?: PublicOrder[];
|
||||||
book: Book;
|
|
||||||
fav?: Favorites;
|
|
||||||
maxWidth: number;
|
maxWidth: number;
|
||||||
maxHeight: number;
|
maxHeight: number;
|
||||||
fullWidth?: number;
|
fullWidth?: number;
|
||||||
@ -42,16 +41,11 @@ interface BookTableProps {
|
|||||||
showControls?: boolean;
|
showControls?: boolean;
|
||||||
showFooter?: boolean;
|
showFooter?: boolean;
|
||||||
showNoResults?: boolean;
|
showNoResults?: boolean;
|
||||||
setFav?: (state: Favorites) => void;
|
|
||||||
onOrderClicked?: (id: number) => void;
|
onOrderClicked?: (id: number) => void;
|
||||||
baseUrl: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const BookTable = ({
|
const BookTable = ({
|
||||||
clickRefresh,
|
orderList,
|
||||||
book,
|
|
||||||
fav = { currency: 1, type: 0, mode: 'fiat' },
|
|
||||||
setFav,
|
|
||||||
maxWidth = 100,
|
maxWidth = 100,
|
||||||
maxHeight = 70,
|
maxHeight = 70,
|
||||||
fullWidth = 100,
|
fullWidth = 100,
|
||||||
@ -63,10 +57,12 @@ const BookTable = ({
|
|||||||
showFooter = true,
|
showFooter = true,
|
||||||
showNoResults = true,
|
showNoResults = true,
|
||||||
onOrderClicked = () => null,
|
onOrderClicked = () => null,
|
||||||
baseUrl,
|
|
||||||
}: BookTableProps): JSX.Element => {
|
}: BookTableProps): JSX.Element => {
|
||||||
|
const { book, fetchBook, fav, setFav, baseUrl } = useContext<AppContextProps>(AppContext);
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const orders = orderList ?? book.orders;
|
||||||
const [pageSize, setPageSize] = useState(0);
|
const [pageSize, setPageSize] = useState(0);
|
||||||
const [fullscreen, setFullscreen] = useState(defaultFullscreen);
|
const [fullscreen, setFullscreen] = useState(defaultFullscreen);
|
||||||
const [paymentMethods, setPaymentMethods] = useState<string[]>([]);
|
const [paymentMethods, setPaymentMethods] = useState<string[]>([]);
|
||||||
@ -641,7 +637,7 @@ const BookTable = ({
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={6}>
|
<Grid item xs={6}>
|
||||||
<IconButton onClick={clickRefresh}>
|
<IconButton onClick={() => fetchBook()}>
|
||||||
<Refresh />
|
<Refresh />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Grid>
|
</Grid>
|
||||||
@ -729,11 +725,11 @@ const BookTable = ({
|
|||||||
rows={
|
rows={
|
||||||
showControls
|
showControls
|
||||||
? filterOrders({
|
? filterOrders({
|
||||||
orders: book.orders,
|
orders,
|
||||||
baseFilter: fav,
|
baseFilter: fav,
|
||||||
paymentMethods,
|
paymentMethods,
|
||||||
})
|
})
|
||||||
: book.orders
|
: orders
|
||||||
}
|
}
|
||||||
loading={book.loading}
|
loading={book.loading}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
@ -748,7 +744,7 @@ const BookTable = ({
|
|||||||
setPaymentMethods,
|
setPaymentMethods,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
pageSize={book.loading && book.orders.length == 0 ? 0 : pageSize}
|
pageSize={book.loading && orders.length == 0 ? 0 : pageSize}
|
||||||
rowsPerPageOptions={width < 22 ? [] : [0, pageSize, defaultPageSize * 2, 50, 100]}
|
rowsPerPageOptions={width < 22 ? [] : [0, pageSize, defaultPageSize * 2, 50, 100]}
|
||||||
onPageSizeChange={(newPageSize) => {
|
onPageSizeChange={(newPageSize) => {
|
||||||
setPageSize(newPageSize);
|
setPageSize(newPageSize);
|
||||||
@ -769,11 +765,11 @@ const BookTable = ({
|
|||||||
rows={
|
rows={
|
||||||
showControls
|
showControls
|
||||||
? filterOrders({
|
? filterOrders({
|
||||||
orders: book.orders,
|
orders,
|
||||||
baseFilter: fav,
|
baseFilter: fav,
|
||||||
paymentMethods,
|
paymentMethods,
|
||||||
})
|
})
|
||||||
: book.orders
|
: orders
|
||||||
}
|
}
|
||||||
loading={book.loading}
|
loading={book.loading}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
@ -788,7 +784,7 @@ const BookTable = ({
|
|||||||
setPaymentMethods,
|
setPaymentMethods,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
pageSize={book.loading && book.orders.length == 0 ? 0 : pageSize}
|
pageSize={book.loading && orders.length == 0 ? 0 : pageSize}
|
||||||
rowsPerPageOptions={[0, pageSize, defaultPageSize * 2, 50, 100]}
|
rowsPerPageOptions={[0, pageSize, defaultPageSize * 2, 50, 100]}
|
||||||
onPageSizeChange={(newPageSize) => {
|
onPageSizeChange={(newPageSize) => {
|
||||||
setPageSize(newPageSize);
|
setPageSize(newPageSize);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState, useContext } from 'react';
|
||||||
import {
|
import {
|
||||||
ResponsiveLine,
|
ResponsiveLine,
|
||||||
Serie,
|
Serie,
|
||||||
@ -26,32 +26,24 @@ 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';
|
||||||
|
|
||||||
interface DepthChartProps {
|
interface DepthChartProps {
|
||||||
orders: PublicOrder[];
|
|
||||||
lastDayPremium?: number | undefined;
|
|
||||||
currency: number;
|
|
||||||
limits: LimitList;
|
|
||||||
maxWidth: number;
|
maxWidth: number;
|
||||||
maxHeight: number;
|
maxHeight: number;
|
||||||
fillContainer?: boolean;
|
fillContainer?: boolean;
|
||||||
elevation?: number;
|
elevation?: number;
|
||||||
onOrderClicked?: (id: number) => void;
|
onOrderClicked?: (id: number) => void;
|
||||||
baseUrl: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const DepthChart: React.FC<DepthChartProps> = ({
|
const DepthChart: React.FC<DepthChartProps> = ({
|
||||||
orders,
|
|
||||||
lastDayPremium,
|
|
||||||
currency,
|
|
||||||
limits,
|
|
||||||
maxWidth,
|
maxWidth,
|
||||||
maxHeight,
|
maxHeight,
|
||||||
fillContainer = false,
|
fillContainer = false,
|
||||||
elevation = 6,
|
elevation = 6,
|
||||||
onOrderClicked = () => null,
|
onOrderClicked = () => null,
|
||||||
baseUrl,
|
|
||||||
}) => {
|
}) => {
|
||||||
|
const { book, fav, info, limits, baseUrl } = useContext<AppContextProps>(AppContext);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const [enrichedOrders, setEnrichedOrders] = useState<Order[]>([]);
|
const [enrichedOrders, setEnrichedOrders] = useState<Order[]>([]);
|
||||||
@ -66,22 +58,22 @@ const DepthChart: React.FC<DepthChartProps> = ({
|
|||||||
const width = maxWidth < 20 ? 20 : maxWidth > 72.8 ? 72.8 : maxWidth;
|
const width = maxWidth < 20 ? 20 : maxWidth > 72.8 ? 72.8 : maxWidth;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCurrencyCode(currency === 0 ? 1 : currency);
|
setCurrencyCode(fav.currency === 0 ? 1 : fav.currency);
|
||||||
}, [currency]);
|
}, [fav.currency]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (Object.keys(limits).length > 0) {
|
if (Object.keys(limits.list).length > 0) {
|
||||||
const enriched = orders.map((order) => {
|
const enriched = book.orders.map((order) => {
|
||||||
// We need to transform all currencies to the same base (ex. USD), we don't have the exchange rate
|
// We need to transform all currencies to the same base (ex. USD), we don't have the exchange rate
|
||||||
// for EUR -> USD, but we know the rate of both to BTC, so we get advantage of it and apply a
|
// for EUR -> USD, but we know the rate of both to BTC, so we get advantage of it and apply a
|
||||||
// simple rule of three
|
// simple rule of three
|
||||||
order.base_amount =
|
order.base_amount =
|
||||||
(order.price * limits[currencyCode].price) / limits[order.currency].price;
|
(order.price * limits.list[currencyCode].price) / limits.list[order.currency].price;
|
||||||
return order;
|
return order;
|
||||||
});
|
});
|
||||||
setEnrichedOrders(enriched);
|
setEnrichedOrders(enriched);
|
||||||
}
|
}
|
||||||
}, [limits, orders, currencyCode]);
|
}, [limits.list, book.orders, currencyCode]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (enrichedOrders.length > 0) {
|
if (enrichedOrders.length > 0) {
|
||||||
@ -102,16 +94,16 @@ const DepthChart: React.FC<DepthChartProps> = ({
|
|||||||
setXRange(maxRange);
|
setXRange(maxRange);
|
||||||
setRangeSteps(rangeSteps);
|
setRangeSteps(rangeSteps);
|
||||||
} else {
|
} else {
|
||||||
if (lastDayPremium === undefined) {
|
if (info.last_day_nonkyc_btc_premium === undefined) {
|
||||||
const premiums: number[] = enrichedOrders.map((order) => order?.premium || 0);
|
const premiums: number[] = enrichedOrders.map((order) => order?.premium || 0);
|
||||||
setCenter(~~matchMedian(premiums));
|
setCenter(~~matchMedian(premiums));
|
||||||
} else {
|
} else {
|
||||||
setCenter(lastDayPremium);
|
setCenter(info.last_day_nonkyc_btc_premium);
|
||||||
}
|
}
|
||||||
setXRange(8);
|
setXRange(8);
|
||||||
setRangeSteps(0.5);
|
setRangeSteps(0.5);
|
||||||
}
|
}
|
||||||
}, [enrichedOrders, xType, lastDayPremium, currencyCode]);
|
}, [enrichedOrders, xType, info.last_day_nonkyc_btc_premium, currencyCode]);
|
||||||
|
|
||||||
const generateSeries: () => void = () => {
|
const generateSeries: () => void = () => {
|
||||||
const sortedOrders: PublicOrder[] =
|
const sortedOrders: PublicOrder[] =
|
||||||
|
@ -117,7 +117,7 @@ const CoordinatorSummaryDialog = ({ open = false, onClose, info }: Props): JSX.E
|
|||||||
primaryTypographyProps={{ fontSize: '14px' }}
|
primaryTypographyProps={{ fontSize: '14px' }}
|
||||||
secondaryTypographyProps={{ fontSize: '12px' }}
|
secondaryTypographyProps={{ fontSize: '12px' }}
|
||||||
primary={`${info.last_day_nonkyc_btc_premium}%`}
|
primary={`${info.last_day_nonkyc_btc_premium}%`}
|
||||||
secondary={t('24h non-KYC bitcoin premium')}
|
secondary={t('Last 24h mean premium')}
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
InputAdornment,
|
InputAdornment,
|
||||||
@ -29,7 +29,6 @@ import { LimitList, Maker, Favorites, defaultMaker } from '../../models';
|
|||||||
|
|
||||||
import { LocalizationProvider, TimePicker } from '@mui/x-date-pickers';
|
import { LocalizationProvider, TimePicker } from '@mui/x-date-pickers';
|
||||||
import DateFnsUtils from '@date-io/date-fns';
|
import DateFnsUtils from '@date-io/date-fns';
|
||||||
import { useHistory } from 'react-router-dom';
|
|
||||||
import { ConfirmationDialog } from '../Dialogs';
|
import { ConfirmationDialog } from '../Dialogs';
|
||||||
import { apiClient } from '../../services/api';
|
import { apiClient } from '../../services/api';
|
||||||
|
|
||||||
@ -41,35 +40,21 @@ import { amountToString, 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 { Page } from '../../basic/NavBar';
|
import { AppContext, AppContextProps } from '../../contexts/AppContext';
|
||||||
|
|
||||||
interface MakerFormProps {
|
interface MakerFormProps {
|
||||||
limits: { list: LimitList; loading: boolean };
|
|
||||||
fetchLimits: () => void;
|
|
||||||
pricingMethods?: boolean;
|
|
||||||
maker: Maker;
|
|
||||||
fav: Favorites;
|
|
||||||
setFav: (state: Favorites) => void;
|
|
||||||
setMaker: (state: Maker) => void;
|
|
||||||
disableRequest?: boolean;
|
disableRequest?: boolean;
|
||||||
|
pricingMethods?: boolean;
|
||||||
collapseAll?: boolean;
|
collapseAll?: boolean;
|
||||||
onSubmit?: () => void;
|
onSubmit?: () => void;
|
||||||
onReset?: () => void;
|
onReset?: () => void;
|
||||||
submitButtonLabel?: string;
|
submitButtonLabel?: string;
|
||||||
onOrderCreated?: (id: number) => void;
|
onOrderCreated?: (id: number) => void;
|
||||||
hasRobot?: boolean;
|
hasRobot?: boolean;
|
||||||
setPage?: (state: Page) => void;
|
|
||||||
baseUrl: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const MakerForm = ({
|
const MakerForm = ({
|
||||||
limits,
|
|
||||||
fetchLimits,
|
|
||||||
pricingMethods = false,
|
pricingMethods = false,
|
||||||
fav,
|
|
||||||
setFav,
|
|
||||||
maker,
|
|
||||||
setMaker,
|
|
||||||
disableRequest = false,
|
disableRequest = false,
|
||||||
collapseAll = false,
|
collapseAll = false,
|
||||||
onSubmit = () => {},
|
onSubmit = () => {},
|
||||||
@ -77,12 +62,12 @@ const MakerForm = ({
|
|||||||
submitButtonLabel = 'Create Order',
|
submitButtonLabel = 'Create Order',
|
||||||
onOrderCreated = () => null,
|
onOrderCreated = () => null,
|
||||||
hasRobot = true,
|
hasRobot = true,
|
||||||
setPage = () => null,
|
|
||||||
baseUrl,
|
|
||||||
}: MakerFormProps): JSX.Element => {
|
}: MakerFormProps): JSX.Element => {
|
||||||
|
const { fav, setFav, limits, fetchLimits, maker, setMaker, setPage, baseUrl } =
|
||||||
|
useContext<AppContextProps>(AppContext);
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const history = useHistory();
|
|
||||||
const [badRequest, setBadRequest] = useState<string | null>(null);
|
const [badRequest, setBadRequest] = useState<string | null>(null);
|
||||||
const [amountLimits, setAmountLimits] = useState<number[]>([1, 1000]);
|
const [amountLimits, setAmountLimits] = useState<number[]>([1, 1000]);
|
||||||
const [satoshisLimits, setSatoshisLimits] = useState<number[]>([20000, 4000000]);
|
const [satoshisLimits, setSatoshisLimits] = useState<number[]>([20000, 4000000]);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { AppContextProps, AppContext } from '../../contexts/AppContext';
|
||||||
import {
|
import {
|
||||||
Grid,
|
Grid,
|
||||||
Paper,
|
Paper,
|
||||||
@ -14,7 +15,6 @@ import {
|
|||||||
ToggleButtonGroup,
|
ToggleButtonGroup,
|
||||||
ToggleButton,
|
ToggleButton,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { Favorites, Settings } from '../../models';
|
|
||||||
import SelectLanguage from './SelectLanguage';
|
import SelectLanguage from './SelectLanguage';
|
||||||
import {
|
import {
|
||||||
Translate,
|
Translate,
|
||||||
@ -31,21 +31,11 @@ import SwapCalls from '@mui/icons-material/SwapCalls';
|
|||||||
|
|
||||||
interface SettingsFormProps {
|
interface SettingsFormProps {
|
||||||
dense?: boolean;
|
dense?: boolean;
|
||||||
fav: Favorites;
|
|
||||||
setFav: (state: Favorites) => void;
|
|
||||||
settings: Settings;
|
|
||||||
setSettings: (state: Settings) => void;
|
|
||||||
showNetwork?: boolean;
|
showNetwork?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SettingsForm = ({
|
const SettingsForm = ({ dense = false, showNetwork = false }: SettingsFormProps): JSX.Element => {
|
||||||
dense = false,
|
const { fav, setFav, settings, setSettings } = useContext<AppContextProps>(AppContext);
|
||||||
fav,
|
|
||||||
setFav,
|
|
||||||
settings,
|
|
||||||
setSettings,
|
|
||||||
showNetwork = false,
|
|
||||||
}: SettingsFormProps): JSX.Element => {
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const fontSizes = [
|
const fontSizes = [
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
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';
|
||||||
|
|
||||||
interface TorIndicatorProps {
|
interface TorIndicatorProps {
|
||||||
color: 'inherit' | 'error' | 'warning' | 'success' | 'primary' | 'secondary' | 'info' | undefined;
|
color: 'inherit' | 'error' | 'warning' | 'success' | 'primary' | 'secondary' | 'info' | undefined;
|
||||||
@ -53,11 +54,8 @@ const TorIndicator = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
interface TorConnectionBadgeProps {
|
const TorConnectionBadge = (): JSX.Element => {
|
||||||
torStatus: 'NOTINIT' | 'STARTING' | '"Done"' | 'DONE';
|
const { torStatus } = useContext<AppContextProps>(AppContext);
|
||||||
}
|
|
||||||
|
|
||||||
const TorConnectionBadge = ({ torStatus }: TorConnectionBadgeProps): JSX.Element => {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
if (window?.NativeRobosats == null) {
|
if (window?.NativeRobosats == null) {
|
||||||
|
501
frontend/src/contexts/AppContext.tsx
Normal file
501
frontend/src/contexts/AppContext.tsx
Normal file
@ -0,0 +1,501 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { Page } from '../basic/NavBar';
|
||||||
|
import { OpenDialogs } from '../basic/MainDialogs';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Book,
|
||||||
|
LimitList,
|
||||||
|
Maker,
|
||||||
|
Robot,
|
||||||
|
Info,
|
||||||
|
Settings,
|
||||||
|
Favorites,
|
||||||
|
defaultMaker,
|
||||||
|
defaultInfo,
|
||||||
|
Coordinator,
|
||||||
|
Order,
|
||||||
|
} from '../models';
|
||||||
|
|
||||||
|
import { apiClient } from '../services/api';
|
||||||
|
import { checkVer, getHost, tokenStrength } from '../utils';
|
||||||
|
import { sha256 } from 'js-sha256';
|
||||||
|
|
||||||
|
import defaultCoordinators from '../../static/federation.json';
|
||||||
|
import { useTheme } from '@mui/material';
|
||||||
|
import { systemClient } from '../services/System';
|
||||||
|
|
||||||
|
const getWindowSize = function (fontSize: number) {
|
||||||
|
// returns window size in EM units
|
||||||
|
return {
|
||||||
|
width: window.innerWidth / fontSize,
|
||||||
|
height: window.innerHeight / fontSize,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Refresh delays (ms) according to Order status
|
||||||
|
const statusToDelay = [
|
||||||
|
3000, // 'Waiting for maker bond'
|
||||||
|
35000, // 'Public'
|
||||||
|
180000, // 'Paused'
|
||||||
|
3000, // 'Waiting for taker bond'
|
||||||
|
999999, // 'Cancelled'
|
||||||
|
999999, // 'Expired'
|
||||||
|
8000, // 'Waiting for trade collateral and buyer invoice'
|
||||||
|
8000, // 'Waiting only for seller trade collateral'
|
||||||
|
8000, // 'Waiting only for buyer invoice'
|
||||||
|
10000, // 'Sending fiat - In chatroom'
|
||||||
|
10000, // 'Fiat sent - In chatroom'
|
||||||
|
100000, // 'In dispute'
|
||||||
|
999999, // 'Collaboratively cancelled'
|
||||||
|
10000, // 'Sending satoshis to buyer'
|
||||||
|
999999, // 'Sucessful trade'
|
||||||
|
30000, // 'Failed lightning network routing'
|
||||||
|
300000, // 'Wait for dispute resolution'
|
||||||
|
300000, // 'Maker lost dispute'
|
||||||
|
300000, // 'Taker lost dispute'
|
||||||
|
];
|
||||||
|
|
||||||
|
export interface SlideDirection {
|
||||||
|
in: 'left' | 'right' | undefined;
|
||||||
|
out: 'left' | 'right' | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface fetchRobotProps {
|
||||||
|
action?: 'login' | 'generate';
|
||||||
|
newKeys?: { encPrivKey: string; pubKey: string } | null;
|
||||||
|
newToken?: string | null;
|
||||||
|
refCode?: string | null;
|
||||||
|
setBadRequest?: (state: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
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 = {
|
||||||
|
more: false,
|
||||||
|
learn: false,
|
||||||
|
community: false,
|
||||||
|
info: false,
|
||||||
|
coordinator: false,
|
||||||
|
exchange: false,
|
||||||
|
client: false,
|
||||||
|
update: false,
|
||||||
|
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),
|
||||||
|
// }
|
||||||
|
|
||||||
|
export interface AppContextProviderProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
settings: Settings;
|
||||||
|
setSettings: (state: Settings) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AppContextProvider = ({
|
||||||
|
children,
|
||||||
|
settings,
|
||||||
|
setSettings,
|
||||||
|
}: AppContextProviderProps): JSX.Element => {
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
|
// All app data structured
|
||||||
|
const [torStatus, setTorStatus] = useState<TorStatus>('NOTINIT');
|
||||||
|
const [book, setBook] = useState<Book>({ orders: [], loading: true });
|
||||||
|
const [limits, setLimits] = useState<{ list: LimitList; loading: boolean }>({
|
||||||
|
list: [],
|
||||||
|
loading: true,
|
||||||
|
});
|
||||||
|
const [robot, setRobot] = useState<Robot>(new Robot());
|
||||||
|
const [maker, setMaker] = useState<Maker>(defaultMaker);
|
||||||
|
const [info, setInfo] = useState<Info>(defaultInfo);
|
||||||
|
const [coordinators, setCoordinators] = useState<Coordinator[]>(defaultCoordinators);
|
||||||
|
const [baseUrl, setBaseUrl] = useState<string>('');
|
||||||
|
const [fav, setFav] = useState<Favorites>({ type: null, mode: 'fiat', currency: 0 });
|
||||||
|
|
||||||
|
const [delay, setDelay] = useState<number>(60000);
|
||||||
|
const [timer, setTimer] = useState<NodeJS.Timer | undefined>(setInterval(() => null, delay));
|
||||||
|
const [order, setOrder] = useState<Order | 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 [slideDirection, setSlideDirection] = useState<SlideDirection>({
|
||||||
|
in: undefined,
|
||||||
|
out: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [currentOrder, setCurrentOrder] = useState<number | undefined>(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<OpenDialogs>(closeAll);
|
||||||
|
|
||||||
|
const [windowSize, setWindowSize] = useState<{ width: number; height: number }>(
|
||||||
|
getWindowSize(theme.typography.fontSize),
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof window !== undefined) {
|
||||||
|
window.addEventListener('resize', onResize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (baseUrl != '') {
|
||||||
|
fetchBook();
|
||||||
|
fetchLimits();
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
if (typeof window !== undefined) {
|
||||||
|
window.removeEventListener('resize', onResize);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [baseUrl]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let host = '';
|
||||||
|
if (window.NativeRobosats === undefined) {
|
||||||
|
host = getHost();
|
||||||
|
} else {
|
||||||
|
host =
|
||||||
|
settings.network === 'mainnet'
|
||||||
|
? coordinators[0].mainnetOnion
|
||||||
|
: coordinators[0].testnetOnion;
|
||||||
|
}
|
||||||
|
setBaseUrl(`${location.protocol}//${host}`);
|
||||||
|
}, [settings.network]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setWindowSize(getWindowSize(theme.typography.fontSize));
|
||||||
|
}, [theme.typography.fontSize]);
|
||||||
|
|
||||||
|
const onResize = function () {
|
||||||
|
setWindowSize(getWindowSize(theme.typography.fontSize));
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchBook = function () {
|
||||||
|
setBook({ ...book, loading: true });
|
||||||
|
apiClient.get(baseUrl, '/api/book/').then((data: any) =>
|
||||||
|
setBook({
|
||||||
|
loading: false,
|
||||||
|
orders: data.not_found ? [] : data,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchLimits = async () => {
|
||||||
|
setLimits({ ...limits, loading: true });
|
||||||
|
const data = apiClient.get(baseUrl, '/api/limits/').then((data) => {
|
||||||
|
setLimits({ list: data ?? [], loading: false });
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
return await data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchInfo = function () {
|
||||||
|
setInfo({ ...info, loading: true });
|
||||||
|
apiClient.get(baseUrl, '/api/info/').then((data: Info) => {
|
||||||
|
const versionInfo: any = checkVer(data.version.major, data.version.minor, data.version.patch);
|
||||||
|
setInfo({
|
||||||
|
...data,
|
||||||
|
openUpdateClient: versionInfo.updateAvailable,
|
||||||
|
coordinatorVersion: versionInfo.coordinatorVersion,
|
||||||
|
clientVersion: versionInfo.clientVersion,
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open.stats || open.coordinator || info.coordinatorVersion == 'v?.?.?') {
|
||||||
|
fetchInfo();
|
||||||
|
}
|
||||||
|
}, [open.stats, open.coordinator]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Sets Setting network from coordinator API param if accessing via web
|
||||||
|
if (settings.network == undefined && info.network) {
|
||||||
|
setSettings((settings: Settings) => {
|
||||||
|
return { ...settings, network: info.network };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [info]);
|
||||||
|
|
||||||
|
// Fetch current order at load and in a loop
|
||||||
|
useEffect(() => {
|
||||||
|
if (currentOrder != undefined && (page == 'order' || (order == badOrder) == undefined)) {
|
||||||
|
fetchOrder();
|
||||||
|
}
|
||||||
|
}, [currentOrder, page]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
clearInterval(timer);
|
||||||
|
setTimer(setInterval(fetchOrder, delay));
|
||||||
|
return () => clearInterval(timer);
|
||||||
|
}, [delay, currentOrder, page, badOrder]);
|
||||||
|
|
||||||
|
const orderReceived = function (data: any) {
|
||||||
|
if (data.bad_request != undefined) {
|
||||||
|
setBadOrder(data.bad_request);
|
||||||
|
setDelay(99999999);
|
||||||
|
setOrder(undefined);
|
||||||
|
} else {
|
||||||
|
setDelay(
|
||||||
|
data.status >= 0 && data.status <= 18
|
||||||
|
? page == 'order'
|
||||||
|
? statusToDelay[data.status]
|
||||||
|
: statusToDelay[data.status] * 5
|
||||||
|
: 99999999,
|
||||||
|
);
|
||||||
|
setOrder(data);
|
||||||
|
setBadOrder(undefined);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchOrder = function () {
|
||||||
|
if (currentOrder != undefined) {
|
||||||
|
apiClient.get(baseUrl, '/api/order/?order_id=' + currentOrder).then(orderReceived);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearOrder = function () {
|
||||||
|
setOrder(undefined);
|
||||||
|
setBadOrder(undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchRobot = function ({
|
||||||
|
action = 'login',
|
||||||
|
newKeys = null,
|
||||||
|
newToken = null,
|
||||||
|
refCode = null,
|
||||||
|
setBadRequest = () => {},
|
||||||
|
}: fetchRobotProps) {
|
||||||
|
setRobot({ ...robot, loading: true, avatarLoaded: false });
|
||||||
|
setBadRequest('');
|
||||||
|
|
||||||
|
let requestBody = {};
|
||||||
|
if (action == 'login') {
|
||||||
|
requestBody.token_sha256 = sha256(newToken ?? robot.token);
|
||||||
|
} else if (action == 'generate' && newToken != null) {
|
||||||
|
const strength = tokenStrength(newToken);
|
||||||
|
requestBody.token_sha256 = sha256(newToken);
|
||||||
|
requestBody.unique_values = strength.uniqueValues;
|
||||||
|
requestBody.counts = strength.counts;
|
||||||
|
requestBody.length = newToken.length;
|
||||||
|
requestBody.ref_code = refCode;
|
||||||
|
requestBody.public_key = newKeys.pubKey ?? robot.pubkey;
|
||||||
|
requestBody.encrypted_private_key = newKeys.encPrivKey ?? robot.encPrivKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
apiClient.post(baseUrl, '/api/user/', requestBody).then((data: any) => {
|
||||||
|
setCurrentOrder(
|
||||||
|
data.active_order_id
|
||||||
|
? data.active_order_id
|
||||||
|
: data.last_order_id
|
||||||
|
? data.last_order_id
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
if (data.bad_request) {
|
||||||
|
setBadRequest(data.bad_request);
|
||||||
|
setRobot({
|
||||||
|
...robot,
|
||||||
|
loading: false,
|
||||||
|
nickname: data.nickname ?? robot.nickname,
|
||||||
|
activeOrderId: data.active_order_id ?? null,
|
||||||
|
referralCode: data.referral_code ?? robot.referralCode,
|
||||||
|
earnedRewards: data.earned_rewards ?? robot.earnedRewards,
|
||||||
|
lastOrderId: data.last_order_id ?? robot.lastOrderId,
|
||||||
|
stealthInvoices: data.wants_stealth ?? robot.stealthInvoices,
|
||||||
|
tgEnabled: data.tg_enabled,
|
||||||
|
tgBotName: data.tg_bot_name,
|
||||||
|
tgToken: data.tg_token,
|
||||||
|
found: false,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setRobot({
|
||||||
|
...robot,
|
||||||
|
nickname: data.nickname,
|
||||||
|
token: newToken ?? robot.token,
|
||||||
|
loading: false,
|
||||||
|
activeOrderId: data.active_order_id ?? null,
|
||||||
|
lastOrderId: data.last_order_id ?? null,
|
||||||
|
referralCode: data.referral_code,
|
||||||
|
earnedRewards: data.earned_rewards ?? 0,
|
||||||
|
stealthInvoices: data.wants_stealth,
|
||||||
|
tgEnabled: data.tg_enabled,
|
||||||
|
tgBotName: data.tg_bot_name,
|
||||||
|
tgToken: data.tg_token,
|
||||||
|
found: data?.found,
|
||||||
|
bitsEntropy: data.token_bits_entropy,
|
||||||
|
shannonEntropy: data.token_shannon_entropy,
|
||||||
|
pubKey: data.public_key,
|
||||||
|
encPrivKey: data.encrypted_private_key,
|
||||||
|
copiedToken: data.found ? true : robot.copiedToken,
|
||||||
|
});
|
||||||
|
systemClient.setItem('robot_token', newToken ?? robot.token);
|
||||||
|
systemClient.setItem('pub_key', data.public_key.split('\n').join('\\'));
|
||||||
|
systemClient.setItem('enc_priv_key', data.encrypted_private_key.split('\n').join('\\'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (baseUrl != '' && page != 'robot') {
|
||||||
|
if (open.profile || (robot.token && robot.nickname === null)) {
|
||||||
|
fetchRobot({ action: 'login' }); // fetch existing robot
|
||||||
|
} else if (robot.token && robot.encPrivKey && robot.pubKey) {
|
||||||
|
fetchRobot({ action: 'login' }); // create new robot with existing token and keys (on network and coordinator change)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [open.profile, baseUrl]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AppContext.Provider
|
||||||
|
value={{
|
||||||
|
torStatus,
|
||||||
|
settings,
|
||||||
|
setSettings,
|
||||||
|
book,
|
||||||
|
setBook,
|
||||||
|
fetchBook,
|
||||||
|
fetchRobot,
|
||||||
|
limits,
|
||||||
|
info,
|
||||||
|
setLimits,
|
||||||
|
fetchLimits,
|
||||||
|
maker,
|
||||||
|
setMaker,
|
||||||
|
clearOrder,
|
||||||
|
robot,
|
||||||
|
setRobot,
|
||||||
|
baseUrl,
|
||||||
|
setBaseUrl,
|
||||||
|
fav,
|
||||||
|
setFav,
|
||||||
|
order,
|
||||||
|
setOrder,
|
||||||
|
badOrder,
|
||||||
|
setBadOrder,
|
||||||
|
setDelay,
|
||||||
|
page,
|
||||||
|
setPage,
|
||||||
|
slideDirection,
|
||||||
|
setSlideDirection,
|
||||||
|
currentOrder,
|
||||||
|
setCurrentOrder,
|
||||||
|
navbarHeight,
|
||||||
|
closeAll,
|
||||||
|
open,
|
||||||
|
setOpen,
|
||||||
|
windowSize,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</AppContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AppContext = React.createContext();
|
@ -22,6 +22,7 @@ class Robot {
|
|||||||
public tgBotName: string = 'unknown';
|
public tgBotName: string = 'unknown';
|
||||||
public tgToken: string = 'unknown';
|
public tgToken: string = 'unknown';
|
||||||
public loading: boolean = false;
|
public loading: boolean = false;
|
||||||
|
public found: boolean = false;
|
||||||
public avatarLoaded: boolean = false;
|
public avatarLoaded: boolean = false;
|
||||||
public copiedToken: boolean = false;
|
public copiedToken: boolean = false;
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,6 @@
|
|||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useContext, useState } from 'react';
|
||||||
import GridLayout, { Layout } from 'react-grid-layout';
|
import GridLayout, { Layout } from 'react-grid-layout';
|
||||||
import { Grid, styled, useTheme } from '@mui/material';
|
import { Grid, styled, useTheme } from '@mui/material';
|
||||||
import { apiClient } from '../services/api';
|
|
||||||
import checkVer from '../utils/checkVer';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Book,
|
|
||||||
LimitList,
|
|
||||||
Maker,
|
|
||||||
Robot,
|
|
||||||
Info,
|
|
||||||
Settings,
|
|
||||||
Favorites,
|
|
||||||
defaultMaker,
|
|
||||||
defaultInfo,
|
|
||||||
} from '../models';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
PlaceholderWidget,
|
PlaceholderWidget,
|
||||||
@ -26,21 +12,7 @@ import {
|
|||||||
import ToolBar from '../pro/ToolBar';
|
import ToolBar from '../pro/ToolBar';
|
||||||
import LandingDialog from '../pro/LandingDialog';
|
import LandingDialog from '../pro/LandingDialog';
|
||||||
|
|
||||||
import defaultCoordinators from '../../static/federation.json';
|
import { AppContext, AppContextProps } from '../contexts/AppContext';
|
||||||
import { getHost } from '../utils';
|
|
||||||
|
|
||||||
const getWindowSize = function (fontSize: number) {
|
|
||||||
// returns window size in EM units
|
|
||||||
return {
|
|
||||||
width: window.innerWidth / fontSize,
|
|
||||||
height: window.innerHeight / fontSize,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
interface MainProps {
|
|
||||||
settings: Settings;
|
|
||||||
setSettings: (state: Settings) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
// To Do. Add dotted grid when layout is not frozen
|
// To Do. Add dotted grid when layout is not frozen
|
||||||
// ${freeze ?
|
// ${freeze ?
|
||||||
@ -57,115 +29,58 @@ const StyledRGL = styled(GridLayout)(
|
|||||||
`,
|
`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const Main = ({ settings, setSettings }: MainProps): JSX.Element => {
|
const defaultLayout: Layout = [
|
||||||
|
{ i: 'Maker', w: 10, h: 16, x: 67, y: 0, minW: 8, maxW: 22, minH: 10, maxH: 28 },
|
||||||
|
{ i: 'Book', w: 43, h: 15, x: 34, y: 16, minW: 6, maxW: 70, minH: 9, maxH: 25 },
|
||||||
|
{ i: 'DepthChart', w: 15, h: 10, x: 19, y: 16, minW: 6, maxW: 22, minH: 9, maxH: 25 },
|
||||||
|
{ i: 'Garage', w: 52, h: 16, x: 0, y: 0, minW: 15, maxW: 78, minH: 8, maxH: 30 },
|
||||||
|
{ i: 'History', w: 8, h: 10, x: 11, y: 16, minW: 6, maxW: 22, minH: 9, maxH: 25 },
|
||||||
|
{ i: 'Trade', w: 15, h: 16, x: 52, y: 0, minW: 6, maxW: 22, minH: 9, maxH: 25 },
|
||||||
|
{ i: 'Settings', w: 11, h: 15, x: 0, y: 16, minW: 6, maxW: 22, minH: 9, maxH: 25 },
|
||||||
|
{ i: 'Other', w: 23, h: 5, x: 11, y: 26, minW: 2, maxW: 50, minH: 4, maxH: 25 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const Main = (): JSX.Element => {
|
||||||
|
const {
|
||||||
|
book,
|
||||||
|
fetchBook,
|
||||||
|
maker,
|
||||||
|
setMaker,
|
||||||
|
setSettings,
|
||||||
|
clearOrder,
|
||||||
|
torStatus,
|
||||||
|
settings,
|
||||||
|
limits,
|
||||||
|
fetchLimits,
|
||||||
|
robot,
|
||||||
|
setRobot,
|
||||||
|
fetchRobot,
|
||||||
|
setOrder,
|
||||||
|
setDelay,
|
||||||
|
info,
|
||||||
|
fav,
|
||||||
|
setFav,
|
||||||
|
baseUrl,
|
||||||
|
order,
|
||||||
|
page,
|
||||||
|
setPage,
|
||||||
|
currentOrder,
|
||||||
|
setCurrentOrder,
|
||||||
|
open,
|
||||||
|
setOpen,
|
||||||
|
windowSize,
|
||||||
|
badOrder,
|
||||||
|
setBadOrder,
|
||||||
|
} = useContext<AppContextProps>(AppContext);
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const em: number = theme.typography.fontSize;
|
const em: number = theme.typography.fontSize;
|
||||||
const toolbarHeight: number = 3;
|
const toolbarHeight: number = 3;
|
||||||
const gridCellSize: number = 2;
|
const gridCellSize: number = 2;
|
||||||
|
|
||||||
const defaultLayout: Layout = [
|
const [openLanding, setOpenLanding] = useState<boolean>(true);
|
||||||
{ i: 'Maker', w: 10, h: 16, x: 67, y: 0, minW: 8, maxW: 22, minH: 10, maxH: 28 },
|
|
||||||
{ i: 'Book', w: 43, h: 15, x: 34, y: 16, minW: 6, maxW: 70, minH: 9, maxH: 25 },
|
|
||||||
{ i: 'DepthChart', w: 15, h: 10, x: 19, y: 16, minW: 6, maxW: 22, minH: 9, maxH: 25 },
|
|
||||||
{ i: 'Garage', w: 52, h: 16, x: 0, y: 0, minW: 15, maxW: 78, minH: 8, maxH: 30 },
|
|
||||||
{ i: 'History', w: 10, h: 10, x: 9, y: 16, minW: 6, maxW: 22, minH: 9, maxH: 25 },
|
|
||||||
{ i: 'Trade', w: 15, h: 16, x: 52, y: 0, minW: 6, maxW: 22, minH: 9, maxH: 25 },
|
|
||||||
{ i: 'Settings', w: 9, h: 15, x: 0, y: 16, minW: 6, maxW: 22, minH: 9, maxH: 25 },
|
|
||||||
{ i: 'Other', w: 25, h: 5, x: 9, y: 26, minW: 2, maxW: 50, minH: 4, maxH: 25 },
|
|
||||||
];
|
|
||||||
|
|
||||||
// All app data structured
|
|
||||||
const [book, setBook] = useState<Book>({ orders: [], loading: true });
|
|
||||||
const [limits, setLimits] = useState<{ list: LimitList; loading: boolean }>({
|
|
||||||
list: [],
|
|
||||||
loading: true,
|
|
||||||
});
|
|
||||||
const [robot, setRobot] = useState<Robot>(new Robot());
|
|
||||||
const [maker, setMaker] = useState<Maker>(defaultMaker);
|
|
||||||
const [info, setInfo] = useState<Info>(defaultInfo);
|
|
||||||
const [coordinators, setCoordinators] = useState<Coordinator[]>(defaultCoordinators);
|
|
||||||
const [fav, setFav] = useState<Favorites>({ type: null, currency: 0 });
|
|
||||||
const [baseUrl, setBaseUrl] = useState<string>('');
|
|
||||||
const [layout, setLayout] = useState<Layout>(defaultLayout);
|
const [layout, setLayout] = useState<Layout>(defaultLayout);
|
||||||
|
|
||||||
const [openLanding, setOpenLanding] = useState<boolean>(true);
|
|
||||||
const [windowSize, setWindowSize] = useState<{ width: number; height: number }>(
|
|
||||||
getWindowSize(em),
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (typeof window !== undefined) {
|
|
||||||
window.addEventListener('resize', onResize);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (baseUrl != '') {
|
|
||||||
fetchBook();
|
|
||||||
fetchLimits();
|
|
||||||
}
|
|
||||||
return () => {
|
|
||||||
if (typeof window !== undefined) {
|
|
||||||
window.removeEventListener('resize', onResize);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, [baseUrl]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
let host = '';
|
|
||||||
if (window.NativeRobosats === undefined) {
|
|
||||||
host = getHost();
|
|
||||||
} else {
|
|
||||||
host =
|
|
||||||
settings.network === 'mainnet'
|
|
||||||
? coordinators[0].mainnetOnion
|
|
||||||
: coordinators[0].testnetOnion;
|
|
||||||
}
|
|
||||||
setBaseUrl(`http://${host}`);
|
|
||||||
}, [settings.network]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setWindowSize(getWindowSize(theme.typography.fontSize));
|
|
||||||
}, [theme.typography.fontSize]);
|
|
||||||
|
|
||||||
const onResize = function () {
|
|
||||||
setWindowSize(getWindowSize(em));
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setWindowSize(getWindowSize(theme.typography.fontSize));
|
|
||||||
}, [theme.typography.fontSize]);
|
|
||||||
|
|
||||||
const fetchLimits = async () => {
|
|
||||||
setLimits({ ...limits, loading: true });
|
|
||||||
const data = apiClient.get(baseUrl, '/api/limits/').then((data) => {
|
|
||||||
setLimits({ list: data ?? [], loading: false });
|
|
||||||
return data;
|
|
||||||
});
|
|
||||||
return await data;
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchBook = function () {
|
|
||||||
setBook({ ...book, loading: true });
|
|
||||||
apiClient.get(baseUrl, '/api/book/').then((data: any) =>
|
|
||||||
setBook({
|
|
||||||
loading: false,
|
|
||||||
orders: data.not_found ? [] : data,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchInfo = function () {
|
|
||||||
apiClient.get(baseUrl, '/api/info/').then((data: any) => {
|
|
||||||
const versionInfo: any = checkVer(data.version.major, data.version.minor, data.version.patch);
|
|
||||||
setInfo({
|
|
||||||
...data,
|
|
||||||
openUpdateClient: versionInfo.updateAvailable,
|
|
||||||
coordinatorVersion: versionInfo.coordinatorVersion,
|
|
||||||
clientVersion: versionInfo.clientVersion,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log(layout);
|
|
||||||
return (
|
return (
|
||||||
<Grid container direction='column' sx={{ width: `${windowSize.width}em` }}>
|
<Grid container direction='column' sx={{ width: `${windowSize.width}em` }}>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
@ -191,41 +106,16 @@ const Main = ({ settings, setSettings }: MainProps): JSX.Element => {
|
|||||||
onLayoutChange={(layout: Layout) => setLayout(layout)}
|
onLayoutChange={(layout: Layout) => setLayout(layout)}
|
||||||
>
|
>
|
||||||
<div key='Maker'>
|
<div key='Maker'>
|
||||||
<MakerWidget
|
<MakerWidget />
|
||||||
baseUrl={baseUrl}
|
|
||||||
limits={limits}
|
|
||||||
fetchLimits={fetchLimits}
|
|
||||||
fav={fav}
|
|
||||||
setFav={setFav}
|
|
||||||
maker={maker}
|
|
||||||
setMaker={setMaker}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div key='Book'>
|
<div key='Book'>
|
||||||
<BookWidget
|
<BookWidget layout={layout[1]} gridCellSize={gridCellSize} />
|
||||||
baseUrl={baseUrl}
|
|
||||||
book={book}
|
|
||||||
layout={layout[1]}
|
|
||||||
gridCellSize={gridCellSize}
|
|
||||||
fetchBook={fetchBook}
|
|
||||||
fav={fav}
|
|
||||||
setFav={setFav}
|
|
||||||
windowSize={windowSize}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div key='DepthChart'>
|
<div key='DepthChart'>
|
||||||
<DepthChartWidget
|
<DepthChartWidget gridCellSize={gridCellSize} layout={layout[2]} />
|
||||||
baseUrl={baseUrl}
|
|
||||||
orders={book.orders}
|
|
||||||
gridCellSize={gridCellSize}
|
|
||||||
limitList={limits.list}
|
|
||||||
layout={layout[2]}
|
|
||||||
currency={fav.currency}
|
|
||||||
windowSize={windowSize}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div key='Settings'>
|
<div key='Settings'>
|
||||||
<SettingsWidget settings={settings} setSettings={setSettings} />
|
<SettingsWidget />
|
||||||
</div>
|
</div>
|
||||||
<div key='Garage'>
|
<div key='Garage'>
|
||||||
<PlaceholderWidget label='Robot Garage' />
|
<PlaceholderWidget label='Robot Garage' />
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
|
import { AppContext, AppContextProps } from '../../contexts/AppContext';
|
||||||
|
|
||||||
import { Book, Favorites } from '../../models';
|
import { Book, Favorites } from '../../models';
|
||||||
import { Paper, useTheme } from '@mui/material';
|
import { Paper } from '@mui/material';
|
||||||
import BookTable from '../../components/BookTable';
|
import BookTable from '../../components/BookTable';
|
||||||
|
|
||||||
interface BookWidgetProps {
|
interface BookWidgetProps {
|
||||||
@ -24,13 +25,7 @@ const BookWidget = React.forwardRef(
|
|||||||
(
|
(
|
||||||
{
|
{
|
||||||
layout,
|
layout,
|
||||||
baseUrl,
|
|
||||||
gridCellSize = 2,
|
gridCellSize = 2,
|
||||||
book,
|
|
||||||
fetchBook,
|
|
||||||
fav,
|
|
||||||
setFav,
|
|
||||||
windowSize,
|
|
||||||
style,
|
style,
|
||||||
className,
|
className,
|
||||||
onMouseDown,
|
onMouseDown,
|
||||||
@ -39,24 +34,18 @@ const BookWidget = React.forwardRef(
|
|||||||
}: BookWidgetProps,
|
}: BookWidgetProps,
|
||||||
ref,
|
ref,
|
||||||
) => {
|
) => {
|
||||||
const theme = useTheme();
|
const { book, windowSize, fav } = useContext<AppContextProps>(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%' }}>
|
||||||
<BookTable
|
<BookTable
|
||||||
baseUrl={baseUrl}
|
|
||||||
elevation={0}
|
elevation={0}
|
||||||
clickRefresh={() => fetchBook()}
|
|
||||||
book={book}
|
|
||||||
fav={fav}
|
|
||||||
fillContainer={true}
|
fillContainer={true}
|
||||||
maxWidth={layout.w * gridCellSize} // EM units
|
maxWidth={layout.w * gridCellSize} // EM units
|
||||||
maxHeight={layout.h * gridCellSize} // EM units
|
maxHeight={layout.h * gridCellSize} // EM units
|
||||||
fullWidth={windowSize.width} // EM units
|
fullWidth={windowSize.width} // EM units
|
||||||
fullHeight={windowSize.height} // EM units
|
fullHeight={windowSize.height} // EM units
|
||||||
defaultFullscreen={false}
|
defaultFullscreen={false}
|
||||||
onCurrencyChange={(e) => setFav({ ...fav, currency: e.target.value })}
|
|
||||||
onTypeChange={(mouseEvent, val) => setFav({ ...fav, type: val })}
|
|
||||||
/>
|
/>
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
|
@ -1,22 +1,16 @@
|
|||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
|
import { AppContext, AppContextProps } from '../../contexts/AppContext';
|
||||||
import { Order, LimitList } from '../../models';
|
|
||||||
import { Paper, useTheme } from '@mui/material';
|
import { Paper, useTheme } from '@mui/material';
|
||||||
import DepthChart from '../../components/Charts/DepthChart';
|
import DepthChart from '../../components/Charts/DepthChart';
|
||||||
|
|
||||||
interface DepthChartWidgetProps {
|
interface DepthChartWidgetProps {
|
||||||
layout: any;
|
layout: any;
|
||||||
gridCellSize: number;
|
gridCellSize: number;
|
||||||
orders: PublicOrder[];
|
|
||||||
currency: number;
|
|
||||||
limitList: LimitList;
|
|
||||||
windowSize: { width: number; height: number };
|
|
||||||
style?: Object;
|
style?: Object;
|
||||||
className?: string;
|
className?: string;
|
||||||
onMouseDown?: () => void;
|
onMouseDown?: () => void;
|
||||||
onMouseUp?: () => void;
|
onMouseUp?: () => void;
|
||||||
onTouchEnd?: () => void;
|
onTouchEnd?: () => void;
|
||||||
baseUrl: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const DepthChartWidget = React.forwardRef(
|
const DepthChartWidget = React.forwardRef(
|
||||||
@ -24,11 +18,6 @@ const DepthChartWidget = React.forwardRef(
|
|||||||
{
|
{
|
||||||
layout,
|
layout,
|
||||||
gridCellSize,
|
gridCellSize,
|
||||||
limitList,
|
|
||||||
orders,
|
|
||||||
baseUrl,
|
|
||||||
currency,
|
|
||||||
windowSize,
|
|
||||||
style,
|
style,
|
||||||
className,
|
className,
|
||||||
onMouseDown,
|
onMouseDown,
|
||||||
@ -38,22 +27,19 @@ const DepthChartWidget = React.forwardRef(
|
|||||||
ref,
|
ref,
|
||||||
) => {
|
) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const { fav, book, limits } = useContext<AppContextProps>(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%' }}>
|
||||||
<DepthChart
|
<DepthChart
|
||||||
baseUrl={baseUrl}
|
|
||||||
elevation={0}
|
elevation={0}
|
||||||
orders={orders}
|
|
||||||
currency={currency}
|
|
||||||
limits={limitList}
|
|
||||||
maxWidth={layout.w * gridCellSize} // EM units
|
maxWidth={layout.w * gridCellSize} // EM units
|
||||||
maxHeight={layout.h * gridCellSize} // EM units
|
maxHeight={layout.h * gridCellSize} // EM units
|
||||||
fillContainer={true}
|
fillContainer={true}
|
||||||
/>
|
/>
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
}, [currency, orders, limitList, layout]);
|
}, [fav.currency, book, limits, layout]);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,17 +1,11 @@
|
|||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
|
import { AppContext, AppContextProps } 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';
|
||||||
import { Paper } from '@mui/material';
|
import { Paper } from '@mui/material';
|
||||||
|
|
||||||
interface MakerWidgetProps {
|
interface MakerWidgetProps {
|
||||||
limits: { list: LimitList; loading: boolean };
|
|
||||||
fetchLimits: () => void;
|
|
||||||
fav: Favorites;
|
|
||||||
maker: Maker;
|
|
||||||
setFav: (state: Favorites) => void;
|
|
||||||
setMaker: (state: Maker) => void;
|
|
||||||
baseUrl: string;
|
|
||||||
style?: Object;
|
style?: Object;
|
||||||
className?: string;
|
className?: string;
|
||||||
onMouseDown?: () => void;
|
onMouseDown?: () => void;
|
||||||
@ -20,38 +14,15 @@ interface MakerWidgetProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MakerWidget = React.forwardRef(
|
const MakerWidget = React.forwardRef(
|
||||||
(
|
({ style, className, onMouseDown, onMouseUp, onTouchEnd }: MakerWidgetProps, ref) => {
|
||||||
{
|
const { maker, fav, limits } = useContext<AppContextProps>(AppContext);
|
||||||
maker,
|
|
||||||
setMaker,
|
|
||||||
limits,
|
|
||||||
fetchLimits,
|
|
||||||
fav,
|
|
||||||
setFav,
|
|
||||||
baseUrl,
|
|
||||||
style,
|
|
||||||
className,
|
|
||||||
onMouseDown,
|
|
||||||
onMouseUp,
|
|
||||||
onTouchEnd,
|
|
||||||
}: MakerWidgetProps,
|
|
||||||
ref,
|
|
||||||
) => {
|
|
||||||
return React.useMemo(() => {
|
return React.useMemo(() => {
|
||||||
return (
|
return (
|
||||||
<Paper
|
<Paper
|
||||||
elevation={3}
|
elevation={3}
|
||||||
style={{ padding: 8, overflow: 'auto', width: '100%', height: '100%' }}
|
style={{ padding: 8, overflow: 'auto', width: '100%', height: '100%' }}
|
||||||
>
|
>
|
||||||
<MakerForm
|
<MakerForm />
|
||||||
baseUrl={baseUrl}
|
|
||||||
limits={limits}
|
|
||||||
fetchLimits={fetchLimits}
|
|
||||||
maker={maker}
|
|
||||||
setMaker={setMaker}
|
|
||||||
fav={fav}
|
|
||||||
setFav={setFav}
|
|
||||||
/>
|
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
}, [maker, limits, fav]);
|
}, [maker, limits, fav]);
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
|
import { AppContextProps, AppContext } from '../../contexts/AppContext';
|
||||||
import { Settings } from '../../models';
|
import { Settings } from '../../models';
|
||||||
import { Paper, useTheme } from '@mui/material';
|
import { Paper } from '@mui/material';
|
||||||
import SettingsForm from '../../components/SettingsForm';
|
import SettingsForm from '../../components/SettingsForm';
|
||||||
|
|
||||||
interface SettingsWidgetProps {
|
interface SettingsWidgetProps {
|
||||||
settings: Settings;
|
|
||||||
setSettings: (state: Settings) => void;
|
|
||||||
style?: Object;
|
style?: Object;
|
||||||
className?: string;
|
className?: string;
|
||||||
onMouseDown?: () => void;
|
onMouseDown?: () => void;
|
||||||
@ -15,26 +13,15 @@ interface SettingsWidgetProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const SettingsWidget = React.forwardRef(
|
const SettingsWidget = React.forwardRef(
|
||||||
(
|
({ style, className, onMouseDown, onMouseUp, onTouchEnd }: SettingsWidgetProps, ref) => {
|
||||||
{
|
const { settings } = useContext<AppContextProps>(AppContext);
|
||||||
settings,
|
|
||||||
setSettings,
|
|
||||||
style,
|
|
||||||
className,
|
|
||||||
onMouseDown,
|
|
||||||
onMouseUp,
|
|
||||||
onTouchEnd,
|
|
||||||
}: SettingsWidgetProps,
|
|
||||||
ref,
|
|
||||||
) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
return React.useMemo(() => {
|
return React.useMemo(() => {
|
||||||
return (
|
return (
|
||||||
<Paper
|
<Paper
|
||||||
elevation={3}
|
elevation={3}
|
||||||
style={{ width: '100%', height: '100%', position: 'relative', top: '0.6em', left: '0em' }}
|
style={{ width: '100%', height: '100%', position: 'relative', top: '0.6em', left: '0em' }}
|
||||||
>
|
>
|
||||||
<SettingsForm dense={true} settings={settings} setSettings={setSettings} />
|
<SettingsForm dense={true} />
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
}, [settings]);
|
}, [settings]);
|
||||||
|
@ -41,7 +41,8 @@
|
|||||||
"Connection encrypted and anonymized using TOR.": "Connexió xifrada i anònima mitjançant TOR.",
|
"Connection encrypted and anonymized using TOR.": "Connexió xifrada i anònima mitjançant TOR.",
|
||||||
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "Això garanteix la màxima privadesa, però és possible que sentis que l'aplicació es comporta lenta. Si es perd la connexió, reinicia l'aplicació.",
|
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "Això garanteix la màxima privadesa, però és possible que sentis que l'aplicació es comporta lenta. Si es perd la connexió, reinicia l'aplicació.",
|
||||||
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
||||||
"Please, introduce your robot token to re-build your robot and gain access to its trades.": "Si us plau, introdueix el teu token per reconstruir el teu robot i accedir a les seves operacions.",
|
"Robot recovery": "Robot recovery",
|
||||||
|
"Enter your robot token to re-build your robot and gain access to its trades.": "Introdueix el teu token per reconstruir el teu robot i accedir a les seves operacions.",
|
||||||
"Paste token here": "Enganxa el token aquí",
|
"Paste token here": "Enganxa el token aquí",
|
||||||
"Recover": "Recuperar",
|
"Recover": "Recuperar",
|
||||||
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
||||||
|
@ -41,7 +41,8 @@
|
|||||||
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
||||||
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
||||||
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
||||||
"Please, introduce your robot token to re-build your robot and gain access to its trades.": "Please, introduce your robot token to re-build your robot and gain access to its trades.",
|
"Robot recovery": "Robot recovery",
|
||||||
|
"Enter your robot token to re-build your robot and gain access to its trades.": "Enter your robot token to re-build your robot and gain access to its trades.",
|
||||||
"Paste token here": "Paste token here",
|
"Paste token here": "Paste token here",
|
||||||
"Recover": "Recover",
|
"Recover": "Recover",
|
||||||
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
||||||
|
@ -41,7 +41,8 @@
|
|||||||
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
||||||
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
||||||
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
||||||
"Please, introduce your robot token to re-build your robot and gain access to its trades.": "Please, introduce your robot token to re-build your robot and gain access to its trades.",
|
"Robot recovery": "Robot recovery",
|
||||||
|
"Enter your robot token to re-build your robot and gain access to its trades.": "Enter your robot token to re-build your robot and gain access to its trades.",
|
||||||
"Paste token here": "Paste token here",
|
"Paste token here": "Paste token here",
|
||||||
"Recover": "Recover",
|
"Recover": "Recover",
|
||||||
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
||||||
|
@ -41,7 +41,8 @@
|
|||||||
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
||||||
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
||||||
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
||||||
"Please, introduce your robot token to re-build your robot and gain access to its trades.": "Please, introduce your robot token to re-build your robot and gain access to its trades.",
|
"Robot recovery": "Robot recovery",
|
||||||
|
"Enter your robot token to re-build your robot and gain access to its trades.": "Enter your robot token to re-build your robot and gain access to its trades.",
|
||||||
"Paste token here": "Paste token here",
|
"Paste token here": "Paste token here",
|
||||||
"Recover": "Recover",
|
"Recover": "Recover",
|
||||||
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
||||||
|
@ -41,7 +41,8 @@
|
|||||||
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
||||||
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
||||||
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
||||||
"Please, introduce your robot token to re-build your robot and gain access to its trades.": "Please, introduce your robot token to re-build your robot and gain access to its trades.",
|
"Robot recovery": "Robot recovery",
|
||||||
|
"Enter your robot token to re-build your robot and gain access to its trades.": "Enter your robot token to re-build your robot and gain access to its trades.",
|
||||||
"Paste token here": "Paste token here",
|
"Paste token here": "Paste token here",
|
||||||
"Recover": "Recover",
|
"Recover": "Recover",
|
||||||
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
||||||
|
@ -41,7 +41,8 @@
|
|||||||
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
||||||
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
||||||
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
||||||
"Please, introduce your robot token to re-build your robot and gain access to its trades.": "Please, introduce your robot token to re-build your robot and gain access to its trades.",
|
"Robot recovery": "Robot recovery",
|
||||||
|
"Enter your robot token to re-build your robot and gain access to its trades.": "Enter your robot token to re-build your robot and gain access to its trades.",
|
||||||
"Paste token here": "Paste token here",
|
"Paste token here": "Paste token here",
|
||||||
"Recover": "Recover",
|
"Recover": "Recover",
|
||||||
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
||||||
|
@ -41,7 +41,8 @@
|
|||||||
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
||||||
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
||||||
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
||||||
"Please, introduce your robot token to re-build your robot and gain access to its trades.": "Please, introduce your robot token to re-build your robot and gain access to its trades.",
|
"Robot recovery": "Robot recovery",
|
||||||
|
"Enter your robot token to re-build your robot and gain access to its trades.": "Enter your robot token to re-build your robot and gain access to its trades.",
|
||||||
"Paste token here": "Paste token here",
|
"Paste token here": "Paste token here",
|
||||||
"Recover": "Recover",
|
"Recover": "Recover",
|
||||||
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
||||||
|
@ -41,7 +41,8 @@
|
|||||||
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
||||||
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
||||||
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
||||||
"Please, introduce your robot token to re-build your robot and gain access to its trades.": "Please, introduce your robot token to re-build your robot and gain access to its trades.",
|
"Robot recovery": "Robot recovery",
|
||||||
|
"Enter your robot token to re-build your robot and gain access to its trades.": "Enter your robot token to re-build your robot and gain access to its trades.",
|
||||||
"Paste token here": "Paste token here",
|
"Paste token here": "Paste token here",
|
||||||
"Recover": "Recover",
|
"Recover": "Recover",
|
||||||
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
||||||
|
@ -41,7 +41,8 @@
|
|||||||
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
||||||
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
||||||
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
||||||
"Please, introduce your robot token to re-build your robot and gain access to its trades.": "Please, introduce your robot token to re-build your robot and gain access to its trades.",
|
"Robot recovery": "Robot recovery",
|
||||||
|
"Enter your robot token to re-build your robot and gain access to its trades.": "Enter your robot token to re-build your robot and gain access to its trades.",
|
||||||
"Paste token here": "Paste token here",
|
"Paste token here": "Paste token here",
|
||||||
"Recover": "Recover",
|
"Recover": "Recover",
|
||||||
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
||||||
|
@ -41,7 +41,8 @@
|
|||||||
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
||||||
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
||||||
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
||||||
"Please, introduce your robot token to re-build your robot and gain access to its trades.": "Please, introduce your robot token to re-build your robot and gain access to its trades.",
|
"Robot recovery": "Robot recovery",
|
||||||
|
"Enter your robot token to re-build your robot and gain access to its trades.": "Enter your robot token to re-build your robot and gain access to its trades.",
|
||||||
"Paste token here": "Paste token here",
|
"Paste token here": "Paste token here",
|
||||||
"Recover": "Recover",
|
"Recover": "Recover",
|
||||||
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
||||||
|
@ -41,7 +41,8 @@
|
|||||||
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
||||||
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
||||||
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
||||||
"Please, introduce your robot token to re-build your robot and gain access to its trades.": "Please, introduce your robot token to re-build your robot and gain access to its trades.",
|
"Robot recovery": "Robot recovery",
|
||||||
|
"Enter your robot token to re-build your robot and gain access to its trades.": "Enter your robot token to re-build your robot and gain access to its trades.",
|
||||||
"Paste token here": "Paste token here",
|
"Paste token here": "Paste token here",
|
||||||
"Recover": "Recover",
|
"Recover": "Recover",
|
||||||
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
||||||
|
@ -41,7 +41,8 @@
|
|||||||
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
||||||
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
||||||
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
||||||
"Please, introduce your robot token to re-build your robot and gain access to its trades.": "Please, introduce your robot token to re-build your robot and gain access to its trades.",
|
"Robot recovery": "Robot recovery",
|
||||||
|
"Enter your robot token to re-build your robot and gain access to its trades.": "Enter your robot token to re-build your robot and gain access to its trades.",
|
||||||
"Paste token here": "Paste token here",
|
"Paste token here": "Paste token here",
|
||||||
"Recover": "Recover",
|
"Recover": "Recover",
|
||||||
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
||||||
|
@ -41,7 +41,8 @@
|
|||||||
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
||||||
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
||||||
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
||||||
"Please, introduce your robot token to re-build your robot and gain access to its trades.": "Please, introduce your robot token to re-build your robot and gain access to its trades.",
|
"Robot recovery": "Robot recovery",
|
||||||
|
"Enter your robot token to re-build your robot and gain access to its trades.": "Enter your robot token to re-build your robot and gain access to its trades.",
|
||||||
"Paste token here": "Paste token here",
|
"Paste token here": "Paste token here",
|
||||||
"Recover": "Recover",
|
"Recover": "Recover",
|
||||||
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
||||||
|
@ -41,7 +41,8 @@
|
|||||||
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
||||||
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
||||||
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
||||||
"Please, introduce your robot token to re-build your robot and gain access to its trades.": "Please, introduce your robot token to re-build your robot and gain access to its trades.",
|
"Robot recovery": "Robot recovery",
|
||||||
|
"Enter your robot token to re-build your robot and gain access to its trades.": "Enter your robot token to re-build your robot and gain access to its trades.",
|
||||||
"Paste token here": "Paste token here",
|
"Paste token here": "Paste token here",
|
||||||
"Recover": "Recover",
|
"Recover": "Recover",
|
||||||
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
||||||
|
@ -41,7 +41,8 @@
|
|||||||
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
"Connection encrypted and anonymized using TOR.": "Connection encrypted and anonymized using TOR.",
|
||||||
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
"This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.": "This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.",
|
||||||
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
"#7": "Phrases in basic/RobotPage/Recovery.tsx",
|
||||||
"Please, introduce your robot token to re-build your robot and gain access to its trades.": "Please, introduce your robot token to re-build your robot and gain access to its trades.",
|
"Robot recovery": "Robot recovery",
|
||||||
|
"Enter your robot token to re-build your robot and gain access to its trades.": "Enter your robot token to re-build your robot and gain access to its trades.",
|
||||||
"Paste token here": "Paste token here",
|
"Paste token here": "Paste token here",
|
||||||
"Recover": "Recover",
|
"Recover": "Recover",
|
||||||
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
"#8": "Phrases in basic/RobotPage/Welcome.tsx",
|
||||||
|
Loading…
Reference in New Issue
Block a user