mirror of
https://github.com/Reckless-Satoshi/robosats.git
synced 2024-09-11 11:55:26 +03:00
Add RoboSats PRO frame as react layout grid playground (#299)
* Add react layout grid playground * Add BookWidget and style * Rename basic.js back to main.js
This commit is contained in:
parent
5723cde20e
commit
a4a3dbb95e
82
frontend/package-lock.json
generated
82
frontend/package-lock.json
generated
@ -35,6 +35,7 @@
|
||||
"react": "^18.2.0",
|
||||
"react-countdown": "^2.3.2",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-grid-layout": "^1.3.4",
|
||||
"react-i18next": "^11.16.2",
|
||||
"react-image": "^4.0.3",
|
||||
"react-qr-code": "^2.0.3",
|
||||
@ -9863,6 +9864,11 @@
|
||||
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash.isequal": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
@ -13073,6 +13079,35 @@
|
||||
"react": "^18.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-draggable": {
|
||||
"version": "4.4.5",
|
||||
"resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.5.tgz",
|
||||
"integrity": "sha512-OMHzJdyJbYTZo4uQE393fHcqqPYsEtkjfMgvCHr6rejT+Ezn4OZbNyGH50vv+SunC1RMvwOTSWkEODQLzw1M9g==",
|
||||
"dependencies": {
|
||||
"clsx": "^1.1.1",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 16.3.0",
|
||||
"react-dom": ">= 16.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-grid-layout": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/react-grid-layout/-/react-grid-layout-1.3.4.tgz",
|
||||
"integrity": "sha512-sB3rNhorW77HUdOjB4JkelZTdJGQKuXLl3gNg+BI8gJkTScspL1myfZzW/EM0dLEn+1eH+xW+wNqk0oIM9o7cw==",
|
||||
"dependencies": {
|
||||
"clsx": "^1.1.1",
|
||||
"lodash.isequal": "^4.0.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-draggable": "^4.0.0",
|
||||
"react-resizable": "^3.0.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 16.3.0",
|
||||
"react-dom": ">= 16.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-i18next": {
|
||||
"version": "11.18.5",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.18.5.tgz",
|
||||
@ -13132,6 +13167,18 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-resizable": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-resizable/-/react-resizable-3.0.4.tgz",
|
||||
"integrity": "sha512-StnwmiESiamNzdRHbSSvA65b0ZQJ7eVQpPusrSmcpyGKzC0gojhtO62xxH6YOBmepk9dQTBi9yxidL3W4s3EBA==",
|
||||
"dependencies": {
|
||||
"prop-types": "15.x",
|
||||
"react-draggable": "^4.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 16.3"
|
||||
}
|
||||
},
|
||||
"node_modules/react-responsive": {
|
||||
"version": "9.0.0-beta.10",
|
||||
"resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-9.0.0-beta.10.tgz",
|
||||
@ -22098,6 +22145,11 @@
|
||||
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.isequal": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
|
||||
},
|
||||
"lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
@ -24312,6 +24364,27 @@
|
||||
"scheduler": "^0.23.0"
|
||||
}
|
||||
},
|
||||
"react-draggable": {
|
||||
"version": "4.4.5",
|
||||
"resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.5.tgz",
|
||||
"integrity": "sha512-OMHzJdyJbYTZo4uQE393fHcqqPYsEtkjfMgvCHr6rejT+Ezn4OZbNyGH50vv+SunC1RMvwOTSWkEODQLzw1M9g==",
|
||||
"requires": {
|
||||
"clsx": "^1.1.1",
|
||||
"prop-types": "^15.8.1"
|
||||
}
|
||||
},
|
||||
"react-grid-layout": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/react-grid-layout/-/react-grid-layout-1.3.4.tgz",
|
||||
"integrity": "sha512-sB3rNhorW77HUdOjB4JkelZTdJGQKuXLl3gNg+BI8gJkTScspL1myfZzW/EM0dLEn+1eH+xW+wNqk0oIM9o7cw==",
|
||||
"requires": {
|
||||
"clsx": "^1.1.1",
|
||||
"lodash.isequal": "^4.0.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-draggable": "^4.0.0",
|
||||
"react-resizable": "^3.0.4"
|
||||
}
|
||||
},
|
||||
"react-i18next": {
|
||||
"version": "11.18.5",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.18.5.tgz",
|
||||
@ -24346,6 +24419,15 @@
|
||||
"qr.js": "0.0.0"
|
||||
}
|
||||
},
|
||||
"react-resizable": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-resizable/-/react-resizable-3.0.4.tgz",
|
||||
"integrity": "sha512-StnwmiESiamNzdRHbSSvA65b0ZQJ7eVQpPusrSmcpyGKzC0gojhtO62xxH6YOBmepk9dQTBi9yxidL3W4s3EBA==",
|
||||
"requires": {
|
||||
"prop-types": "15.x",
|
||||
"react-draggable": "^4.0.3"
|
||||
}
|
||||
},
|
||||
"react-responsive": {
|
||||
"version": "9.0.0-beta.10",
|
||||
"resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-9.0.0-beta.10.tgz",
|
||||
|
@ -73,6 +73,7 @@
|
||||
"react": "^18.2.0",
|
||||
"react-countdown": "^2.3.2",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-grid-layout": "^1.3.4",
|
||||
"react-i18next": "^11.16.2",
|
||||
"react-image": "^4.0.3",
|
||||
"react-qr-code": "^2.0.3",
|
||||
|
@ -1,89 +1,57 @@
|
||||
import React, { Suspense, useState } from 'react';
|
||||
import React, { Suspense, useState, useEffect } from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import Main from './basic/Main';
|
||||
import { CssBaseline, IconButton } from '@mui/material';
|
||||
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||
import { CssBaseline } from '@mui/material';
|
||||
import { ThemeProvider, createTheme, Theme } from '@mui/material/styles';
|
||||
import UnsafeAlert from './components/UnsafeAlert';
|
||||
import { LearnDialog } from './components/Dialogs';
|
||||
import TorConnection from './components/TorConnection';
|
||||
|
||||
import { I18nextProvider } from 'react-i18next';
|
||||
import i18n from './i18n/Web';
|
||||
|
||||
// Icons
|
||||
import DarkModeIcon from '@mui/icons-material/DarkMode';
|
||||
import LightModeIcon from '@mui/icons-material/LightMode';
|
||||
import SchoolIcon from '@mui/icons-material/School';
|
||||
import { systemClient } from './services/System';
|
||||
import { Settings, defaultSettings } from './models';
|
||||
|
||||
const defaultTheme = createTheme({
|
||||
const defaultTheme: Theme = createTheme({
|
||||
palette: {
|
||||
mode:
|
||||
window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
? 'dark'
|
||||
: 'light',
|
||||
mode: defaultSettings.mode,
|
||||
background: {
|
||||
default:
|
||||
window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
? '#070707'
|
||||
: '#fff',
|
||||
default: defaultSettings.mode === 'dark' ? '#070707' : '#fff',
|
||||
},
|
||||
},
|
||||
typography: { fontSize: defaultSettings.fontSize },
|
||||
});
|
||||
|
||||
const App = (): JSX.Element => {
|
||||
const [openLearn, setOpenLearn] = useState<boolean>(false);
|
||||
const [theme, setTheme] = useState(defaultTheme);
|
||||
const [theme, setTheme] = useState<Theme>(defaultTheme);
|
||||
const [settings, setSettings] = useState<Settings>(defaultSettings);
|
||||
|
||||
const handleModeChange = function () {
|
||||
if (theme.palette.mode === 'light') {
|
||||
setTheme(
|
||||
createTheme({
|
||||
palette: {
|
||||
mode: 'dark',
|
||||
background: {
|
||||
default: '#070707',
|
||||
},
|
||||
const updateTheme = function () {
|
||||
setTheme(
|
||||
createTheme({
|
||||
palette: {
|
||||
mode: settings.mode,
|
||||
background: {
|
||||
default: settings.mode === 'dark' ? '#070707' : '#fff',
|
||||
},
|
||||
}),
|
||||
);
|
||||
} else if (theme.palette.mode === 'dark') {
|
||||
setTheme(
|
||||
createTheme({
|
||||
palette: {
|
||||
mode: 'light',
|
||||
background: {
|
||||
default: '#fff',
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
},
|
||||
typography: { fontSize: settings.fontSize },
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
updateTheme();
|
||||
}, [settings]);
|
||||
|
||||
return (
|
||||
<Suspense fallback='loading language'>
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<LearnDialog open={openLearn} onClose={() => setOpenLearn(false)} />
|
||||
<TorConnection />
|
||||
<IconButton
|
||||
color='inherit'
|
||||
sx={{ position: 'fixed', right: '34px', color: 'text.secondary' }}
|
||||
onClick={() => setOpenLearn(true)}
|
||||
>
|
||||
<SchoolIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
color='inherit'
|
||||
sx={{ position: 'fixed', right: '0px', color: 'text.secondary' }}
|
||||
onClick={() => handleModeChange()}
|
||||
>
|
||||
{theme.palette.mode === 'dark' ? <LightModeIcon /> : <DarkModeIcon />}
|
||||
</IconButton>
|
||||
<UnsafeAlert className='unsafeAlert' />
|
||||
<Main />
|
||||
<Main settings={settings} setSettings={setSettings} />
|
||||
</ThemeProvider>
|
||||
</I18nextProvider>
|
||||
</Suspense>
|
||||
|
@ -83,37 +83,6 @@ const BookPage = ({
|
||||
setFav({ ...fav, type: val });
|
||||
};
|
||||
|
||||
const NoOrdersFound = function () {
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
direction='column'
|
||||
justifyContent='center'
|
||||
alignItems='center'
|
||||
sx={{ width: '100%', height: '100%' }}
|
||||
>
|
||||
<Grid item>
|
||||
<Typography align='center' component='h5' variant='h5'>
|
||||
{fav.type == 0
|
||||
? t('No orders found to sell BTC for {{currencyCode}}', {
|
||||
currencyCode:
|
||||
fav.currency == 0 ? t('ANY') : currencyDict[fav.currency.toString()],
|
||||
})
|
||||
: t('No orders found to buy BTC for {{currencyCode}}', {
|
||||
currencyCode:
|
||||
fav.currency == 0 ? t('ANY') : currencyDict[fav.currency.toString()],
|
||||
})}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography align='center' color='primary' variant='h6'>
|
||||
{t('Be the first one to create an order')}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
const NavButtons = function () {
|
||||
return (
|
||||
<ButtonGroup variant='contained' color='inherit'>
|
||||
@ -153,7 +122,6 @@ const BookPage = ({
|
||||
<MakerForm
|
||||
limits={limits}
|
||||
fetchLimits={fetchLimits}
|
||||
pricingMethods={false}
|
||||
maker={maker}
|
||||
setMaker={setMaker}
|
||||
fav={fav}
|
||||
@ -185,7 +153,6 @@ const BookPage = ({
|
||||
defaultFullscreen={false}
|
||||
onCurrencyChange={handleCurrencyChange}
|
||||
onTypeChange={handleTypeChange}
|
||||
noResultsOverlay={NoOrdersFound}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
@ -220,7 +187,6 @@ const BookPage = ({
|
||||
defaultFullscreen={false}
|
||||
onCurrencyChange={handleCurrencyChange}
|
||||
onTypeChange={handleTypeChange}
|
||||
noResultsOverlay={NoOrdersFound}
|
||||
/>
|
||||
)}
|
||||
</Grid>
|
||||
|
@ -1,12 +1,13 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { HashRouter, BrowserRouter, Switch, Route, useHistory } from 'react-router-dom';
|
||||
import { useTheme } from '@mui/material';
|
||||
import { useTheme, IconButton } from '@mui/material';
|
||||
|
||||
import UserGenPage from './UserGenPage';
|
||||
import MakerPage from './MakerPage';
|
||||
import BookPage from './BookPage';
|
||||
import OrderPage from './OrderPage';
|
||||
import BottomBar from './BottomBar';
|
||||
import { LearnDialog } from '../components/Dialogs';
|
||||
|
||||
import { apiClient } from '../services/api';
|
||||
import checkVer from '../utils/checkVer';
|
||||
@ -22,9 +23,13 @@ import {
|
||||
defaultMaker,
|
||||
defaultRobot,
|
||||
defaultInfo,
|
||||
defaultSettings,
|
||||
} from '../models';
|
||||
|
||||
// Icons
|
||||
import DarkModeIcon from '@mui/icons-material/DarkMode';
|
||||
import LightModeIcon from '@mui/icons-material/LightMode';
|
||||
import SchoolIcon from '@mui/icons-material/School';
|
||||
|
||||
const getWindowSize = function (fontSize: number) {
|
||||
// returns window size in EM units
|
||||
return {
|
||||
@ -33,11 +38,18 @@ const getWindowSize = function (fontSize: number) {
|
||||
};
|
||||
};
|
||||
|
||||
const Main = (): JSX.Element => {
|
||||
interface MainProps {
|
||||
updateTheme: () => void;
|
||||
settings: Settings;
|
||||
setSettings: (state: Settings) => void;
|
||||
}
|
||||
|
||||
const Main = ({ settings, setSettings }: MainProps): JSX.Element => {
|
||||
const theme = useTheme();
|
||||
const history = useHistory();
|
||||
const Router = window.NativeRobosats != null ? HashRouter : BrowserRouter;
|
||||
const basename = window.NativeRobosats != null ? window.location.pathname : '';
|
||||
const [openLearn, setOpenLearn] = useState<boolean>(false);
|
||||
|
||||
// All app data structured
|
||||
const [book, setBook] = useState<Book>({ orders: [], loading: true });
|
||||
@ -49,7 +61,6 @@ const Main = (): JSX.Element => {
|
||||
const [maker, setMaker] = useState<Maker>(defaultMaker);
|
||||
const [info, setInfo] = useState<Info>(defaultInfo);
|
||||
const [fav, setFav] = useState<Favorites>({ type: null, currency: 0 });
|
||||
const [settings, setSettings] = useState<Settings>(defaultSettings);
|
||||
|
||||
const [windowSize, setWindowSize] = useState<{ width: number; height: number }>(
|
||||
getWindowSize(theme.typography.fontSize),
|
||||
@ -119,6 +130,25 @@ const Main = (): JSX.Element => {
|
||||
|
||||
return (
|
||||
<Router basename={basename}>
|
||||
<div className='temporaryUpperIcons'>
|
||||
<LearnDialog open={openLearn} onClose={() => setOpenLearn(false)} />
|
||||
<IconButton
|
||||
color='inherit'
|
||||
sx={{ position: 'fixed', right: '34px', color: 'text.secondary' }}
|
||||
onClick={() => setOpenLearn(true)}
|
||||
>
|
||||
<SchoolIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
color='inherit'
|
||||
sx={{ position: 'fixed', right: '0px', color: 'text.secondary' }}
|
||||
onClick={() =>
|
||||
setSettings({ ...settings, mode: settings.mode === 'dark' ? 'light' : 'dark' })
|
||||
}
|
||||
>
|
||||
{theme.palette.mode === 'dark' ? <LightModeIcon /> : <DarkModeIcon />}
|
||||
</IconButton>
|
||||
</div>
|
||||
<div className='appCenter'>
|
||||
<Switch>
|
||||
<Route
|
||||
|
@ -81,7 +81,6 @@ const MakerPage = ({
|
||||
<MakerForm
|
||||
limits={limits}
|
||||
fetchLimits={fetchLimits}
|
||||
pricingMethods={false}
|
||||
fav={fav}
|
||||
setFav={setFav}
|
||||
maker={maker}
|
||||
|
@ -1,102 +0,0 @@
|
||||
import React, { Suspense, useState } from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import Main from './Main';
|
||||
import { CssBaseline, IconButton } from '@mui/material';
|
||||
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||
import UnsafeAlert from './UnsafeAlert';
|
||||
import { LearnDialog } from './Dialogs';
|
||||
import TorConnection from './TorConnection';
|
||||
|
||||
import { I18nextProvider } from 'react-i18next';
|
||||
import i18n from '../i18n/Web';
|
||||
|
||||
// Icons
|
||||
import DarkModeIcon from '@mui/icons-material/DarkMode';
|
||||
import LightModeIcon from '@mui/icons-material/LightMode';
|
||||
import SchoolIcon from '@mui/icons-material/School';
|
||||
import { systemClient } from '../services/System';
|
||||
|
||||
const defaultTheme = createTheme({
|
||||
palette: {
|
||||
mode:
|
||||
window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
? 'dark'
|
||||
: 'light',
|
||||
background: {
|
||||
default:
|
||||
window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
? '#070707'
|
||||
: '#fff',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const App = (): JSX.Element => {
|
||||
const [openLearn, setOpenLearn] = useState<boolean>(false);
|
||||
const [theme, setTheme] = useState(defaultTheme);
|
||||
|
||||
const handleModeChange = function () {
|
||||
if (theme.palette.mode === 'light') {
|
||||
setTheme(
|
||||
createTheme({
|
||||
palette: {
|
||||
mode: 'dark',
|
||||
background: {
|
||||
default: '#070707',
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
} else if (theme.palette.mode === 'dark') {
|
||||
setTheme(
|
||||
createTheme({
|
||||
palette: {
|
||||
mode: 'light',
|
||||
background: {
|
||||
default: '#fff',
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Suspense fallback='loading language'>
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<LearnDialog open={openLearn} onClose={() => setOpenLearn(false)} />
|
||||
<TorConnection />
|
||||
<IconButton
|
||||
color='inherit'
|
||||
sx={{ position: 'fixed', right: '34px', color: 'text.secondary' }}
|
||||
onClick={() => setOpenLearn(true)}
|
||||
>
|
||||
<SchoolIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
color='inherit'
|
||||
sx={{ position: 'fixed', right: '0px', color: 'text.secondary' }}
|
||||
onClick={() => handleModeChange()}
|
||||
>
|
||||
{theme.palette.mode === 'dark' ? <LightModeIcon /> : <DarkModeIcon />}
|
||||
</IconButton>
|
||||
<UnsafeAlert className='unsafeAlert' />
|
||||
<Main />
|
||||
</ThemeProvider>
|
||||
</I18nextProvider>
|
||||
</Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
const loadApp = () => {
|
||||
if (systemClient.loading) {
|
||||
setTimeout(loadApp, 200);
|
||||
} else {
|
||||
const root = ReactDOM.createRoot(document.getElementById('app') ?? new HTMLElement());
|
||||
root.render(<App />);
|
||||
}
|
||||
};
|
||||
|
||||
loadApp();
|
@ -40,12 +40,13 @@ interface Props {
|
||||
maxHeight: number;
|
||||
fullWidth?: number;
|
||||
fullHeight?: number;
|
||||
defaultFullscreen: boolean;
|
||||
elevation: number;
|
||||
defaultFullscreen?: boolean;
|
||||
fillContainer?: boolean;
|
||||
showControls?: boolean;
|
||||
showFooter?: boolean;
|
||||
onCurrencyChange?: (e: any) => void;
|
||||
onTypeChange?: (mouseEvent: any, val: number) => void;
|
||||
noResultsOverlay?: () => JSX.Element;
|
||||
}
|
||||
|
||||
const BookTable = ({
|
||||
@ -57,11 +58,12 @@ const BookTable = ({
|
||||
fullWidth,
|
||||
fullHeight,
|
||||
defaultFullscreen = false,
|
||||
elevation = 6,
|
||||
fillContainer = false,
|
||||
showControls = true,
|
||||
showFooter = true,
|
||||
onCurrencyChange,
|
||||
onTypeChange,
|
||||
noResultsOverlay,
|
||||
}: Props): JSX.Element => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
@ -656,6 +658,37 @@ const BookTable = ({
|
||||
Toolbar?: JSX.Element;
|
||||
}
|
||||
|
||||
const NoResultsOverlay = function () {
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
direction='column'
|
||||
justifyContent='center'
|
||||
alignItems='center'
|
||||
sx={{ width: '100%', height: '100%' }}
|
||||
>
|
||||
<Grid item>
|
||||
<Typography align='center' component='h5' variant='h5'>
|
||||
{fav.type == 0
|
||||
? t('No orders found to sell BTC for {{currencyCode}}', {
|
||||
currencyCode:
|
||||
fav.currency == 0 ? t('ANY') : currencyDict[fav.currency.toString()],
|
||||
})
|
||||
: t('No orders found to buy BTC for {{currencyCode}}', {
|
||||
currencyCode:
|
||||
fav.currency == 0 ? t('ANY') : currencyDict[fav.currency.toString()],
|
||||
})}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography align='center' color='primary' variant='h6'>
|
||||
{t('Be the first one to create an order')}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
const Controls = function () {
|
||||
return (
|
||||
<BookControl
|
||||
@ -673,12 +706,10 @@ const BookTable = ({
|
||||
const gridComponents = function () {
|
||||
const components: GridComponentProps = {
|
||||
LoadingOverlay: LinearProgress,
|
||||
NoResultsOverlay: NoResultsOverlay,
|
||||
NoRowsOverlay: NoResultsOverlay,
|
||||
};
|
||||
|
||||
if (noResultsOverlay != null) {
|
||||
components.NoResultsOverlay = noResultsOverlay;
|
||||
components.NoRowsOverlay = noResultsOverlay;
|
||||
}
|
||||
if (showFooter) {
|
||||
components.Footer = Footer;
|
||||
}
|
||||
@ -690,7 +721,14 @@ const BookTable = ({
|
||||
|
||||
if (!fullscreen) {
|
||||
return (
|
||||
<Paper style={{ width: `${width}em`, height: `${height}em`, overflow: 'auto' }}>
|
||||
<Paper
|
||||
elevation={elevation}
|
||||
style={
|
||||
fillContainer
|
||||
? { width: '100%', height: '100%' }
|
||||
: { width: `${width}em`, height: `${height}em`, overflow: 'auto' }
|
||||
}
|
||||
>
|
||||
<DataGrid
|
||||
localeText={localeText}
|
||||
rows={
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { light } from '@mui/material/styles/createPalette';
|
||||
import { palette } from '@mui/system';
|
||||
import { Theme as NivoTheme } from '@nivo/core';
|
||||
import { Theme as MuiTheme } from './createTheme';
|
||||
import { Theme as MuiTheme } from '@mui/material/styles';
|
||||
|
||||
export const getNivoScheme: (theme: MuiTheme) => NivoTheme = (theme) => {
|
||||
const lightMode = {
|
||||
|
@ -1,198 +0,0 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { HashRouter, BrowserRouter, Switch, Route, useHistory } from 'react-router-dom';
|
||||
import { useTheme } from '@mui/material';
|
||||
|
||||
import UserGenPage from './UserGenPage';
|
||||
import MakerPage from './MakerPage';
|
||||
import BookPage from './BookPage';
|
||||
import OrderPage from './OrderPage';
|
||||
import BottomBar from './BottomBar';
|
||||
|
||||
import { apiClient } from '../services/api';
|
||||
import checkVer from '../utils/checkVer';
|
||||
|
||||
import {
|
||||
Book,
|
||||
LimitList,
|
||||
Maker,
|
||||
Robot,
|
||||
Info,
|
||||
Settings,
|
||||
Favorites,
|
||||
defaultMaker,
|
||||
defaultRobot,
|
||||
defaultInfo,
|
||||
defaultSettings,
|
||||
} from '../models';
|
||||
|
||||
const getWindowSize = function (fontSize: number) {
|
||||
// returns window size in EM units
|
||||
return {
|
||||
width: window.innerWidth / fontSize,
|
||||
height: window.innerHeight / fontSize,
|
||||
};
|
||||
};
|
||||
|
||||
const Main = (): JSX.Element => {
|
||||
const theme = useTheme();
|
||||
const history = useHistory();
|
||||
const Router = window.NativeRobosats != null ? HashRouter : BrowserRouter;
|
||||
const basename = window.NativeRobosats != null ? window.location.pathname : '';
|
||||
|
||||
// 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>(defaultRobot);
|
||||
const [maker, setMaker] = useState<Maker>(defaultMaker);
|
||||
const [info, setInfo] = useState<Info>(defaultInfo);
|
||||
const [fav, setFav] = useState<Favorites>({ type: null, currency: 0 });
|
||||
const [settings, setSettings] = useState<Settings>(defaultSettings);
|
||||
|
||||
const [windowSize, setWindowSize] = useState<{ width: number; height: number }>(
|
||||
getWindowSize(theme.typography.fontSize),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== undefined) {
|
||||
window.addEventListener('resize', onResize);
|
||||
}
|
||||
fetchBook();
|
||||
fetchLimits();
|
||||
fetchInfo();
|
||||
return () => {
|
||||
if (typeof window !== undefined) {
|
||||
window.removeEventListener('resize', onResize);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const onResize = function () {
|
||||
setWindowSize(getWindowSize(theme.typography.fontSize));
|
||||
};
|
||||
|
||||
const fetchBook = function () {
|
||||
setBook({ ...book, loading: true });
|
||||
apiClient.get('/api/book/').then((data: any) =>
|
||||
setBook({
|
||||
loading: false,
|
||||
orders: data.not_found ? [] : data,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const fetchLimits = async () => {
|
||||
setLimits({ ...limits, loading: true });
|
||||
const data = apiClient.get('/api/limits/').then((data) => {
|
||||
setLimits({ list: data ?? [], loading: false });
|
||||
return data;
|
||||
});
|
||||
return await data;
|
||||
};
|
||||
|
||||
const fetchInfo = function () {
|
||||
apiClient.get('/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,
|
||||
});
|
||||
setRobot({
|
||||
...robot,
|
||||
nickname: data.nickname,
|
||||
loading: false,
|
||||
activeOrderId: data.active_order_id ?? null,
|
||||
lastOrderId: data.last_order_id ?? null,
|
||||
referralCode: data.referral_code,
|
||||
tgEnabled: data.tg_enabled,
|
||||
tgBotName: data.tg_bot_name,
|
||||
tgToken: data.tg_token,
|
||||
earnedRewards: data.earned_rewards ?? 0,
|
||||
stealthInvoices: data.wants_stealth,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
console.log(robot);
|
||||
return (
|
||||
<Router basename={basename}>
|
||||
<div className='appCenter'>
|
||||
<Switch>
|
||||
<Route
|
||||
exact
|
||||
path='/'
|
||||
render={(props: any) => (
|
||||
<UserGenPage match={props.match} theme={theme} robot={robot} setRobot={setRobot} />
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path='/ref/:refCode'
|
||||
render={(props: any) => (
|
||||
<UserGenPage match={props.match} theme={theme} robot={robot} setRobot={setRobot} />
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path='/make'
|
||||
render={() => (
|
||||
<MakerPage
|
||||
orders={book.orders}
|
||||
limits={limits}
|
||||
fetchLimits={fetchLimits}
|
||||
maker={maker}
|
||||
setMaker={setMaker}
|
||||
fav={fav}
|
||||
setFav={setFav}
|
||||
windowSize={windowSize}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path='/book'
|
||||
render={() => (
|
||||
<BookPage
|
||||
book={book}
|
||||
fetchBook={fetchBook}
|
||||
limits={limits}
|
||||
fetchLimits={fetchLimits}
|
||||
fav={fav}
|
||||
setFav={setFav}
|
||||
maker={maker}
|
||||
setMaker={setMaker}
|
||||
lastDayPremium={info.last_day_nonkyc_btc_premium}
|
||||
windowSize={windowSize}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path='/order/:orderId'
|
||||
render={(props: any) => <OrderPage theme={theme} history={history} {...props} />}
|
||||
/>
|
||||
</Switch>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
height: '2.5em',
|
||||
position: 'fixed',
|
||||
bottom: 0,
|
||||
}}
|
||||
>
|
||||
<BottomBar
|
||||
theme={theme}
|
||||
windowSize={windowSize}
|
||||
redirectTo={(location: string) => history.push(location)}
|
||||
robot={robot}
|
||||
setRobot={setRobot}
|
||||
info={info}
|
||||
setInfo={setInfo}
|
||||
fetchInfo={fetchInfo}
|
||||
/>
|
||||
</div>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
export default Main;
|
@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAutocomplete } from '@mui/base/AutocompleteUnstyled';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { Button, Fade, Tooltip, Typography, Grow } from '@mui/material';
|
||||
import { Button, Fade, Tooltip, Typography, Grow, useTheme } from '@mui/material';
|
||||
import { fiatMethods, swapMethods, PaymentIcon } from '../PaymentMethods';
|
||||
|
||||
// Icons
|
||||
@ -77,9 +77,9 @@ const InputWrapper = styled('div')(
|
||||
& input {
|
||||
background-color: ${theme.palette.mode === 'dark' ? '#141414' : '#fff'};
|
||||
color: ${theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.65)' : 'rgba(0,0,0,.85)'};
|
||||
height: 2.15em;
|
||||
height: 2em;
|
||||
box-sizing: border-box;
|
||||
padding: 4px 6px;
|
||||
padding: 0.28em 0.4em;
|
||||
width: 0;
|
||||
min-width: 2.15em;
|
||||
font-size: ${theme.typography.fontSize * 1.0714};
|
||||
@ -93,11 +93,13 @@ const InputWrapper = styled('div')(
|
||||
);
|
||||
|
||||
function Tag(props) {
|
||||
const theme = useTheme();
|
||||
const { label, icon, onDelete, ...other } = props;
|
||||
const iconSize = 1.5 * theme.typography.fontSize;
|
||||
return (
|
||||
<div {...other}>
|
||||
<div style={{ position: 'relative', left: '-5px', top: '4px' }}>
|
||||
<PaymentIcon width={22} height={22} icon={icon} />
|
||||
<div style={{ position: 'relative', left: '-5px', top: '0.28em' }}>
|
||||
<PaymentIcon width={iconSize} height={iconSize} icon={icon} />
|
||||
</div>
|
||||
<span style={{ position: 'relative', left: '2px' }}>{label}</span>
|
||||
<CloseIcon onClick={onDelete} />
|
||||
@ -122,7 +124,7 @@ const StyledTag = styled(Tag)(
|
||||
border: 1px solid ${theme.palette.mode === 'dark' ? '#303030' : '#e8e8e8'};
|
||||
border-radius: 2px;
|
||||
box-sizing: content-box;
|
||||
padding: 0 4px 0 10px;
|
||||
padding: 0 0.28em 0 0.65em;
|
||||
outline: 0;
|
||||
overflow: hidden;
|
||||
|
||||
@ -141,7 +143,7 @@ const StyledTag = styled(Tag)(
|
||||
& svg {
|
||||
font-size: 0.857em;
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
padding: 0.28em;
|
||||
}
|
||||
`,
|
||||
);
|
||||
@ -150,8 +152,8 @@ const ListHeader = styled('span')(
|
||||
({ theme }) => `
|
||||
color: ${theme.palette.mode === 'dark' ? '#90caf9' : '#1976d2'};
|
||||
align: left;
|
||||
line-height:10px;
|
||||
max-height: 10px;
|
||||
line-height:0.7em;
|
||||
max-height: 10.7em;
|
||||
display: inline-block;
|
||||
background-color: ${theme.palette.mode === 'dark' ? '#141414' : '#ffffff'};
|
||||
font-size: 0.875em;
|
||||
@ -234,6 +236,8 @@ export default function AutocompletePayments(props) {
|
||||
|
||||
const [val, setVal] = useState('');
|
||||
const fewerOptions = groupedOptions.length > 8 ? groupedOptions.slice(0, 8) : groupedOptions;
|
||||
const theme = useTheme();
|
||||
const iconSize = 1.5 * theme.typography.fontSize;
|
||||
|
||||
function handleAddNew(inputProps) {
|
||||
fiatMethods.push({ name: inputProps.value, icon: 'custom' });
|
||||
@ -319,7 +323,7 @@ export default function AutocompletePayments(props) {
|
||||
style={{ justifyContent: 'flex-start' }}
|
||||
>
|
||||
<div style={{ padding: '0.286em', position: 'relative', top: '0.35em' }}>
|
||||
<PaymentIcon width={22} height={22} icon={option.icon} />
|
||||
<PaymentIcon width={iconSize} height={iconSize} icon={option.icon} />
|
||||
</div>
|
||||
<Typography variant='inherit' align='left'>
|
||||
{t(option.name)}
|
||||
|
@ -45,7 +45,7 @@ import { LoadingButton } from '@mui/lab';
|
||||
interface MakerFormProps {
|
||||
limits: { list: LimitList; loading: boolean };
|
||||
fetchLimits: () => void;
|
||||
pricingMethods: boolean;
|
||||
pricingMethods?: boolean;
|
||||
maker: Maker;
|
||||
fav: Favorites;
|
||||
setFav: (state: Favorites) => void;
|
||||
@ -60,7 +60,7 @@ interface MakerFormProps {
|
||||
const MakerForm = ({
|
||||
limits,
|
||||
fetchLimits,
|
||||
pricingMethods,
|
||||
pricingMethods = false,
|
||||
fav,
|
||||
setFav,
|
||||
maker,
|
||||
|
7
frontend/src/models/Settings.default.basic.ts
Normal file
7
frontend/src/models/Settings.default.basic.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { baseSettings, Settings } from './Settings.model';
|
||||
|
||||
export const defaultSettings: Settings = {
|
||||
...baseSettings,
|
||||
};
|
||||
|
||||
export default defaultSettings;
|
8
frontend/src/models/Settings.default.pro.ts
Normal file
8
frontend/src/models/Settings.default.pro.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { baseSettings, Settings } from './Settings.model';
|
||||
|
||||
export const defaultSettings: Settings = {
|
||||
...baseSettings,
|
||||
fontSize: 12,
|
||||
};
|
||||
|
||||
export default defaultSettings;
|
@ -1,5 +1,34 @@
|
||||
export interface Settings {}
|
||||
export interface Settings {
|
||||
mode: 'light' | 'dark';
|
||||
fontSize: number;
|
||||
language:
|
||||
| 'en'
|
||||
| 'es'
|
||||
| 'ru'
|
||||
| 'de'
|
||||
| 'pl'
|
||||
| 'fr'
|
||||
| 'ca'
|
||||
| 'it'
|
||||
| 'pt'
|
||||
| 'eu'
|
||||
| 'cs'
|
||||
| 'th'
|
||||
| 'pl'
|
||||
| 'sv'
|
||||
| 'zh-SI'
|
||||
| 'zh-TR';
|
||||
freezeViewports: boolean;
|
||||
}
|
||||
|
||||
export const defaultSettings: Settings = {};
|
||||
export const baseSettings: Settings = {
|
||||
mode:
|
||||
window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
? 'dark'
|
||||
: 'light',
|
||||
fontSize: 14,
|
||||
language: 'en',
|
||||
freezeViewports: false,
|
||||
};
|
||||
|
||||
export default Settings;
|
||||
|
@ -10,5 +10,5 @@ export type { Favorites } from './Favorites.model';
|
||||
|
||||
export { defaultMaker } from './Maker.model';
|
||||
export { defaultRobot } from './Robot.model';
|
||||
export { defaultSettings } from './Settings.model';
|
||||
export { defaultSettings } from './Settings.default.basic';
|
||||
export { defaultInfo } from './Info.model';
|
||||
|
53
frontend/src/pro/LandingDialog/index.tsx
Normal file
53
frontend/src/pro/LandingDialog/index.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Dialog, DialogTitle, DialogContent, Grid, Box } from '@mui/material';
|
||||
import { useTheme } from '@mui/system';
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const LandingDialog = ({ open, onClose }: Props): JSX.Element => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Dialog fullWidth maxWidth={'md'} open={open} onClose={onClose}>
|
||||
<DialogTitle>{t('Oh... a robot technician has arrived...')}</DialogTitle>
|
||||
|
||||
<DialogContent sx={{ height: '30em' }}>
|
||||
<Grid container sx={{ width: '100%', height: '100%' }}>
|
||||
<Grid item xs={6} sx={{ padding: '1em', width: '100%', height: '100%' }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
justifyContent: 'center',
|
||||
alignContent: 'center',
|
||||
}}
|
||||
>
|
||||
{t('Indeed, but it is my first time. Generate a new workspace and extended token.')}
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={6} sx={{ padding: '1em', width: '100%', height: '100%' }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
justifyContent: 'center',
|
||||
alignContent: 'center',
|
||||
}}
|
||||
>
|
||||
{t('Yup, here are my robots. Drag and drop workspace.json')}
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default LandingDialog;
|
@ -1,9 +1,8 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { HashRouter, BrowserRouter, Switch, Route, useHistory } from 'react-router-dom';
|
||||
import { useTheme } from '@mui/material';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import GridLayout, { Layout } from 'react-grid-layout';
|
||||
import { Grid, useTheme } from '@mui/material';
|
||||
|
||||
import { apiClient } from '../services/api';
|
||||
import checkVer from '../utils/checkVer';
|
||||
|
||||
import {
|
||||
Book,
|
||||
@ -16,9 +15,12 @@ import {
|
||||
defaultMaker,
|
||||
defaultRobot,
|
||||
defaultInfo,
|
||||
defaultSettings,
|
||||
} from '../models';
|
||||
|
||||
import { PlaceholderWidget, MakerWidget, BookWidget } from '../pro/Widgets';
|
||||
import ToolBar from '../pro/ToolBar';
|
||||
import LandingDialog from '../pro/LandingDialog';
|
||||
|
||||
const getWindowSize = function (fontSize: number) {
|
||||
// returns window size in EM units
|
||||
return {
|
||||
@ -27,8 +29,138 @@ const getWindowSize = function (fontSize: number) {
|
||||
};
|
||||
};
|
||||
|
||||
const Main = (): JSX.Element => {
|
||||
return <span> "Robosats PRO" </span>;
|
||||
interface MainProps {
|
||||
settings: Settings;
|
||||
setSettings: (state: Settings) => void;
|
||||
}
|
||||
|
||||
const Main = ({ settings, setSettings }: MainProps): JSX.Element => {
|
||||
const theme = useTheme();
|
||||
|
||||
const defaultLayout: Layout = [
|
||||
{ i: 'MakerWidget', w: 6, h: 13, x: 42, y: 0, minW: 6, maxW: 12, minH: 9, maxH: 18 },
|
||||
{ i: 'BookWidget', w: 27, h: 13, x: 21, y: 13, minW: 6, maxW: 40, minH: 9, maxH: 15 },
|
||||
{ i: 'robots', w: 33, h: 13, x: 0, y: 0, minW: 15, maxW: 48, minH: 8, maxH: 20 },
|
||||
{ i: 'history', w: 7, h: 9, x: 6, y: 13, minW: 6, maxW: 12, minH: 9, maxH: 15 },
|
||||
{ i: 'trade', w: 9, h: 13, x: 33, y: 0, minW: 6, maxW: 12, minH: 9, maxH: 15 },
|
||||
{ i: 'depth', w: 8, h: 9, x: 13, y: 13, minW: 6, maxW: 12, minH: 9, maxH: 15 },
|
||||
{ i: 'settings', w: 6, h: 13, x: 0, y: 13, minW: 6, maxW: 12, minH: 9, maxH: 15 },
|
||||
{ i: 'other', w: 15, h: 4, x: 6, y: 22, minW: 2, maxW: 30, minH: 4, maxH: 15 },
|
||||
];
|
||||
|
||||
// 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>(defaultRobot);
|
||||
const [maker, setMaker] = useState<Maker>(defaultMaker);
|
||||
const [info, setInfo] = useState<Info>(defaultInfo);
|
||||
const [fav, setFav] = useState<Favorites>({ type: null, currency: 0 });
|
||||
const [layout, setLayout] = useState<Layout>(defaultLayout);
|
||||
|
||||
const [openLanding, setOpenLanding] = useState<boolean>(true);
|
||||
const [windowSize, setWindowSize] = useState<{ width: number; height: number }>(
|
||||
getWindowSize(theme.typography.fontSize),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== undefined) {
|
||||
window.addEventListener('resize', onResize);
|
||||
}
|
||||
fetchLimits();
|
||||
fetchBook();
|
||||
return () => {
|
||||
if (typeof window !== undefined) {
|
||||
window.removeEventListener('resize', onResize);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const onResize = function () {
|
||||
setWindowSize(getWindowSize(theme.typography.fontSize));
|
||||
};
|
||||
|
||||
const fetchLimits = async () => {
|
||||
setLimits({ ...limits, loading: true });
|
||||
const data = apiClient.get('/api/limits/').then((data) => {
|
||||
setLimits({ list: data ?? [], loading: false });
|
||||
return data;
|
||||
});
|
||||
return await data;
|
||||
};
|
||||
|
||||
const fetchBook = function () {
|
||||
setBook({ ...book, loading: true });
|
||||
apiClient.get('/api/book/').then((data: any) =>
|
||||
setBook({
|
||||
loading: false,
|
||||
orders: data.not_found ? [] : data,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const bookRef = useRef<HTMLInputElement>(null);
|
||||
console.log(bookRef);
|
||||
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
direction='column'
|
||||
sx={{ width: `${windowSize.width * theme.typography.fontSize}px` }}
|
||||
>
|
||||
<Grid item>
|
||||
<ToolBar settings={settings} setSettings={setSettings} />
|
||||
<LandingDialog open={openLanding} onClose={() => setOpenLanding(!openLanding)} />
|
||||
</Grid>
|
||||
|
||||
<Grid item sx={{ height: `${(windowSize.height / 16) * 14 - 3}em` }}>
|
||||
<GridLayout
|
||||
className='layout'
|
||||
layout={layout}
|
||||
cols={48}
|
||||
margin={[theme.typography.fontSize / 2, theme.typography.fontSize / 2]}
|
||||
isDraggable={!settings.freezeViewports}
|
||||
isResizable={!settings.freezeViewports}
|
||||
rowHeight={theme.typography.fontSize * 2.4}
|
||||
width={windowSize.width * theme.typography.fontSize}
|
||||
autoSize={true}
|
||||
onLayoutChange={(layout: Layout) => setLayout(layout)}
|
||||
>
|
||||
<div key='MakerWidget'>
|
||||
<MakerWidget
|
||||
ref={bookRef}
|
||||
limits={limits}
|
||||
fetchLimits={fetchLimits}
|
||||
fav={fav}
|
||||
setFav={setFav}
|
||||
maker={maker}
|
||||
setMaker={setMaker}
|
||||
/>
|
||||
</div>
|
||||
<div key='BookWidget'>
|
||||
<BookWidget
|
||||
book={book}
|
||||
layoutBook={layout[1]}
|
||||
fetchBook={fetchBook}
|
||||
fav={fav}
|
||||
setFav={setFav}
|
||||
windowSize={windowSize}
|
||||
/>
|
||||
</div>
|
||||
<PlaceholderWidget key='robots'>Robot Table</PlaceholderWidget>
|
||||
<PlaceholderWidget key='history'>Workspace History</PlaceholderWidget>
|
||||
<PlaceholderWidget key='trade'>
|
||||
Trade Box (for selected order in Robot Table)
|
||||
</PlaceholderWidget>
|
||||
<PlaceholderWidget key='depth'>Depth Chart</PlaceholderWidget>
|
||||
<PlaceholderWidget key='settings'>Settings</PlaceholderWidget>
|
||||
<PlaceholderWidget key='other'>Other</PlaceholderWidget>
|
||||
</GridLayout>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default Main;
|
||||
|
51
frontend/src/pro/ToolBar/index.tsx
Normal file
51
frontend/src/pro/ToolBar/index.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import React from 'react';
|
||||
import { Paper, Grid, IconButton, Tooltip } from '@mui/material';
|
||||
import { Lock, LockOpen } from '@mui/icons-material';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Settings } from '../../models';
|
||||
|
||||
interface ToolBarProps {
|
||||
settings: Settings;
|
||||
setSettings: (state: Settings) => void;
|
||||
}
|
||||
|
||||
const ToolBar = ({ settings, setSettings }: ToolBarProps): JSX.Element => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Paper
|
||||
elevation={12}
|
||||
sx={{
|
||||
width: `100%`,
|
||||
height: '3em',
|
||||
textAlign: 'center',
|
||||
padding: '1em',
|
||||
borderRadius: 0,
|
||||
}}
|
||||
>
|
||||
<Grid container>
|
||||
<Grid item>ToolBar Goes here!</Grid>
|
||||
<Grid item>
|
||||
<Tooltip
|
||||
title={settings.freezeViewports ? t('Customize viewports') : t('Freeze viewports')}
|
||||
placement='bottom'
|
||||
enterTouchDelay={500}
|
||||
enterDelay={700}
|
||||
enterNextDelay={2000}
|
||||
>
|
||||
<IconButton
|
||||
onClick={() =>
|
||||
setSettings({ ...settings, freezeViewports: !settings.freezeViewports })
|
||||
}
|
||||
sx={{ position: 'fixed', right: '1em', top: '0em', color: 'text.secondary' }}
|
||||
>
|
||||
{settings.freezeViewports ? <Lock color='primary' /> : <LockOpen color='secondary' />}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
|
||||
export default ToolBar;
|
@ -0,0 +1,64 @@
|
||||
import React, { Ref } from 'react';
|
||||
|
||||
import MakerForm from '../../components/MakerForm';
|
||||
import { Book, Favorites } from '../../models';
|
||||
import { Paper, useTheme } from '@mui/material';
|
||||
import BookTable from '../../components/BookTable';
|
||||
|
||||
interface BookWidgetProps {
|
||||
layoutBook: any;
|
||||
book: Book;
|
||||
fetchBook: () => void;
|
||||
fav: Favorites;
|
||||
setFav: (state: Favorites) => void;
|
||||
windowSize: { width: number; height: number };
|
||||
style?: Object;
|
||||
className?: string;
|
||||
onMouseDown?: () => void;
|
||||
onMouseUp?: () => void;
|
||||
onTouchEnd?: () => void;
|
||||
}
|
||||
|
||||
const BookWidget = React.forwardRef(
|
||||
(
|
||||
{
|
||||
layoutBook,
|
||||
book,
|
||||
fetchBook,
|
||||
fav,
|
||||
setFav,
|
||||
windowSize,
|
||||
style,
|
||||
className,
|
||||
onMouseDown,
|
||||
onMouseUp,
|
||||
onTouchEnd,
|
||||
}: BookWidgetProps,
|
||||
ref,
|
||||
) => {
|
||||
console.log(layoutBook);
|
||||
const theme = useTheme();
|
||||
return React.useMemo(() => {
|
||||
return (
|
||||
<Paper elevation={6} style={{ width: '100%', height: '100%' }}>
|
||||
<BookTable
|
||||
elevation={0}
|
||||
clickRefresh={() => fetchBook()}
|
||||
book={book}
|
||||
fav={fav}
|
||||
fillContainer={true}
|
||||
maxWidth={(windowSize.width / 48) * layoutBook.w} // EM units
|
||||
maxHeight={(layoutBook.h * 30) / theme.typography.fontSize} // EM units
|
||||
fullWidth={windowSize.width} // EM units
|
||||
fullHeight={windowSize.height} // EM units
|
||||
defaultFullscreen={false}
|
||||
onCurrencyChange={(e) => setFav({ ...fav, currency: e.target.value })}
|
||||
onTypeChange={(mouseEvent, val) => setFav({ ...fav, type: val })}
|
||||
/>
|
||||
</Paper>
|
||||
);
|
||||
}, [book, layoutBook, windowSize, fav]);
|
||||
},
|
||||
);
|
||||
|
||||
export default BookWidget;
|
@ -0,0 +1,58 @@
|
||||
import React from 'react';
|
||||
|
||||
import MakerForm from '../../components/MakerForm';
|
||||
import { LimitList, Maker, Favorites } from '../../models';
|
||||
import { Paper } from '@mui/material';
|
||||
|
||||
interface MakerWidgetProps {
|
||||
limits: { list: LimitList; loading: boolean };
|
||||
fetchLimits: () => void;
|
||||
fav: Favorites;
|
||||
maker: Maker;
|
||||
setFav: (state: Favorites) => void;
|
||||
setMaker: (state: Maker) => void;
|
||||
style?: Object;
|
||||
className?: string;
|
||||
onMouseDown?: () => void;
|
||||
onMouseUp?: () => void;
|
||||
onTouchEnd?: () => void;
|
||||
}
|
||||
|
||||
const MakerWidget = React.forwardRef(
|
||||
(
|
||||
{
|
||||
maker,
|
||||
setMaker,
|
||||
limits,
|
||||
fetchLimits,
|
||||
fav,
|
||||
setFav,
|
||||
style,
|
||||
className,
|
||||
onMouseDown,
|
||||
onMouseUp,
|
||||
onTouchEnd,
|
||||
}: MakerWidgetProps,
|
||||
ref,
|
||||
) => {
|
||||
return React.useMemo(() => {
|
||||
return (
|
||||
<Paper
|
||||
elevation={6}
|
||||
style={{ padding: 8, overflow: 'auto', width: '100%', height: '100%' }}
|
||||
>
|
||||
<MakerForm
|
||||
limits={limits}
|
||||
fetchLimits={fetchLimits}
|
||||
maker={maker}
|
||||
setMaker={setMaker}
|
||||
fav={fav}
|
||||
setFav={setFav}
|
||||
/>
|
||||
</Paper>
|
||||
);
|
||||
}, [maker, limits, fav]);
|
||||
},
|
||||
);
|
||||
|
||||
export default MakerWidget;
|
17
frontend/src/pro/Widgets/Placeholder.tsx
Normal file
17
frontend/src/pro/Widgets/Placeholder.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
|
||||
import { styled } from '@mui/material/styles';
|
||||
|
||||
const PlaceholderWidget = styled('div')(
|
||||
({ theme }) => `
|
||||
background-color: rgb(128,128,128,0.3);
|
||||
color: ${theme.palette.text.primary};
|
||||
font-size: ${theme.typography.fontSize};
|
||||
text-align: center;
|
||||
vertical-align: center;
|
||||
padding: 1em;
|
||||
border-radius: 0.3em;
|
||||
`,
|
||||
);
|
||||
|
||||
export default PlaceholderWidget;
|
@ -0,0 +1,3 @@
|
||||
export { default as MakerWidget } from './Maker';
|
||||
export { default as BookWidget } from './Book';
|
||||
export { default as PlaceholderWidget } from './Placeholder';
|
138
frontend/static/css_pro/react-grid-layout.css
Normal file
138
frontend/static/css_pro/react-grid-layout.css
Normal file
@ -0,0 +1,138 @@
|
||||
.react-grid-layout {
|
||||
position: relative;
|
||||
transition: height 200ms ease;
|
||||
}
|
||||
.react-grid-item {
|
||||
transition: all 200ms ease;
|
||||
transition-property: left, top;
|
||||
}
|
||||
.react-grid-item img {
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
.react-grid-item.cssTransforms {
|
||||
transition-property: transform;
|
||||
}
|
||||
.react-grid-item.resizing {
|
||||
z-index: 1;
|
||||
will-change: width, height;
|
||||
}
|
||||
|
||||
.react-grid-item.react-draggable-dragging {
|
||||
transition: none;
|
||||
z-index: 3;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.react-grid-item.dropping {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.react-grid-item.react-grid-placeholder {
|
||||
background: #1976d2;
|
||||
opacity: 0.2;
|
||||
transition-duration: 100ms;
|
||||
z-index: 2;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
-o-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.react-grid-item > .react-resizable-handle {
|
||||
position: absolute;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
.react-grid-item > .react-resizable-handle::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0.3em;
|
||||
bottom: 0.3em;
|
||||
width: 0.8em;
|
||||
height: 0.8em;
|
||||
border-right: 2px solid #1976d2;
|
||||
border-bottom: 2px solid #1976d2;
|
||||
}
|
||||
|
||||
.react-resizable-hide > .react-resizable-handle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.react-grid-item > .react-resizable-handle.react-resizable-handle-sw {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
cursor: sw-resize;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
.react-grid-item > .react-resizable-handle.react-resizable-handle-se {
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
cursor: se-resize;
|
||||
}
|
||||
.react-grid-item > .react-resizable-handle.react-resizable-handle-nw {
|
||||
top: 0;
|
||||
left: 0;
|
||||
cursor: nw-resize;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
.react-grid-item > .react-resizable-handle.react-resizable-handle-ne {
|
||||
top: 0;
|
||||
right: 0;
|
||||
cursor: ne-resize;
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
.react-grid-item > .react-resizable-handle.react-resizable-handle-w,
|
||||
.react-grid-item > .react-resizable-handle.react-resizable-handle-e {
|
||||
top: 50%;
|
||||
margin-top: -10px;
|
||||
cursor: ew-resize;
|
||||
}
|
||||
.react-grid-item > .react-resizable-handle.react-resizable-handle-w {
|
||||
left: 0;
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
.react-grid-item > .react-resizable-handle.react-resizable-handle-e {
|
||||
right: 0;
|
||||
transform: rotate(315deg);
|
||||
}
|
||||
.react-grid-item > .react-resizable-handle.react-resizable-handle-n,
|
||||
.react-grid-item > .react-resizable-handle.react-resizable-handle-s {
|
||||
left: 50%;
|
||||
margin-left: -10px;
|
||||
cursor: ns-resize;
|
||||
}
|
||||
.react-grid-item > .react-resizable-handle.react-resizable-handle-n {
|
||||
top: 0;
|
||||
transform: rotate(225deg);
|
||||
}
|
||||
.react-grid-item > .react-resizable-handle.react-resizable-handle-s {
|
||||
bottom: 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.react-grid-item.react-grid-placeholder {
|
||||
background: #90caf9;
|
||||
opacity: 0.4;
|
||||
transition-duration: 100ms;
|
||||
z-index: 2;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
-o-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.react-grid-item > .react-resizable-handle::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0.3em;
|
||||
bottom: 0.3em;
|
||||
width: 0.8em;
|
||||
height: 0.8em;
|
||||
border-right: 2px solid #90caf9;
|
||||
border-bottom: 2px solid #90caf9;
|
||||
}
|
||||
}
|
65
frontend/static/css_pro/react-resizable.css
Normal file
65
frontend/static/css_pro/react-resizable.css
Normal file
@ -0,0 +1,65 @@
|
||||
/* .react-resizable {
|
||||
position: relative;
|
||||
}
|
||||
.react-resizable-handle {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-repeat: no-repeat;
|
||||
background-origin: content-box;
|
||||
box-sizing: border-box;
|
||||
background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2IDYiIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiNmZmZmZmYwMCIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iNnB4Ij48ZyBvcGFjaXR5PSIwLjMwMiI+PHBhdGggZD0iTSA2IDYgTCAwIDYgTCAwIDQuMiBMIDQgNC4yIEwgNC4yIDQuMiBMIDQuMiAwIEwgNiAwIEwgNiA2IEwgNiA2IFoiIGZpbGw9IiMwMDAwMDAiLz48L2c+PC9zdmc+');
|
||||
background-position: bottom right;
|
||||
padding: 0 3px 3px 0;
|
||||
}
|
||||
.react-resizable-handle-sw {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
cursor: sw-resize;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
.react-resizable-handle-se {
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
cursor: se-resize;
|
||||
}
|
||||
.react-resizable-handle-nw {
|
||||
top: 0;
|
||||
left: 0;
|
||||
cursor: nw-resize;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
.react-resizable-handle-ne {
|
||||
top: 0;
|
||||
right: 0;
|
||||
cursor: ne-resize;
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
.react-resizable-handle-w,
|
||||
.react-resizable-handle-e {
|
||||
top: 50%;
|
||||
margin-top: -10px;
|
||||
cursor: ew-resize;
|
||||
}
|
||||
.react-resizable-handle-w {
|
||||
left: 0;
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
.react-resizable-handle-e {
|
||||
right: 0;
|
||||
transform: rotate(315deg);
|
||||
}
|
||||
.react-resizable-handle-n,
|
||||
.react-resizable-handle-s {
|
||||
left: 50%;
|
||||
margin-left: -10px;
|
||||
cursor: ns-resize;
|
||||
}
|
||||
.react-resizable-handle-n {
|
||||
top: 0;
|
||||
transform: rotate(225deg);
|
||||
}
|
||||
.react-resizable-handle-s {
|
||||
bottom: 0;
|
||||
transform: rotate(45deg);
|
||||
} */
|
@ -55,6 +55,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="{% static "frontend/basic.js" %}"></script>
|
||||
<script src="{% static "frontend/main.js" %}"></script>
|
||||
</body>
|
||||
</html>
|
@ -16,6 +16,8 @@
|
||||
<link rel="stylesheet" href="{% static "css/fonts.css" %}"/>
|
||||
<link rel="stylesheet" type="text/css" href="{% static "css/loader.css" %}"/>
|
||||
<link rel="stylesheet" type="text/css" href="{% static "css/index.css" %}"/>
|
||||
<link rel="stylesheet" type="text/css" href="{% static "css_pro/react-grid-layout.css" %}"/>
|
||||
<link rel="stylesheet" type="text/css" href="{% static "css_pro/react-resizable.css" %}"/>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
|
@ -27,7 +27,7 @@ const configWeb: Configuration = {
|
||||
...config,
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'static/frontend'),
|
||||
filename: 'basic.js',
|
||||
filename: 'main.js',
|
||||
},
|
||||
};
|
||||
|
||||
@ -79,6 +79,15 @@ const configWebPro: Configuration = {
|
||||
async: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: path.resolve(__dirname, 'src/models/Settings.default.basic.ts'),
|
||||
loader: 'file-replace-loader',
|
||||
options: {
|
||||
condition: 'if-replacement-exists',
|
||||
replacement: path.resolve(__dirname, 'src/models/Settings.default.pro.ts'),
|
||||
async: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
output: {
|
||||
|
Loading…
Reference in New Issue
Block a user