robosats/frontend/src/components/BookPage.js

458 lines
20 KiB
JavaScript
Raw Normal View History

import React, { Component } from "react";
2022-05-08 15:43:08 +00:00
import { withTranslation } from "react-i18next";
import { Badge, Tooltip, Stack, Paper, Button, FormControlLabel, Checkbox, RadioGroup, ListItemButton, Typography, Grid, Select, MenuItem, FormControl, FormHelperText, ListItemText, ListItemAvatar, IconButton, CircularProgress} from "@mui/material";
2022-01-08 11:51:55 +00:00
import { Link } from 'react-router-dom'
2022-01-14 21:40:54 +00:00
import { DataGrid } from '@mui/x-data-grid';
import currencyDict from '../../static/assets/currencies.json';
2022-01-27 14:40:14 +00:00
import MediaQuery from 'react-responsive'
2022-02-01 19:43:33 +00:00
import Image from 'material-ui-image'
import FlagWithProps from './FlagWithProps'
import { pn } from "../utils/prettyNumbers";
import PaymentText from './PaymentText'
// Icons
import RefreshIcon from '@mui/icons-material/Refresh';
2022-05-16 18:01:17 +00:00
import { SendReceiveIcon, BuySatsCheckedIcon, BuySatsIcon, SellSatsCheckedIcon, SellSatsIcon} from "./Icons";
class BookPage extends Component {
constructor(props) {
super(props);
this.state = {
2022-03-06 21:10:57 +00:00
pageSize: 6,
};
2022-05-02 16:32:15 +00:00
if(this.props.bookOrders.length == 0){
2022-05-02 16:32:15 +00:00
this.getOrderDetails(2, 0)
}
}
2022-01-14 21:40:54 +00:00
getOrderDetails(type, currency) {
2022-05-02 16:32:15 +00:00
this.props.setAppState({bookLoading: true});
fetch('/api/book' + '?currency=' + currency + "&type=" + type)
.then((response) => response.json())
2022-05-02 16:32:15 +00:00
.then((data) => (this.props.setAppState({
bookNotFound: data.not_found,
bookLoading: false,
bookOrders: data,
})));
}
2022-01-14 21:40:54 +00:00
handleRowClick=(e)=>{
2022-01-04 16:27:15 +00:00
this.props.history.push('/order/' + e);
}
handleCurrencyChange=(e)=>{
2022-04-01 14:52:44 +00:00
var currency = e.target.value;
this.props.setAppState({
currency: currency,
2022-04-01 14:52:44 +00:00
bookCurrencyCode: this.getCurrencyCode(currency),
})
}
getCurrencyCode(val){
const { t } = this.props;
if (val){
return val == 0 ? t('ANY_currency') : currencyDict[val.toString()]
2022-04-01 14:52:44 +00:00
}else{
return t('ANY_currency')
}
}
2022-05-02 19:28:34 +00:00
2022-02-03 18:06:30 +00:00
// Colors for the status badges
statusBadgeColor(status){
if(status=='Active'){return("success")}
if(status=='Seen recently'){return("warning")}
if(status=='Inactive'){return('error')}
}
amountToString = (amount,has_range,min_amount,max_amount) => {
if (has_range){
return pn(parseFloat(Number(min_amount).toPrecision(2)))+'-'+pn(parseFloat(Number(max_amount).toPrecision(2)))
}else{
return pn(parseFloat(Number(amount).toPrecision(3)))
}
}
2022-02-03 18:06:30 +00:00
2022-01-27 14:40:14 +00:00
bookListTableDesktop=()=>{
const { t } = this.props;
2022-01-14 21:40:54 +00:00
return (
2022-03-06 21:10:57 +00:00
<div style={{ height: 422, width: '100%' }}>
2022-01-14 21:40:54 +00:00
<DataGrid
rows={
this.props.bookOrders.filter(order => order.type == this.props.type || this.props.type == 2)
.filter(order => order.currency == this.props.currency || this.props.currency == 0)
.map((order) =>
2022-01-14 21:40:54 +00:00
({id: order.id,
avatar: window.location.origin +'/static/assets/avatars/' + order.maker_nick + '.png',
2022-05-02 19:28:34 +00:00
robot: order.maker_nick,
2022-02-03 18:06:30 +00:00
robot_status: order.maker_status,
type: order.type ? t("Seller"): t("Buyer"),
amount: order.amount,
has_range: order.has_range,
min_amount: order.min_amount,
max_amount: order.max_amount,
2022-01-14 21:40:54 +00:00
currency: this.getCurrencyCode(order.currency),
payment_method: order.payment_method,
price: order.price,
premium: order.premium,
})
)}
loading={this.props.bookLoading}
2022-01-14 21:40:54 +00:00
columns={[
// { field: 'id', headerName: 'ID', width: 40 },
2022-05-02 19:28:34 +00:00
{ field: 'robot', headerName: t("Robot"), width: 240,
2022-01-14 21:40:54 +00:00
renderCell: (params) => {return (
<ListItemButton style={{ cursor: "pointer" }}>
<ListItemAvatar>
2022-05-02 19:28:34 +00:00
<Tooltip placement="right" enterTouchDelay={0} title={t(params.row.robot_status)}>
2022-02-03 18:06:30 +00:00
<Badge variant="dot" overlap="circular" badgeContent="" color={this.statusBadgeColor(params.row.robot_status)}>
<Badge overlap="circular" anchorOrigin={{horizontal: 'right', vertical: 'bottom'}} badgeContent={<div style={{position:"relative", left:"11px", top:"2px"}}> {params.row.type == t("Buyer") ? <SendReceiveIcon sx={{transform: "scaleX(-1)",height:"20px",width:"20px"}} color="secondary"/> : <SendReceiveIcon sx={{height:"20px",width:"20px"}} color="primary"/>}</div>}>
2022-02-03 18:06:30 +00:00
<div style={{ width: 45, height: 45 }}>
2022-05-02 19:28:34 +00:00
<Image className='bookAvatar'
disableError={true}
disableSpinner={true}
2022-02-03 18:06:30 +00:00
color='null'
alt={params.row.robot}
src={params.row.avatar}
/>
</div>
</Badge>
</Badge>
2022-02-03 18:06:30 +00:00
</Tooltip>
2022-01-14 21:40:54 +00:00
</ListItemAvatar>
2022-02-03 18:06:30 +00:00
<ListItemText primary={params.row.robot}/>
2022-01-14 21:40:54 +00:00
</ListItemButton>
);
} },
{ field: 'type', headerName: t("Is"), width: 60 },
{ field: 'amount', headerName: t("Amount"), type: 'number', width: 90,
renderCell: (params) => {return (
<div style={{ cursor: "pointer" }}>{this.amountToString(params.row.amount,params.row.has_range, params.row.min_amount, params.row.max_amount)}</div>
)}},
2022-05-02 19:28:34 +00:00
{ field: 'currency', headerName: t("Currency"), width: 100,
2022-01-15 15:12:26 +00:00
renderCell: (params) => {return (
<div style={{ cursor: "pointer", display:'flex',alignItems:'center', flexWrap:'wrap'}}>
{params.row.currency+" "}
<FlagWithProps code={params.row.currency} />
</div>
)
}},
{ field: 'payment_method', headerName: t("Payment Method"), width: 180 ,
renderCell: (params) => {return (
2022-05-01 21:39:50 +00:00
<div style={{ cursor: "pointer" }}><PaymentText othersText={t("Others")} verbose={true} size={24} text={params.row.payment_method}/></div>
)} },
{ field: 'price', headerName: t("Price"), type: 'number', width: 140,
2022-01-14 21:40:54 +00:00
renderCell: (params) => {return (
<div style={{ cursor: "pointer" }}>{pn(params.row.price) + " " +params.row.currency+ "/BTC" }</div>
2022-01-14 21:40:54 +00:00
)} },
{ field: 'premium', headerName: t("Premium"), type: 'number', width: 100,
2022-01-14 21:40:54 +00:00
renderCell: (params) => {return (
<div style={{ cursor: "pointer" }}>{parseFloat(parseFloat(params.row.premium).toFixed(4))+"%" }</div>
)} },
]}
2022-05-02 19:28:34 +00:00
components={{
NoRowsOverlay: () => (
<Stack height="100%" alignItems="center" justifyContent="center">
<div style={{ height:"220px"}}/>
2022-05-05 21:28:54 +00:00
{this.NoOrdersFound()}
</Stack>
),
NoResultsOverlay: () => (
<Stack height="100%" alignItems="center" justifyContent="center">
{t("Filter has no results")}
</Stack>
)
}}
pageSize={this.props.bookLoading ? 0 : this.state.pageSize}
2022-05-10 18:44:12 +00:00
rowsPerPageOptions={[0,6,20,50]}
2022-03-06 21:10:57 +00:00
onPageSizeChange={(newPageSize) => this.setState({pageSize:newPageSize})}
2022-01-14 21:40:54 +00:00
onRowClick={(params) => this.handleRowClick(params.row.id)} // Whole row is clickable, but the mouse only looks clickly in some places.
/>
</div>
);
2022-01-05 13:39:58 +00:00
}
2022-01-27 14:40:14 +00:00
bookListTablePhone=()=>{
const { t } = this.props;
2022-01-27 14:40:14 +00:00
return (
<div style={{ height: 422, width: '100%' }}>
2022-01-27 14:40:14 +00:00
<DataGrid
loading={this.props.bookLoading}
2022-01-27 14:40:14 +00:00
rows={
this.props.bookOrders.filter(order => order.type == this.props.type || this.props.type == 2)
.filter(order => order.currency == this.props.currency || this.props.currency == 0)
.map((order) =>
2022-01-27 14:40:14 +00:00
({id: order.id,
avatar: window.location.origin +'/static/assets/avatars/' + order.maker_nick + '.png',
2022-05-02 19:28:34 +00:00
robot: order.maker_nick,
2022-02-03 18:06:30 +00:00
robot_status: order.maker_status,
type: order.type ? t("Seller"): t("Buyer"),
amount: order.amount,
has_range: order.has_range,
min_amount: order.min_amount,
max_amount: order.max_amount,
2022-01-27 14:40:14 +00:00
currency: this.getCurrencyCode(order.currency),
payment_method: order.payment_method,
price: order.price,
premium: order.premium,
})
)}
columns={[
// { field: 'id', headerName: 'ID', width: 40 },
2022-05-02 19:28:34 +00:00
{ field: 'robot', headerName: t("Robot"), width: 64,
2022-01-27 14:40:14 +00:00
renderCell: (params) => {return (
<div style={{ position: "relative", left: "-5px" }}>
2022-05-02 19:28:34 +00:00
<Tooltip placement="right" enterTouchDelay={0} title={params.row.robot+" ("+t(params.row.robot_status)+")"}>
<Badge variant="dot" overlap="circular" badgeContent="" color={this.statusBadgeColor(params.row.robot_status)}>
<Badge overlap="circular" anchorOrigin={{horizontal: 'right', vertical: 'bottom'}} badgeContent={<div style={{position:"relative", left:"11px", top:"2px"}}> {params.row.type == t("Buyer") ? <SendReceiveIcon sx={{transform: "scaleX(-1)",height:"20px",width:"20px"}} color="secondary"/> : <SendReceiveIcon sx={{height:"20px",width:"20px"}} color="primary"/>}</div>}>
<div style={{ width: 45, height: 45 }}>
2022-05-02 19:28:34 +00:00
<Image className='bookAvatar'
2022-05-08 15:43:08 +00:00
disableError={true}
disableSpinner={true}
color={null}
alt={params.row.robot}
src={params.row.avatar}
/>
</div>
</Badge>
</Badge>
</Tooltip>
</div>
2022-01-27 14:40:14 +00:00
);
} },
{ field: 'type', headerName: t("Is"), width: 60, hide:'true'},
2022-05-02 19:28:34 +00:00
{ field: 'amount', headerName: t("Amount"), type: 'number', width: 84,
2022-02-03 18:06:30 +00:00
renderCell: (params) => {return (
2022-05-02 19:28:34 +00:00
<Tooltip placement="right" enterTouchDelay={0} title={t(params.row.type)}>
<div style={{ cursor: "pointer" }}>{this.amountToString(params.row.amount,params.row.has_range, params.row.min_amount, params.row.max_amount)}</div>
2022-02-03 18:06:30 +00:00
</Tooltip>
)} },
2022-05-02 19:28:34 +00:00
{ field: 'currency', headerName: t("Currency"), width: 85,
2022-01-27 14:40:14 +00:00
renderCell: (params) => {return (
2022-05-02 19:28:34 +00:00
// <Tooltip placement="left" enterTouchDelay={0} title={params.row.payment_method}>
<div style={{ cursor: "pointer", display:'flex',alignItems:'center', flexWrap:'wrap'}}>
{params.row.currency+" "}
<FlagWithProps code={params.row.currency} />
</div>
// </Tooltip>
2022-01-27 14:40:14 +00:00
)} },
{ field: 'payment_method', headerName: t("Payment Method"), width: 180, hide:'true'},
{ field: 'payment_icons', headerName: t("Pay"), width: 75 ,
renderCell: (params) => {return (
<div style={{position:'relative', left:'-4px', cursor: "pointer", align:"center"}}><PaymentText othersText={t("Others")} size={16} text={params.row.payment_method}/></div>
)} },
{ field: 'price', headerName: t("Price"), type: 'number', width: 140, hide:'true',
2022-01-27 14:40:14 +00:00
renderCell: (params) => {return (
<div style={{ cursor: "pointer" }}>{pn(params.row.price) + " " +params.row.currency+ "/BTC" }</div>
2022-01-27 14:40:14 +00:00
)} },
{ field: 'premium', headerName: t("Premium"), type: 'number', width: 85,
2022-01-27 14:40:14 +00:00
renderCell: (params) => {return (
2022-05-02 19:28:34 +00:00
<Tooltip placement="left" enterTouchDelay={0} title={pn(params.row.price) + " " +params.row.currency+ "/BTC" }>
2022-01-27 14:40:14 +00:00
<div style={{ cursor: "pointer" }}>{parseFloat(parseFloat(params.row.premium).toFixed(4))+"%" }</div>
</Tooltip>
2022-01-27 14:40:14 +00:00
)} },
]}
2022-05-02 19:28:34 +00:00
components={{
NoRowsOverlay: () => (
<Stack height="100%" alignItems="center" justifyContent="center">
<div style={{ height:"220px"}}/>
2022-05-05 21:28:54 +00:00
{this.NoOrdersFound()}
</Stack>
),
NoResultsOverlay: () => (
<Stack height="100%" alignItems="center" justifyContent="center">
{t("Local filter returns no result")}
</Stack>
)
}}
pageSize={this.props.bookLoading ? 0 : this.state.pageSize}
2022-05-10 18:44:12 +00:00
rowsPerPageOptions={[0,6,20,50]}
2022-03-06 21:10:57 +00:00
onPageSizeChange={(newPageSize) => this.setState({pageSize:newPageSize})}
2022-01-27 14:40:14 +00:00
onRowClick={(params) => this.handleRowClick(params.row.id)} // Whole row is clickable, but the mouse only looks clickly in some places.
2022-03-06 21:10:57 +00:00
2022-01-27 14:40:14 +00:00
/>
</div>
);
}
handleTypeChange=(buyChecked, sellChecked)=>{
this.props.setAppState({buyChecked: buyChecked, sellChecked: sellChecked})
if (buyChecked & sellChecked || !buyChecked & !sellChecked) {
var type = 2
} else if (buyChecked) {
var type = 1
} else if (sellChecked) {
var type = 0
}
this.props.setAppState({type: type})
}
handleClickBuy=(e)=>{
var buyChecked = e.target.checked
var sellChecked = this.props.sellChecked
this.handleTypeChange(buyChecked, sellChecked);
}
handleClickSell=(e)=>{
var buyChecked = this.props.buyChecked
var sellChecked = e.target.checked
this.handleTypeChange(buyChecked, sellChecked);
}
NoOrdersFound=()=>{
const { t } = this.props;
2022-05-08 15:43:08 +00:00
return(
<Grid item xs={12} align="center">
<Grid item xs={12} align="center">
<Typography component="h5" variant="h5">
{this.props.type == 0 ?
t("No orders found to sell BTC for {{currencyCode}}",{currencyCode:this.props.bookCurrencyCode})
:
t("No orders found to buy BTC for {{currencyCode}}",{currencyCode:this.props.bookCurrencyCode})
}
</Typography>
</Grid>
<br/>
<Grid item>
<Button size="large" variant="contained" color='primary' to='/make/' component={Link}>{t("Make Order")}</Button>
</Grid>
2022-05-08 15:43:08 +00:00
<Typography color="primary" variant="body1">
<b>{t("Be the first one to create an order")}</b>
<br/>
<br/>
</Typography>
</Grid>
)
}
render() {
const { t } = this.props;
return (
2022-01-31 03:04:54 +00:00
<Grid className='orderBook' container spacing={1} sx={{minWidth:400}}>
<IconButton sx={{position:'fixed',right:'0px', top:'30px'}} onClick={()=>this.setState({loading: true}) & this.getOrderDetails(2, 0)}>
<RefreshIcon/>
</IconButton>
<Grid item xs={6} align="right">
<FormControl align="center">
2022-05-08 15:43:08 +00:00
<FormHelperText align="center" sx={{position:"relative", left:"10px", textAlign:"center"}}>
{t("I want to")}
</FormHelperText>
<RadioGroup row>
<div style={{position:"relative", left:"20px"}}>
<FormControlLabel
2022-05-08 15:43:08 +00:00
control={<Checkbox icon={<BuySatsIcon sx={{width:"30px",height:"30px"}} color="inherit"/>} checkedIcon={<BuySatsCheckedIcon sx={{width:"30px",height:"30px"}} color="primary"/>}/>}
label={
<div style={{position:"relative",top:"-13px"}}>
2022-05-02 19:28:34 +00:00
{this.props.buyChecked ?
<Typography variant="caption" color="primary"><b>{t("Buy")}</b></Typography>
2022-05-02 19:28:34 +00:00
:
<Typography variant="caption" color="text.secondary">{t("Buy")}</Typography>
}
</div>
}
labelPlacement="bottom"
checked={this.props.buyChecked}
onChange={this.handleClickBuy}
/>
</div>
<FormControlLabel
2022-05-08 15:43:08 +00:00
control={<Checkbox icon={<SellSatsIcon sx={{width:"30px",height:"30px"}} color="inherit"/>} checkedIcon={<SellSatsCheckedIcon sx={{width:"30px",height:"30px"}} color="secondary"/>}/>}
label={
<div style={{position:"relative",top:"-13px"}}>
2022-05-02 19:28:34 +00:00
{this.props.sellChecked ?
<Typography variant="caption" color="secondary"><b>{t("Sell")}</b></Typography>
2022-05-02 19:28:34 +00:00
:
<Typography variant="caption" color="text.secondary">{t("Sell")}</Typography>
}
</div>
}
labelPlacement="bottom"
checked={this.props.sellChecked}
onChange={this.handleClickSell}
/>
</RadioGroup>
</FormControl>
</Grid>
<Grid item xs={6} align="left">
<FormControl align="center">
2022-05-08 15:43:08 +00:00
<FormHelperText align="center" sx={{textAlign:"center", position:"relative", left:"-5px"}}>
{this.props.type == 0 ? t("and receive") : (this.props.type == 1 ? t("and pay with") : t("and use") )}
</FormHelperText>
<Select
//autoWidth={true}
sx={{width:120}}
label={t("Select Payment Currency")}
2022-05-02 19:28:34 +00:00
required={true}
value={this.props.currency}
inputProps={{
style: {textAlign:"center"}
}}
onChange={this.handleCurrencyChange}
> <MenuItem value={0}><div style={{display:'flex',alignItems:'center', flexWrap:'wrap'}}><FlagWithProps code="ANY" />{" "+t("ANY_currency")}</div></MenuItem>
{
Object.entries(currencyDict)
.map( ([key, value]) => <MenuItem key={key} value={parseInt(key)}><div style={{display:'flex',alignItems:'center', flexWrap:'wrap'}}><FlagWithProps code={value} />{" "+value}</div></MenuItem> )
}
</Select>
</FormControl>
</Grid>
{ this.props.bookNotFound ? "" :
<Grid item xs={12} align="center">
<Typography component="h5" variant="h5">
{this.props.type == 0 ?
2022-05-02 19:28:34 +00:00
t("You are SELLING BTC for {{currencyCode}}",{currencyCode:this.props.bookCurrencyCode})
:
(this.props.type == 1 ?
t("You are BUYING BTC for {{currencyCode}}",{currencyCode:this.props.bookCurrencyCode})
:
t("You are looking at all")
)
2022-05-02 19:28:34 +00:00
}
</Typography>
</Grid>
2022-01-05 13:39:58 +00:00
}
{ this.props.bookNotFound ?
this.NoOrdersFound()
2022-05-02 19:28:34 +00:00
:
2022-01-10 10:49:21 +00:00
<Grid item xs={12} align="center">
2022-01-27 14:40:14 +00:00
{/* Desktop Book */}
2022-03-06 21:10:57 +00:00
<MediaQuery minWidth={930}>
<Paper elevation={0} style={{width: 925, maxHeight: 500, overflow: 'auto'}}>
2022-05-05 21:28:54 +00:00
{this.bookListTableDesktop()}
2022-01-27 14:40:14 +00:00
</Paper>
</MediaQuery>
2022-01-14 21:40:54 +00:00
2022-01-27 14:40:14 +00:00
{/* Smartphone Book */}
2022-03-06 21:10:57 +00:00
<MediaQuery maxWidth={929}>
<Paper elevation={0} style={{width: 395, maxHeight: 450, overflow: 'auto'}}>
2022-05-05 21:28:54 +00:00
{this.bookListTablePhone()}
2022-01-27 14:40:14 +00:00
</Paper>
</MediaQuery>
2022-01-10 10:49:21 +00:00
</Grid>
2022-01-05 13:39:58 +00:00
}
<Grid item xs={12} align="center">
{ !this.props.bookNotFound ?
<Button variant="contained" color='primary' to='/make/' component={Link}>{t("Make Order")}</Button>
: null
}
<Button color="secondary" variant="contained" to="/" component={Link}>
{t("Back")}
</Button>
</Grid>
</Grid>
);
2022-05-02 19:28:34 +00:00
}
}
2022-05-02 19:28:34 +00:00
export default withTranslation()(BookPage);