From e47d55b582fee2257d5aa247017f4150d9fce552 Mon Sep 17 00:00:00 2001
From: Reckless_Satoshi <90936742+Reckless-Satoshi@users.noreply.github.com>
Date: Tue, 27 Sep 2022 15:52:00 +0000
Subject: [PATCH] Book functional component (#256)
* Add bookTable functional component
* Implement responsive booktable
* Fix unwanted scroll bar on chromium browsers
* Add column self-organization and 3 new columns
* Add responsive behaviour on depth chart
* Run prettier
* Add minimum pageSize (book must at least be 1 row height)
* Adjust circular spinner div height
* Add order ID column style
* Refactor window resize event listener
* Add depth chart outline
* Review fixes
---
frontend/src/components/BookPage.js | 474 ++------------
frontend/src/components/BookTable.tsx | 598 ++++++++++++++++++
.../components/Charts/DepthChart/index.tsx | 210 +++---
.../src/components/Dialogs/UpdateClient.tsx | 4 +-
frontend/src/components/HomePage.js | 19 +-
frontend/src/components/OrderPage.js | 18 +-
.../components/Robots/RobotAvatar/index.tsx | 4 +-
frontend/src/utils/checkVer.ts | 4 +-
frontend/src/utils/hexToRgb.js | 14 +
frontend/src/utils/statusBadgeColor.ts | 9 +
frontend/static/css/index.css | 9 +-
11 files changed, 800 insertions(+), 563 deletions(-)
create mode 100644 frontend/src/components/BookTable.tsx
create mode 100644 frontend/src/utils/hexToRgb.js
create mode 100644 frontend/src/utils/statusBadgeColor.ts
diff --git a/frontend/src/components/BookPage.js b/frontend/src/components/BookPage.js
index a2718c3c..a4621ec1 100644
--- a/frontend/src/components/BookPage.js
+++ b/frontend/src/components/BookPage.js
@@ -1,38 +1,27 @@
import React, { Component } from 'react';
import { withTranslation } from 'react-i18next';
import {
- Tooltip,
- Stack,
- Paper,
Button,
ToggleButtonGroup,
ToggleButton,
- ListItemButton,
Typography,
Grid,
Select,
MenuItem,
FormControl,
FormHelperText,
- ListItemText,
- ListItemAvatar,
IconButton,
ButtonGroup,
} from '@mui/material';
import { Link } from 'react-router-dom';
-import { DataGrid } from '@mui/x-data-grid';
import currencyDict from '../../static/assets/currencies.json';
-
-import MediaQuery from 'react-responsive';
import FlagWithProps from './FlagWithProps';
-import { pn, amountToString } from '../utils/prettyNumbers';
-import PaymentText from './PaymentText';
import DepthChart from './Charts/DepthChart';
-import RobotAvatar from './Robots/RobotAvatar';
import { apiClient } from '../services/api/index';
// Icons
import { BarChart, FormatListBulleted, Refresh } from '@mui/icons-material';
+import BookTable from './BookTable';
class BookPage extends Component {
constructor(props) {
@@ -43,11 +32,11 @@ class BookPage extends Component {
};
}
- componentDidMount() {
- this.getOrderDetails(2, 0);
- }
+ componentDidMount = () => {
+ this.getOrderDetails();
+ };
- getOrderDetails(type, currency) {
+ getOrderDetails() {
this.props.setAppState({ bookLoading: true });
apiClient.get('/api/book/').then((data) =>
this.props.setAppState({
@@ -79,378 +68,6 @@ class BookPage extends Component {
}
}
- // Colors for the status badges
- statusBadgeColor(status) {
- if (status === 'Active') {
- return 'success';
- }
- if (status === 'Seen recently') {
- return 'warning';
- }
- if (status === 'Inactive') {
- return 'error';
- }
- }
-
- dataGridLocaleText = () => {
- const { t } = this.props;
- return {
- MuiTablePagination: { labelRowsPerPage: t('Orders per page:') },
- noRowsLabel: t('No rows'),
- noResultsOverlayLabel: t('No results found.'),
- errorOverlayDefaultLabel: t('An error occurred.'),
- toolbarColumns: t('Columns'),
- toolbarColumnsLabel: t('Select columns'),
- columnsPanelTextFieldLabel: t('Find column'),
- columnsPanelTextFieldPlaceholder: t('Column title'),
- columnsPanelDragIconLabel: t('Reorder column'),
- columnsPanelShowAllButton: t('Show all'),
- columnsPanelHideAllButton: t('Hide all'),
- filterPanelAddFilter: t('Add filter'),
- filterPanelDeleteIconLabel: t('Delete'),
- filterPanelLinkOperator: t('Logic operator'),
- filterPanelOperators: t('Operator'), // TODO v6: rename to filterPanelOperator
- filterPanelOperatorAnd: t('And'),
- filterPanelOperatorOr: t('Or'),
- filterPanelColumns: t('Columns'),
- filterPanelInputLabel: t('Value'),
- filterPanelInputPlaceholder: t('Filter value'),
- filterOperatorContains: t('contains'),
- filterOperatorEquals: t('equals'),
- filterOperatorStartsWith: t('starts with'),
- filterOperatorEndsWith: t('ends with'),
- filterOperatorIs: t('is'),
- filterOperatorNot: t('is not'),
- filterOperatorAfter: t('is after'),
- filterOperatorOnOrAfter: t('is on or after'),
- filterOperatorBefore: t('is before'),
- filterOperatorOnOrBefore: t('is on or before'),
- filterOperatorIsEmpty: t('is empty'),
- filterOperatorIsNotEmpty: t('is not empty'),
- filterOperatorIsAnyOf: t('is any of'),
- filterValueAny: t('any'),
- filterValueTrue: t('true'),
- filterValueFalse: t('false'),
- columnMenuLabel: t('Menu'),
- columnMenuShowColumns: t('Show columns'),
- columnMenuFilter: t('Filter'),
- columnMenuHideColumn: t('Hide'),
- columnMenuUnsort: t('Unsort'),
- columnMenuSortAsc: t('Sort by ASC'),
- columnMenuSortDesc: t('Sort by DESC'),
- columnHeaderFiltersLabel: t('Show filters'),
- columnHeaderSortIconLabel: t('Sort'),
- booleanCellTrueLabel: t('yes'),
- booleanCellFalseLabel: t('no'),
- };
- };
-
- bookListTableDesktop = () => {
- const { t } = this.props;
- return (
-
-
- (order.type == this.props.type || this.props.type == null) &&
- (order.currency == this.props.currency || this.props.currency == 0),
- )}
- loading={this.props.bookLoading}
- columns={[
- // { field: 'id', headerName: 'ID', width: 40 },
- {
- field: 'maker_nick',
- headerName: t('Robot'),
- width: 240,
- renderCell: (params) => {
- return (
-
-
-
-
-
-
- );
- },
- },
- {
- field: 'type',
- headerName: t('Is'),
- width: 60,
- renderCell: (params) => (params.row.type ? t('Seller') : t('Buyer')),
- },
- {
- field: 'amount',
- headerName: t('Amount'),
- type: 'number',
- width: 90,
- renderCell: (params) => {
- return (
-
- {amountToString(
- params.row.amount,
- params.row.has_range,
- params.row.min_amount,
- params.row.max_amount,
- )}
-
- );
- },
- },
- {
- field: 'currency',
- headerName: t('Currency'),
- width: 100,
- renderCell: (params) => {
- const currencyCode = this.getCurrencyCode(params.row.currency);
- return (
-
- {currencyCode + ' '}
-
-
- );
- },
- },
- {
- field: 'payment_method',
- headerName: t('Payment Method'),
- width: 180,
- renderCell: (params) => {
- return (
-
- );
- },
- },
- {
- field: 'price',
- headerName: t('Price'),
- type: 'number',
- width: 140,
- renderCell: (params) => {
- const currencyCode = this.getCurrencyCode(params.row.currency);
- return (
-
- {pn(params.row.price) + ' ' + currencyCode + '/BTC'}
-
- );
- },
- },
- {
- field: 'premium',
- headerName: t('Premium'),
- type: 'number',
- width: 100,
- renderCell: (params) => {
- return (
-
- {parseFloat(parseFloat(params.row.premium).toFixed(4)) + '%'}
-
- );
- },
- },
- ]}
- components={{
- NoRowsOverlay: () => (
-
-
- {this.NoOrdersFound()}
-
- ),
- NoResultsOverlay: () => (
-
- {t('Filter has no results')}
-
- ),
- }}
- pageSize={this.props.bookLoading ? 0 : this.state.pageSize}
- rowsPerPageOptions={[0, 6, 20, 50]}
- onPageSizeChange={(newPageSize) => this.setState({ pageSize: newPageSize })}
- onRowClick={(params) => this.handleRowClick(params.row.id)} // Whole row is clickable, but the mouse only looks clickly in some places.
- />
-
- );
- };
-
- bookListTablePhone = () => {
- const { t } = this.props;
- return (
-
-
- (order.type == this.props.type || this.props.type == null) &&
- (order.currency == this.props.currency || this.props.currency == 0),
- )}
- columns={[
- // { field: 'id', headerName: 'ID', width: 40 },
- {
- field: 'maker_nick',
- headerName: t('Robot'),
- width: 64,
- renderCell: (params) => {
- return (
-
-
-
- );
- },
- },
- {
- field: 'amount',
- headerName: t('Amount'),
- type: 'number',
- width: 84,
- renderCell: (params) => {
- return (
-
-
- {amountToString(
- params.row.amount,
- params.row.has_range,
- params.row.min_amount,
- params.row.max_amount,
- )}
-
-
- );
- },
- },
- {
- field: 'currency',
- headerName: t('Currency'),
- width: 85,
- renderCell: (params) => {
- const currencyCode = this.getCurrencyCode(params.row.currency);
- return (
-
- {currencyCode + ' '}
-
-
- );
- },
- },
- { field: 'payment_method', headerName: t('Payment Method'), width: 180, hide: 'true' },
- {
- field: 'payment_icons',
- headerName: t('Pay'),
- width: 75,
- renderCell: (params) => {
- return (
-
- );
- },
- },
- {
- field: 'price',
- headerName: t('Price'),
- type: 'number',
- width: 140,
- hide: 'true',
- renderCell: (params) => {
- return (
-
- {pn(params.row.price) + ' ' + params.row.currency + '/BTC'}
-
- );
- },
- },
- {
- field: 'premium',
- headerName: t('Premium'),
- type: 'number',
- width: 85,
- renderCell: (params) => {
- return (
-
-
- {parseFloat(parseFloat(params.row.premium).toFixed(4)) + '%'}
-
-
- );
- },
- },
- ]}
- components={{
- NoRowsOverlay: () => (
-
-
- {this.NoOrdersFound()}
-
- ),
- NoResultsOverlay: () => (
-
- {t('Local filter returns no result')}
-
- ),
- }}
- pageSize={this.props.bookLoading ? 0 : this.state.pageSize}
- rowsPerPageOptions={[0, 6, 20, 50]}
- onPageSizeChange={(newPageSize) => this.setState({ pageSize: newPageSize })}
- onRowClick={(params) => this.handleRowClick(params.row.id)} // Whole row is clickable, but the mouse only looks clickly in some places.
- />
-
- );
- };
-
handleTypeChange = (mouseEvent, val) => {
this.props.setAppState({ type: val });
};
@@ -495,45 +112,32 @@ class BookPage extends Component {
return this.NoOrdersFound();
}
- const components =
- this.state.view == 'depth'
- ? [
- ,
- ,
- ]
- : [this.bookListTableDesktop(), this.bookListTablePhone()];
-
- return (
- <>
- {/* Desktop */}
-
-
- {components[0]}
-
-
- {/* Smartphone */}
-
-
- {components[1]}
-
-
- >
- );
+ if (this.state.view === 'depth') {
+ return (
+
+ );
+ } else {
+ return (
+
+ );
+ }
};
getTitle = () => {
@@ -562,7 +166,7 @@ class BookPage extends Component {
this.setState({ loading: true }) & this.getOrderDetails(2, 0)}
+ onClick={() => this.setState({ loading: true }) & this.getOrderDetails()}
>
@@ -631,15 +235,11 @@ class BookPage extends Component {
- {this.props.bookNotFound ? (
- <>>
- ) : (
-
-
- {this.getTitle()}
-
-
- )}
+
+
+ {this.getTitle()}
+
+
{this.mainView()}
diff --git a/frontend/src/components/BookTable.tsx b/frontend/src/components/BookTable.tsx
new file mode 100644
index 00000000..2d409747
--- /dev/null
+++ b/frontend/src/components/BookTable.tsx
@@ -0,0 +1,598 @@
+import React, { useEffect, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useHistory } from 'react-router-dom';
+import {
+ Box,
+ Typography,
+ Paper,
+ Stack,
+ ListItemButton,
+ ListItemText,
+ ListItemAvatar,
+ useTheme,
+ CircularProgress,
+} from '@mui/material';
+import { DataGrid } from '@mui/x-data-grid';
+import currencyDict from '../../static/assets/currencies.json';
+import { Order } from '../models/Order.model';
+
+import FlagWithProps from './FlagWithProps';
+import { pn, amountToString } from '../utils/prettyNumbers';
+import PaymentText from './PaymentText';
+import RobotAvatar from './Robots/RobotAvatar';
+import hexToRgb from '../utils/hexToRgb';
+import statusBadgeColor from '../utils/statusBadgeColor';
+
+interface Props {
+ loading: boolean;
+ orders: Order[];
+ type: number;
+ currency: number;
+ maxWidth: number;
+ maxHeight: number;
+}
+
+const BookTable = ({
+ loading,
+ orders,
+ type,
+ currency,
+ maxWidth,
+ maxHeight,
+}: Props): JSX.Element => {
+ const { t } = useTranslation();
+ const theme = useTheme();
+ const history = useHistory();
+
+ const fontSize = theme.typography.fontSize;
+
+ // all sizes in 'em'
+ const verticalHeightFrame = 6.9075;
+ const verticalHeightRow = 3.25;
+ const defaultPageSize = Math.max(
+ Math.floor((maxHeight - verticalHeightFrame) / verticalHeightRow),
+ 1,
+ );
+ const height = defaultPageSize * verticalHeightRow + verticalHeightFrame;
+
+ const [pageSize, setPageSize] = useState(0);
+ const [useDefaultPageSize, setUseDefaultPageSize] = useState(true);
+ useEffect(() => {
+ if (useDefaultPageSize) {
+ setPageSize(defaultPageSize);
+ }
+ });
+
+ const premiumColor = function (baseColor: string, accentColor: string, point: number) {
+ const baseRGB = hexToRgb(baseColor);
+ const accentRGB = hexToRgb(accentColor);
+ const redDiff = accentRGB[0] - baseRGB[0];
+ const red = baseRGB[0] + redDiff * point;
+ const greenDiff = accentRGB[1] - baseRGB[1];
+ const green = baseRGB[1] + greenDiff * point;
+ const blueDiff = accentRGB[2] - baseRGB[2];
+ const blue = baseRGB[2] + blueDiff * point;
+ return `rgb(${Math.round(red)}, ${Math.round(green)}, ${Math.round(blue)}, ${
+ 0.7 + point * 0.3
+ })`;
+ };
+
+ const localeText = {
+ MuiTablePagination: { labelRowsPerPage: t('Orders per page:') },
+ noRowsLabel: t('No rows'),
+ noResultsOverlayLabel: t('No results found.'),
+ errorOverlayDefaultLabel: t('An error occurred.'),
+ toolbarColumns: t('Columns'),
+ toolbarColumnsLabel: t('Select columns'),
+ columnsPanelTextFieldLabel: t('Find column'),
+ columnsPanelTextFieldPlaceholder: t('Column title'),
+ columnsPanelDragIconLabel: t('Reorder column'),
+ columnsPanelShowAllButton: t('Show all'),
+ columnsPanelHideAllButton: t('Hide all'),
+ filterPanelAddFilter: t('Add filter'),
+ filterPanelDeleteIconLabel: t('Delete'),
+ filterPanelLinkOperator: t('Logic operator'),
+ filterPanelOperators: t('Operator'),
+ filterPanelOperatorAnd: t('And'),
+ filterPanelOperatorOr: t('Or'),
+ filterPanelColumns: t('Columns'),
+ filterPanelInputLabel: t('Value'),
+ filterPanelInputPlaceholder: t('Filter value'),
+ filterOperatorContains: t('contains'),
+ filterOperatorEquals: t('equals'),
+ filterOperatorStartsWith: t('starts with'),
+ filterOperatorEndsWith: t('ends with'),
+ filterOperatorIs: t('is'),
+ filterOperatorNot: t('is not'),
+ filterOperatorAfter: t('is after'),
+ filterOperatorOnOrAfter: t('is on or after'),
+ filterOperatorBefore: t('is before'),
+ filterOperatorOnOrBefore: t('is on or before'),
+ filterOperatorIsEmpty: t('is empty'),
+ filterOperatorIsNotEmpty: t('is not empty'),
+ filterOperatorIsAnyOf: t('is any of'),
+ filterValueAny: t('any'),
+ filterValueTrue: t('true'),
+ filterValueFalse: t('false'),
+ columnMenuLabel: t('Menu'),
+ columnMenuShowColumns: t('Show columns'),
+ columnMenuFilter: t('Filter'),
+ columnMenuHideColumn: t('Hide'),
+ columnMenuUnsort: t('Unsort'),
+ columnMenuSortAsc: t('Sort by ASC'),
+ columnMenuSortDesc: t('Sort by DESC'),
+ columnHeaderFiltersLabel: t('Show filters'),
+ columnHeaderSortIconLabel: t('Sort'),
+ booleanCellTrueLabel: t('yes'),
+ booleanCellFalseLabel: t('no'),
+ };
+
+ const robotObj = function (width: number, hide: boolean) {
+ return {
+ hide,
+ field: 'maker_nick',
+ headerName: t('Robot'),
+ width: width * fontSize,
+ renderCell: (params) => {
+ return (
+
+
+
+
+
+
+ );
+ },
+ };
+ };
+
+ const robotSmallObj = function (width: number, hide: boolean) {
+ return {
+ hide,
+ field: 'maker_nick',
+ headerName: t('Robot'),
+ width: width * fontSize,
+ renderCell: (params) => {
+ return (
+
+
+
+
+
+ );
+ },
+ };
+ };
+
+ const typeObj = function (width: number, hide: boolean) {
+ return {
+ hide,
+ field: 'type',
+ headerName: t('Is'),
+ width: width * fontSize,
+ renderCell: (params) => (params.row.type ? t('Seller') : t('Buyer')),
+ };
+ };
+
+ const amountObj = function (width: number, hide: boolean) {
+ return {
+ hide,
+ field: 'amount',
+ headerName: t('Amount'),
+ type: 'number',
+ width: width * fontSize,
+ renderCell: (params) => {
+ return (
+
+ {amountToString(
+ params.row.amount,
+ params.row.has_range,
+ params.row.min_amount,
+ params.row.max_amount,
+ )}
+
+ );
+ },
+ };
+ };
+
+ const currencyObj = function (width: number, hide: boolean) {
+ return {
+ hide,
+ field: 'currency',
+ headerName: t('Currency'),
+ width: width * fontSize,
+ renderCell: (params) => {
+ const currencyCode = currencyDict[params.row.currency.toString()];
+ return (
+
+ {currencyCode + ' '}
+
+
+ );
+ },
+ };
+ };
+
+ const paymentObj = function (width: number, hide: boolean) {
+ return {
+ hide,
+ field: 'payment_method',
+ headerName: t('Payment Method'),
+ width: width * fontSize,
+ renderCell: (params) => {
+ return (
+
+ );
+ },
+ };
+ };
+
+ const paymentSmallObj = function (width: number, hide: boolean) {
+ return {
+ hide,
+ field: 'payment_icons',
+ headerName: t('Pay'),
+ width: width * fontSize,
+ renderCell: (params) => {
+ return (
+
+ );
+ },
+ };
+ };
+
+ const priceObj = function (width: number, hide: boolean) {
+ return {
+ hide,
+ field: 'price',
+ headerName: t('Price'),
+ type: 'number',
+ width: width * fontSize,
+ renderCell: (params) => {
+ const currencyCode = currencyDict[params.row.currency.toString()];
+ return (
+ {`${pn(params.row.price)} ${currencyCode}/BTC`}
+ );
+ },
+ };
+ };
+
+ const premiumObj = function (width: number, hide: boolean) {
+ // coloring premium texts based on 4 params:
+ // Hardcoded: a sell order at 0% is an outstanding premium
+ // Hardcoded: a buy order at 10% is an outstanding premium
+ const sellStandardPremium = 10;
+ const buyOutstandingPremium = 10;
+ return {
+ hide,
+ field: 'premium',
+ headerName: t('Premium'),
+ type: 'number',
+ width: width * fontSize,
+ renderCell: (params) => {
+ let fontColor = `rgb(0,0,0)`;
+ if (params.row.type === 0) {
+ var premiumPoint = params.row.premium / buyOutstandingPremium;
+ premiumPoint = premiumPoint < 0 ? 0 : premiumPoint > 1 ? 1 : premiumPoint;
+ fontColor = premiumColor(
+ theme.palette.text.primary,
+ theme.palette.secondary.dark,
+ premiumPoint,
+ );
+ } else {
+ var premiumPoint = (sellStandardPremium - params.row.premium) / sellStandardPremium;
+ premiumPoint = premiumPoint < 0 ? 0 : premiumPoint > 1 ? 1 : premiumPoint;
+ fontColor = premiumColor(
+ theme.palette.text.primary,
+ theme.palette.primary.dark,
+ premiumPoint,
+ );
+ }
+ const fontWeight = 400 + Math.round(premiumPoint * 5) * 100;
+ return (
+
+
+ {parseFloat(parseFloat(params.row.premium).toFixed(4)) + '%'}
+
+
+ );
+ },
+ };
+ };
+
+ const timerObj = function (width: number, hide: boolean) {
+ return {
+ hide,
+ field: 'escrow_duration',
+ headerName: t('Timer'),
+ type: 'number',
+ width: width * fontSize,
+ renderCell: (params) => {
+ const hours = Math.round(params.row.escrow_duration / 3600);
+ const minutes = Math.round((params.row.escrow_duration - hours * 3600) / 60);
+ return {hours > 0 ? `${hours}h` : `${minutes}m`}
;
+ },
+ };
+ };
+
+ const expiryObj = function (width: number, hide: boolean) {
+ return {
+ hide,
+ field: 'expires_at',
+ headerName: t('Expiry'),
+ type: 'string',
+ width: width * fontSize,
+ renderCell: (params) => {
+ const expiresAt = new Date(params.row.expires_at);
+ const timeToExpiry = Math.abs(expiresAt - new Date());
+ const percent = Math.round((timeToExpiry / (24 * 60 * 60 * 1000)) * 100);
+ const hours = Math.round(timeToExpiry / (3600 * 1000));
+ const minutes = Math.round((timeToExpiry - hours * (3600 * 1000)) / 60000);
+ return (
+
+
+
+
+ {hours > 0 ? `${hours}h` : `${minutes}m`}
+
+
+
+ );
+ },
+ };
+ };
+
+ const satoshisObj = function (width: number, hide: boolean) {
+ return {
+ hide,
+ field: 'satoshis_now',
+ headerName: t('Sats now'),
+ type: 'number',
+ width: width * fontSize,
+ renderCell: (params) => {
+ return (
+
+ {`${pn(Math.round(params.row.satoshis_now / 1000))}K`}
+
+ );
+ },
+ };
+ };
+
+ const idObj = function (width: number, hide: boolean) {
+ return {
+ hide,
+ field: 'id',
+ headerName: 'Order ID',
+ width: width * fontSize,
+ renderCell: (params) => {
+ return (
+
+
+ {`#${params.row.id}`}
+
+
+ );
+ },
+ };
+ };
+
+ const columnSpecs = {
+ amount: {
+ priority: 1,
+ order: 4,
+ normal: {
+ width: 6.5,
+ object: amountObj,
+ },
+ },
+ currency: {
+ priority: 2,
+ order: 5,
+ normal: {
+ width: 5.8,
+ object: currencyObj,
+ },
+ },
+ premium: {
+ priority: 3,
+ order: 11,
+ normal: {
+ width: 6,
+ object: premiumObj,
+ },
+ },
+ robot: {
+ priority: 4,
+ order: 1,
+ normal: {
+ width: 17.14,
+ object: robotObj,
+ },
+ small: {
+ width: 4.3,
+ object: robotSmallObj,
+ },
+ },
+ paymentMethod: {
+ priority: 5,
+ order: 6,
+ normal: {
+ width: 12.85,
+ object: paymentObj,
+ },
+ small: {
+ width: 5.8,
+ object: paymentSmallObj,
+ },
+ },
+ price: {
+ priority: 6,
+ order: 10,
+ normal: {
+ width: 10,
+ object: priceObj,
+ },
+ },
+ expires_at: {
+ priority: 7,
+ order: 7,
+ normal: {
+ width: 5.8,
+ object: expiryObj,
+ },
+ },
+ escrow_duration: {
+ priority: 8,
+ order: 8,
+ normal: {
+ width: 3.8,
+ object: timerObj,
+ },
+ },
+ satoshisNow: {
+ priority: 9,
+ order: 9,
+ normal: {
+ width: 6,
+ object: satoshisObj,
+ },
+ },
+ type: {
+ priority: 10,
+ order: 2,
+ normal: {
+ width: 4.3,
+ object: typeObj,
+ },
+ },
+ id: {
+ priority: 11,
+ order: 12,
+ normal: {
+ width: 4.8,
+ object: idObj,
+ },
+ },
+ };
+
+ const filteredColumns = function (maxWidth: number) {
+ const useSmall = maxWidth < 70;
+ const selectedColumns: object[] = [];
+ let width: number = 0;
+
+ for (const [key, value] of Object.entries(columnSpecs)) {
+ const colWidth = useSmall && value.small ? value.small.width : value.normal.width;
+ const colObject = useSmall && value.small ? value.small.object : value.normal.object;
+
+ if (width + colWidth < maxWidth || selectedColumns.length < 2) {
+ width = width + colWidth;
+ selectedColumns.push([colObject(colWidth, false), value.order]);
+ } else {
+ selectedColumns.push([colObject(colWidth, true), value.order]);
+ }
+ }
+
+ // sort columns by column.order value
+ selectedColumns.sort(function (first, second) {
+ return first[1] - second[1];
+ });
+
+ const columns = selectedColumns.map(function (item) {
+ return item[0];
+ });
+
+ return [columns, width * 0.875 + 0.15];
+ };
+
+ const [columns, width] = filteredColumns(maxWidth);
+
+ return (
+
+
+ (order.type == type || type == null) && (order.currency == currency || currency == 0),
+ )}
+ loading={loading}
+ columns={columns}
+ components={{
+ NoResultsOverlay: () => (
+
+ {t('Filter has no results')}
+
+ ),
+ }}
+ pageSize={loading ? 0 : pageSize}
+ rowsPerPageOptions={[0, pageSize, defaultPageSize * 2, 50, 100]}
+ onPageSizeChange={(newPageSize) => {
+ setPageSize(newPageSize);
+ setUseDefaultPageSize(false);
+ }}
+ onRowClick={(params) => history.push('/order/' + params.row.id)} // Whole row is clickable, but the mouse only looks clickly in some places.
+ />
+
+ );
+};
+
+export default BookTable;
diff --git a/frontend/src/components/Charts/DepthChart/index.tsx b/frontend/src/components/Charts/DepthChart/index.tsx
index 9c4c45da..8f4051f0 100644
--- a/frontend/src/components/Charts/DepthChart/index.tsx
+++ b/frontend/src/components/Charts/DepthChart/index.tsx
@@ -30,6 +30,7 @@ import PaymentText from '../../PaymentText';
import getNivoScheme from '../NivoScheme';
import median from '../../../utils/match';
import { apiClient } from '../../../services/api/index';
+import statusBadgeColor from '../../../utils/statusBadgeColor';
interface DepthChartProps {
bookLoading: boolean;
@@ -38,7 +39,8 @@ interface DepthChartProps {
currency: number;
setAppState: (state: object) => void;
limits: LimitList;
- compact?: boolean;
+ maxWidth: number;
+ maxHeight: number;
}
const DepthChart: React.FC = ({
@@ -48,7 +50,8 @@ const DepthChart: React.FC = ({
currency,
setAppState,
limits,
- compact,
+ maxWidth,
+ maxHeight,
}) => {
const { t } = useTranslation();
const history = useHistory();
@@ -61,6 +64,9 @@ const DepthChart: React.FC = ({
const [currencyCode, setCurrencyCode] = useState(1);
const [center, setCenter] = useState();
+ const height = maxHeight < 20 ? 20 : maxHeight;
+ const width = maxWidth < 20 ? 20 : maxWidth > 72.8 ? 72.8 : maxWidth;
+
useEffect(() => {
if (Object.keys(limits).length === 0) {
apiClient.get('/api/limits/').then((data) => {
@@ -216,17 +222,6 @@ const DepthChart: React.FC = ({
/>
);
- const statusBadgeColor = (status: string) => {
- if (status === 'Active') {
- return 'success';
- }
- if (status === 'Seen recently') {
- return 'warning';
- }
-
- return 'error';
- };
-
const generateTooltip: React.FunctionComponent = (
pointTooltip: PointTooltipProps,
) => {
@@ -293,94 +288,109 @@ const DepthChart: React.FC = ({
history.push('/order/' + point.data?.order?.id);
};
- return bookLoading || center == undefined || enrichedOrders.length < 1 ? (
-
-
-
- ) : (
-
-
-
-
-
-
-
-
-
- setXRange(xRange + rangeSteps)}>
-
-
+ return (
+
+
+ {bookLoading || center == undefined || enrichedOrders.length < 1 ? (
+
+
+
+ ) : (
+
+
+
+
+
+
+
+
+
+ setXRange(xRange + rangeSteps)}>
+
+
+
+
+
+ {xType === 'base_amount'
+ ? `${center} ${currencyDict[currencyCode]}`
+ : `${center}%`}
+
+
+
+ setXRange(xRange - rangeSteps)} disabled={xRange <= 1}>
+
+
+
+
+
+
+ Number(value).toFixed(0)}
+ lineWidth={3}
+ theme={getNivoScheme(theme)}
+ colors={[theme.palette.secondary.main, theme.palette.primary.main]}
+ xScale={{
+ type: 'linear',
+ min: center - xRange,
+ max: center + xRange,
+ }}
+ layers={['axes', 'areas', 'crosshair', 'lines', centerLine, 'slices', 'mesh']}
+ />
+
-
-
- {xType === 'base_amount' ? `${center} ${currencyDict[currencyCode]}` : `${center}%`}
-
-
-
- setXRange(xRange - rangeSteps)} disabled={xRange <= 1}>
-
-
-
-
-
-
- Number(value).toFixed(0)}
- lineWidth={3}
- theme={getNivoScheme(theme)}
- colors={[theme.palette.secondary.main, theme.palette.primary.main]}
- xScale={{
- type: 'linear',
- min: center - xRange,
- max: center + xRange,
- }}
- layers={['axes', 'areas', 'crosshair', 'lines', centerLine, 'slices', 'mesh']}
- />
-
-
+ )}
+
+
);
};
diff --git a/frontend/src/components/Dialogs/UpdateClient.tsx b/frontend/src/components/Dialogs/UpdateClient.tsx
index 562e54ea..548f7cee 100644
--- a/frontend/src/components/Dialogs/UpdateClient.tsx
+++ b/frontend/src/components/Dialogs/UpdateClient.tsx
@@ -46,7 +46,7 @@ const UpdateClientDialog = ({
{t(
'The RoboSats coordinator is on version {{coordinatorVersion}}, but your client app is {{clientVersion}}. This version mismatch might lead to a bad user experience.',
- { coordinatorVersion: coordinatorVersion, clientVersion: clientVersion },
+ { coordinatorVersion, clientVersion },
)}
@@ -63,7 +63,7 @@ const UpdateClientDialog = ({
diff --git a/frontend/src/components/HomePage.js b/frontend/src/components/HomePage.js
index 725d3ec3..7a949402 100644
--- a/frontend/src/components/HomePage.js
+++ b/frontend/src/components/HomePage.js
@@ -31,6 +31,23 @@ export default class HomePage extends Component {
};
}
+ componentDidMount = () => {
+ if (typeof window !== undefined) {
+ this.setState({ windowWidth: window.innerWidth, windowHeight: window.innerHeight });
+ window.addEventListener('resize', this.onResize);
+ }
+ };
+
+ componentWillUnmount = () => {
+ if (typeof window !== undefined) {
+ window.removeEventListener('resize', this.onResize);
+ }
+ };
+
+ onResize = () => {
+ this.setState({ windowWidth: window.innerWidth, windowHeight: window.innerHeight });
+ };
+
setAppState = (newState) => {
this.setState(newState);
};
@@ -106,7 +123,7 @@ export default class HomePage extends Component {
{
const { t } = this.props;
return (
@@ -694,7 +682,7 @@ class OrderPage extends Component {
void;
}
-const RobotAvatar: React.FC = ({
+const RobotAvatar: React.FC = ({
nickname,
orderType,
statusColor,
diff --git a/frontend/src/utils/checkVer.ts b/frontend/src/utils/checkVer.ts
index a157d0f5..771157c8 100644
--- a/frontend/src/utils/checkVer.ts
+++ b/frontend/src/utils/checkVer.ts
@@ -15,8 +15,8 @@ export const checkVer: (
const patchAvailable = !updateAvailable && patch > Number(semver[2]);
return {
- updateAvailable: updateAvailable,
- patchAvailable: patchAvailable,
+ updateAvailable,
+ patchAvailable,
coordinatorVersion: `v${major}.${minor}.${patch}`,
clientVersion: `v${semver[0]}.${semver[1]}.${semver[2]}`,
};
diff --git a/frontend/src/utils/hexToRgb.js b/frontend/src/utils/hexToRgb.js
new file mode 100644
index 00000000..e88d7f82
--- /dev/null
+++ b/frontend/src/utils/hexToRgb.js
@@ -0,0 +1,14 @@
+export default function hexToRgb(c) {
+ if (c.includes('rgb')) {
+ const vals = c.split('(')[1].split(')')[0];
+ return vals.split(',');
+ }
+ if (/^#([a-f0-9]{3}){1,2}$/.test(c)) {
+ if (c.length == 4) {
+ c = '#' + [c[1], c[1], c[2], c[2], c[3], c[3]].join('');
+ }
+ c = '0x' + c.substring(1);
+ return [(c >> 16) & 255, (c >> 8) & 255, c & 255];
+ }
+ return '';
+}
diff --git a/frontend/src/utils/statusBadgeColor.ts b/frontend/src/utils/statusBadgeColor.ts
new file mode 100644
index 00000000..8c571e91
--- /dev/null
+++ b/frontend/src/utils/statusBadgeColor.ts
@@ -0,0 +1,9 @@
+export default function statusBadgeColor(status: string) {
+ if (status === 'Active') {
+ return 'success';
+ }
+ if (status === 'Seen recently') {
+ return 'warning';
+ }
+ return 'error';
+}
diff --git a/frontend/static/css/index.css b/frontend/static/css/index.css
index a115f0df..350a46d3 100644
--- a/frontend/static/css/index.css
+++ b/frontend/static/css/index.css
@@ -169,11 +169,12 @@ input[type='number'] {
width: auto !important;
}
-@media (max-width: 929px) {
+@media (max-height: 725px) {
.appCenter:has(> div.MuiGrid-root:first-child, > div.MuiBox-root:first-child) {
- overflow-y: scroll;
- margin-top: 12px;
- padding-bottom: 25px;
+ overflow-y: auto;
+ margin-top: 1em;
+ padding-bottom: 3em;
height: 100%;
+ width: 100%;
}
}