Add book double view for large displays. Prettify.

This commit is contained in:
Reckless_Satoshi 2022-09-28 06:49:07 -07:00
parent 7b8fcb3285
commit 344ba2ab67
No known key found for this signature in database
GPG Key ID: 9C4585B561315571
14 changed files with 221 additions and 161 deletions

View File

@ -107,43 +107,90 @@ class BookPage extends Component {
); );
}; };
mainView = () => { mainView = (doubleView, widthEm, heightEm) => {
if (this.props.bookNotFound) { if (this.props.bookNotFound) {
return this.NoOrdersFound(); return this.NoOrdersFound();
} }
if (this.state.view === 'depth') { if (doubleView) {
const width = widthEm * 0.9;
const bookTableWidth = 85;
const chartWidthEm = width - bookTableWidth;
const tableWidthXS = (bookTableWidth / width) * 12;
const chartWidthXS = (chartWidthEm / width) * 12;
console.log(bookTableWidth, chartWidthEm, tableWidthXS, chartWidthXS);
return ( return (
<DepthChart <Grid
bookLoading={this.props.bookLoading} container
orders={this.props.bookOrders} alignItems='center'
lastDayPremium={this.props.lastDayPremium} justifyContent='flex-start'
currency={this.props.currency} spacing={1}
compact={true} direction='row'
setAppState={this.props.setAppState} style={{ width: `${widthEm}em`, position: 'relative', left: `${widthEm / 140}em` }}
limits={this.props.limits} >
maxWidth={(this.props.windowWidth / this.props.theme.typography.fontSize) * 0.8} // EM units <Grid item xs={tableWidthXS} style={{ width: `${bookTableWidth}em` }}>
maxHeight={(this.props.windowHeight / this.props.theme.typography.fontSize) * 0.8 - 11} // EM units <BookTable
/> loading={this.props.bookLoading}
orders={this.props.bookOrders}
type={this.props.type}
currency={this.props.currency}
maxWidth={bookTableWidth} // EM units
maxHeight={heightEm * 0.8 - 11} // EM units
/>
</Grid>
<Grid
item
xs={chartWidthXS}
style={{ width: `${chartWidthEm}em`, position: 'relative', left: '-10em' }}
>
<DepthChart
bookLoading={this.props.bookLoading}
orders={this.props.bookOrders}
lastDayPremium={this.props.lastDayPremium}
currency={this.props.currency}
compact={true}
setAppState={this.props.setAppState}
limits={this.props.limits}
maxWidth={chartWidthEm} // EM units
maxHeight={heightEm * 0.8 - 11} // EM units
/>
</Grid>
</Grid>
); );
} else { } else {
return ( if (this.state.view === 'depth') {
<BookTable return (
loading={this.props.bookLoading} <DepthChart
orders={this.props.bookOrders} bookLoading={this.props.bookLoading}
type={this.props.type} orders={this.props.bookOrders}
currency={this.props.currency} lastDayPremium={this.props.lastDayPremium}
maxWidth={(this.props.windowWidth / this.props.theme.typography.fontSize) * 0.97} // EM units currency={this.props.currency}
maxHeight={(this.props.windowHeight / this.props.theme.typography.fontSize) * 0.8 - 11} // EM units compact={true}
/> setAppState={this.props.setAppState}
); limits={this.props.limits}
maxWidth={widthEm * 0.8} // EM units
maxHeight={heightEm * 0.8 - 11} // EM units
/>
);
} else {
return (
<BookTable
loading={this.props.bookLoading}
orders={this.props.bookOrders}
type={this.props.type}
currency={this.props.currency}
maxWidth={widthEm * 0.97} // EM units
maxHeight={heightEm * 0.8 - 11} // EM units
/>
);
}
} }
}; };
getTitle = () => { getTitle = (doubleView) => {
const { t } = this.props; const { t } = this.props;
if (this.state.view == 'list') { if (this.state.view == 'list' || doubleView) {
if (this.props.type == 0) { if (this.props.type == 0) {
return t('You are SELLING BTC for {{currencyCode}}', { return t('You are SELLING BTC for {{currencyCode}}', {
currencyCode: this.props.bookCurrencyCode, currencyCode: this.props.bookCurrencyCode,
@ -160,10 +207,10 @@ class BookPage extends Component {
} }
}; };
render() { mainFilters = () => {
const { t } = this.props; const { t } = this.props;
return ( return (
<Grid className='orderBook' container spacing={1} sx={{ minWidth: 400 }}> <>
<IconButton <IconButton
sx={{ position: 'fixed', right: '0px', top: '30px' }} sx={{ position: 'fixed', right: '0px', top: '30px' }}
onClick={() => this.setState({ loading: true }) & this.getOrderDetails()} onClick={() => this.setState({ loading: true }) & this.getOrderDetails()}
@ -235,13 +282,26 @@ class BookPage extends Component {
</Select> </Select>
</FormControl> </FormControl>
</Grid> </Grid>
</>
);
};
render() {
const { t } = this.props;
const widthEm = this.props.windowWidth / this.props.theme.typography.fontSize;
const heightEm = this.props.windowHeight / this.props.theme.typography.fontSize;
const doubleView = widthEm > 115;
return (
<Grid className='orderBook' container spacing={1} sx={{ minWidth: 400 }}>
{this.mainFilters()}
<Grid item xs={12} align='center'> <Grid item xs={12} align='center'>
<Typography component='h5' variant='h5'> <Typography component='h5' variant='h5'>
{this.getTitle()} {this.getTitle(doubleView)}
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} align='center'> <Grid item xs={12} align='center'>
{this.mainView()} {this.mainView(doubleView, widthEm, heightEm)}
</Grid> </Grid>
<Grid item xs={12} align='center'> <Grid item xs={12} align='center'>
<ButtonGroup variant='contained' aria-label='outlined primary button group'> <ButtonGroup variant='contained' aria-label='outlined primary button group'>
@ -250,17 +310,23 @@ class BookPage extends Component {
<Button variant='contained' color='primary' to='/make/' component={Link}> <Button variant='contained' color='primary' to='/make/' component={Link}>
{t('Make Order')} {t('Make Order')}
</Button> </Button>
<Button color='inherit' style={{ color: '#111111' }} onClick={this.handleClickView}> {doubleView ? null : (
{this.state.view == 'depth' ? ( <Button
<> color='inherit'
<FormatListBulleted /> {t('List')} style={{ color: '#111111' }}
</> onClick={this.handleClickView}
) : ( >
<> {this.state.view == 'depth' ? (
<BarChart /> {t('Chart')} <>
</> <FormatListBulleted /> {t('List')}
)} </>
</Button> ) : (
<>
<BarChart /> {t('Chart')}
</>
)}
</Button>
)}
</> </>
) : null} ) : null}
<Button color='secondary' variant='contained' to='/' component={Link}> <Button color='secondary' variant='contained' to='/' component={Link}>

View File

@ -171,12 +171,10 @@ class BottomBar extends Component {
showProfileButton = () => { showProfileButton = () => {
return ( return (
this.props.avatarLoaded && ( this.props.avatarLoaded &&
window.NativeRobosats || ( (window.NativeRobosats ||
(this.props.token ? getCookie('robot_token') === this.props.token : true) && ((this.props.token ? getCookie('robot_token') === this.props.token : true) &&
getCookie('sessionid') getCookie('sessionid')))
)
)
); );
}; };

View File

@ -1,5 +1,5 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { HashRouter, BrowserRouter , Switch, Route } from 'react-router-dom'; import { HashRouter, BrowserRouter, Switch, Route } from 'react-router-dom';
import UserGenPage from './UserGenPage'; import UserGenPage from './UserGenPage';
import MakerPage from './MakerPage'; import MakerPage from './MakerPage';
@ -59,16 +59,16 @@ export default class HomePage extends Component {
getBasename() { getBasename() {
if (window.NativeRobosats) { if (window.NativeRobosats) {
// Only for Android // Only for Android
return window.location.pathname return window.location.pathname;
} }
return "" return '';
} }
render() { render() {
const fontSize = this.props.theme.typography.fontSize; const fontSize = this.props.theme.typography.fontSize;
const fontSizeFactor = fontSize / 14; // default fontSize is 14 const fontSizeFactor = fontSize / 14; // default fontSize is 14
const Router = window.NativeRobosats ? HashRouter : BrowserRouter const Router = window.NativeRobosats ? HashRouter : BrowserRouter;
return ( return (
<Router basename={this.getBasename()}> <Router basename={this.getBasename()}>

View File

@ -30,13 +30,13 @@ const RobotAvatar: React.FC<Props> = ({
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [avatarSrc, setAvatarSrc] = useState<string>() const [avatarSrc, setAvatarSrc] = useState<string>();
useEffect(() => { useEffect(() => {
if (nickname) { if (nickname) {
apiClient.fileImageUrl('/static/assets/avatars/' + nickname + '.png').then(setAvatarSrc) apiClient.fileImageUrl('/static/assets/avatars/' + nickname + '.png').then(setAvatarSrc);
} }
}, [nickname]) }, [nickname]);
const statusBadge = ( const statusBadge = (
<div style={{ position: 'relative', left: '6px', top: '1px' }}> <div style={{ position: 'relative', left: '6px', top: '1px' }}>

View File

@ -296,10 +296,9 @@ class UserGenPage extends Component {
<IconButton <IconButton
color='primary' color='primary'
disabled={ disabled={
!this.props.avatarLoaded || ( !this.props.avatarLoaded ||
!window.NativeRobosats && (!window.NativeRobosats &&
!(getCookie('robot_token') === this.state.token) !(getCookie('robot_token') === this.state.token))
)
} }
onClick={() => onClick={() =>
saveAsJson(this.state.nickname + '.json', this.createJsonFile()) saveAsJson(this.state.nickname + '.json', this.createJsonFile())
@ -317,10 +316,9 @@ class UserGenPage extends Component {
<IconButton <IconButton
color={this.props.copiedToken ? 'inherit' : 'primary'} color={this.props.copiedToken ? 'inherit' : 'primary'}
disabled={ disabled={
!this.props.avatarLoaded || ( !this.props.avatarLoaded ||
!window.NativeRobosats && (!window.NativeRobosats &&
!(getCookie('robot_token') === this.state.token) !(getCookie('robot_token') === this.state.token))
)
} }
onClick={() => onClick={() =>
copyToClipboard(getCookie('robot_token')) & copyToClipboard(getCookie('robot_token')) &
@ -375,10 +373,9 @@ class UserGenPage extends Component {
<ButtonGroup variant='contained' aria-label='outlined primary button group'> <ButtonGroup variant='contained' aria-label='outlined primary button group'>
<Button <Button
disabled={ disabled={
this.state.loadingRobot !== false || ( this.state.loadingRobot !== false ||
!window.NativeRobosats && (!window.NativeRobosats &&
!(this.props.token ? getCookie('robot_token') === this.props.token : true) !(this.props.token ? getCookie('robot_token') === this.props.token : true))
)
} }
color='primary' color='primary'
to='/make/' to='/make/'
@ -396,10 +393,9 @@ class UserGenPage extends Component {
/> />
<Button <Button
disabled={ disabled={
this.state.loadingRobot !== false || ( this.state.loadingRobot !== false ||
!window.NativeRobosats && (!window.NativeRobosats &&
!(this.props.token ? getCookie('robot_token') === this.props.token : true) !(this.props.token ? getCookie('robot_token') === this.props.token : true))
)
} }
color='secondary' color='secondary'
to='/book/' to='/book/'

View File

@ -3,29 +3,29 @@ import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next'; import { initReactI18next } from 'react-i18next';
import HttpApi from 'i18next-http-backend'; import HttpApi from 'i18next-http-backend';
import translationEN from "../../static/locales/en.json"; import translationEN from '../../static/locales/en.json';
import translationES from "../../static/locales/es.json"; import translationES from '../../static/locales/es.json';
import translationDE from "../../static/locales/de.json"; import translationDE from '../../static/locales/de.json';
import translationRU from "../../static/locales/ru.json"; import translationRU from '../../static/locales/ru.json';
import translationPL from "../../static/locales/pl.json"; import translationPL from '../../static/locales/pl.json';
import translationFR from "../../static/locales/fr.json"; import translationFR from '../../static/locales/fr.json';
import translationCA from "../../static/locales/ca.json"; import translationCA from '../../static/locales/ca.json';
import translationIT from "../../static/locales/it.json"; import translationIT from '../../static/locales/it.json';
import translationPT from "../../static/locales/pt.json"; import translationPT from '../../static/locales/pt.json';
import translationEU from "../../static/locales/th.json"; import translationEU from '../../static/locales/th.json';
const config = { const config = {
resources: { resources: {
en: {translations: translationEN}, en: { translations: translationEN },
es: {translations: translationES}, es: { translations: translationES },
ru: {translations: translationRU}, ru: { translations: translationRU },
de: {translations: translationDE}, de: { translations: translationDE },
pl: {translations: translationPL}, pl: { translations: translationPL },
fr: {translations: translationFR}, fr: { translations: translationFR },
ca: {translations: translationCA}, ca: { translations: translationCA },
it: {translations: translationIT}, it: { translations: translationIT },
pt: {translations: translationPT}, pt: { translations: translationPT },
eu: {translations: translationEU}, eu: { translations: translationEU },
}, },
fallbackLng: 'en', fallbackLng: 'en',
debug: false, debug: false,
@ -40,13 +40,8 @@ const config = {
react: { react: {
useSuspense: false, useSuspense: false,
}, },
} };
i18n.use(HttpApi).use(LanguageDetector).use(initReactI18next).init(config);
i18n
.use(HttpApi)
.use(LanguageDetector)
.use(initReactI18next)
.init(config);
export default i18n; export default i18n;

View File

@ -26,13 +26,8 @@ const config = {
react: { react: {
useSuspense: false, useSuspense: false,
}, },
} };
i18n.use(HttpApi).use(LanguageDetector).use(initReactI18next).init(config);
i18n
.use(HttpApi)
.use(LanguageDetector)
.use(initReactI18next)
.init(config);
export default i18n; export default i18n;

View File

@ -1,28 +1,28 @@
import NativeRobosats from './index' import NativeRobosats from './index';
declare global { declare global {
interface Window { interface Window {
ReactNativeWebView?: ReactNativeWebView ReactNativeWebView?: ReactNativeWebView;
NativeRobosats?: NativeRobosats NativeRobosats?: NativeRobosats;
} }
} }
export interface ReactNativeWebView { export interface ReactNativeWebView {
postMessage(message: string): void postMessage(message: string): void;
} }
export interface NativeWebViewMessageHttp { export interface NativeWebViewMessageHttp {
id?: number id?: number;
category: 'http' category: 'http';
type: 'post' | 'get' | 'put' | 'delete' | 'xhr' type: 'post' | 'get' | 'put' | 'delete' | 'xhr';
path: string path: string;
headers?: object headers?: object;
body?: object body?: object;
} }
export declare type NativeWebViewMessage = NativeWebViewMessageHttp export declare type NativeWebViewMessage = NativeWebViewMessageHttp;
export interface NativeRobosatsPromise { export interface NativeRobosatsPromise {
resolve: (value: object | PromiseLike<object>) => void, resolve: (value: object | PromiseLike<object>) => void;
reject: (reason?: any) => void reject: (reason?: any) => void;
} }

View File

@ -1,43 +1,51 @@
import { NativeRobosatsPromise, NativeWebViewMessage } from './index.d' import { NativeRobosatsPromise, NativeWebViewMessage } from './index.d';
class NativeRobosats { class NativeRobosats {
constructor() { constructor() {
this.messageCounter = 0 this.messageCounter = 0;
} }
private messageCounter: number private messageCounter: number;
private pendingMessages: {[id:number]: NativeRobosatsPromise} = {} private pendingMessages: { [id: number]: NativeRobosatsPromise } = {};
public onMessageResolve: (messageId: number, response?: object) => void = (messageId, response = {}) =>{ public onMessageResolve: (messageId: number, response?: object) => void = (
messageId,
response = {},
) => {
if (this.pendingMessages[messageId]) { if (this.pendingMessages[messageId]) {
this.pendingMessages[messageId].resolve(response) this.pendingMessages[messageId].resolve(response);
delete this.pendingMessages[messageId] delete this.pendingMessages[messageId];
} }
} };
public onMessageReject: (messageId: number, response?: object) => void = (messageId, response = {}) =>{ public onMessageReject: (messageId: number, response?: object) => void = (
messageId,
response = {},
) => {
if (this.pendingMessages[messageId]) { if (this.pendingMessages[messageId]) {
this.pendingMessages[messageId].reject(response) this.pendingMessages[messageId].reject(response);
delete this.pendingMessages[messageId] delete this.pendingMessages[messageId];
} }
} };
public postMessage: (message: NativeWebViewMessage) => Promise<{[key: string]: any}> = (message) => { public postMessage: (message: NativeWebViewMessage) => Promise<{ [key: string]: any }> = (
this.messageCounter += 1 message,
message.id = this.messageCounter ) => {
const json = JSON.stringify(message) this.messageCounter += 1;
window.ReactNativeWebView?.postMessage(json) message.id = this.messageCounter;
const json = JSON.stringify(message);
window.ReactNativeWebView?.postMessage(json);
return new Promise<object>(async (resolve, reject) => { return new Promise<object>(async (resolve, reject) => {
if (message.id) { if (message.id) {
this.pendingMessages[message.id] = { this.pendingMessages[message.id] = {
resolve: resolve, resolve: resolve,
reject: reject reject: reject,
} };
} }
}) });
} };
} }
export default NativeRobosats export default NativeRobosats;

View File

@ -4,17 +4,17 @@ import NativeRobosats from '../../Native';
class ApiNativeClient implements ApiClient { class ApiNativeClient implements ApiClient {
constructor() { constructor() {
window.NativeRobosats = new NativeRobosats() window.NativeRobosats = new NativeRobosats();
} }
private assetsCache : {[path:string]: string} = {} private assetsCache: { [path: string]: string } = {};
private readonly getHeaders: () => HeadersInit = () => { private readonly getHeaders: () => HeadersInit = () => {
return { 'Content-Type': 'application/json', 'X-CSRFToken': getCookie('csrftoken') || '' }; return { 'Content-Type': 'application/json', 'X-CSRFToken': getCookie('csrftoken') || '' };
}; };
public put: (path: string, body: object) => Promise<object | undefined> = async (path, body) => { public put: (path: string, body: object) => Promise<object | undefined> = async (path, body) => {
return new Promise((res, _rej) => res({})) return new Promise((res, _rej) => res({}));
}; };
public delete: (path: string) => Promise<object | undefined> = async (path) => { public delete: (path: string) => Promise<object | undefined> = async (path) => {
@ -22,8 +22,8 @@ class ApiNativeClient implements ApiClient {
category: 'http', category: 'http',
type: 'delete', type: 'delete',
path, path,
headers: this.getHeaders() headers: this.getHeaders(),
}) });
}; };
public post: (path: string, body: object) => Promise<object | undefined> = async (path, body) => { public post: (path: string, body: object) => Promise<object | undefined> = async (path, body) => {
@ -32,16 +32,16 @@ class ApiNativeClient implements ApiClient {
type: 'post', type: 'post',
path, path,
body, body,
headers: this.getHeaders() headers: this.getHeaders(),
}) });
}; };
public get: (path: string) => Promise<object | undefined> = async (path) => { public get: (path: string) => Promise<object | undefined> = async (path) => {
return window.NativeRobosats?.postMessage({ return window.NativeRobosats?.postMessage({
category: 'http', category: 'http',
type: 'get', type: 'get',
path path,
}) });
}; };
public fileImageUrl: (path: string) => Promise<string | undefined> = async (path) => { public fileImageUrl: (path: string) => Promise<string | undefined> = async (path) => {
@ -50,18 +50,18 @@ class ApiNativeClient implements ApiClient {
} }
if (this.assetsCache[path]) { if (this.assetsCache[path]) {
return this.assetsCache[path] return this.assetsCache[path];
} }
const fileB64 = await window.NativeRobosats?.postMessage({ const fileB64 = await window.NativeRobosats?.postMessage({
category: 'http', category: 'http',
type: 'xhr', type: 'xhr',
path path,
}) });
this.assetsCache[path] = `data:image/png;base64,${fileB64?.b64Data}` this.assetsCache[path] = `data:image/png;base64,${fileB64?.b64Data}`;
return this.assetsCache[path] return this.assetsCache[path];
}; };
} }

View File

@ -40,8 +40,8 @@ class ApiWebClient implements ApiClient {
if (!path) { if (!path) {
return ''; return '';
} }
return window.location.origin + path return window.location.origin + path;
}; };
} }

View File

@ -9,4 +9,6 @@ export interface ApiClient {
fileImageUrl: (path: string) => Promise<string | undefined>; fileImageUrl: (path: string) => Promise<string | undefined>;
} }
export const apiClient: ApiClient = window.ReactNativeWebView ? new ApiNativeClient() : new ApiWebClient(); export const apiClient: ApiClient = window.ReactNativeWebView
? new ApiNativeClient()
: new ApiWebClient();

View File

@ -169,7 +169,7 @@ input[type='number'] {
width: auto !important; width: auto !important;
} }
@media (max-height: 725px) { @media (max-height: 725px) and (max-width: 925px) {
.appCenter:has(> div.MuiGrid-root:first-child, > div.MuiBox-root:first-child) { .appCenter:has(> div.MuiGrid-root:first-child, > div.MuiBox-root:first-child) {
overflow-y: auto; overflow-y: auto;
margin-top: 1em; margin-top: 1em;

View File

@ -19,7 +19,7 @@ const config: Configuration = {
}, },
resolve: { resolve: {
extensions: ['.tsx', '.ts', '.jsx', '.js'], extensions: ['.tsx', '.ts', '.jsx', '.js'],
} },
}; };
const configWeb: Configuration = { const configWeb: Configuration = {
@ -43,9 +43,9 @@ const configMobile: Configuration = {
condition: 'if-replacement-exists', condition: 'if-replacement-exists',
replacement: path.resolve(__dirname, 'src/components/i18n.Native.js'), replacement: path.resolve(__dirname, 'src/components/i18n.Native.js'),
async: true, async: true,
} },
} },
] ],
}, },
output: { output: {
path: path.resolve(__dirname, '../mobile/html/Web.bundle/js'), path: path.resolve(__dirname, '../mobile/html/Web.bundle/js'),