robosats/frontend/src/components/OrderPage.js

840 lines
32 KiB
JavaScript
Raw Normal View History

import React, { Component } from "react";
import { withTranslation} from "react-i18next";
import {TextField,Chip, Tooltip, IconButton, Badge, Tab, Tabs, Alert, Paper, CircularProgress, Button , Grid, Typography, List, ListItem, ListItemIcon, ListItemText, ListItemAvatar, Avatar, Divider, Box, LinearProgress, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle} from "@mui/material"
import Countdown, { zeroPad, calcTimeDelta } from 'react-countdown';
2022-01-27 22:51:57 +00:00
import MediaQuery from 'react-responsive'
import currencyDict from '../../static/assets/currencies.json';
import { Link as LinkRouter } from 'react-router-dom'
2022-01-27 22:51:57 +00:00
import PaymentText from './PaymentText'
import TradeBox from "./TradeBox";
import getFlags from './getFlags'
import { t } from "i18next";
2022-01-14 12:00:53 +00:00
// icons
import AccessTimeIcon from '@mui/icons-material/AccessTime';
import NumbersIcon from '@mui/icons-material/Numbers';
import PriceChangeIcon from '@mui/icons-material/PriceChange';
import PaymentsIcon from '@mui/icons-material/Payments';
import ArticleIcon from '@mui/icons-material/Article';
import SendReceiveIcon from "./icons/SendReceiveIcon";
import HourglassTopIcon from '@mui/icons-material/HourglassTop';
import ContentCopy from "@mui/icons-material/ContentCopy";
2022-01-08 15:34:09 +00:00
import { getCookie } from "../utils/cookies";
import { pn } from "../utils/prettyNumbers";
class OrderPage extends Component {
constructor(props) {
super(props);
this.state = {
is_explicit: false,
delay: 60000, // Refresh every 60 seconds by default
total_secs_exp: 300,
2022-01-14 14:19:25 +00:00
loading: true,
openCancel: false,
2022-01-23 19:02:25 +00:00
openCollaborativeCancel: false,
openInactiveMaker: false,
2022-01-29 14:42:54 +00:00
showContractBox: 1,
orderId: this.props.match.params.orderId,
};
this.getOrderDetails(this.props.match.params.orderId);
2022-01-23 19:02:25 +00:00
// Refresh delays according to Order status
this.statusToDelay = {
"0": 2000, //'Waiting for maker bond'
"1": 25000, //'Public'
"2": 90000, //'Paused'
"3": 2000, //'Waiting for taker bond'
"4": 999999, //'Cancelled'
"5": 999999, //'Expired'
"6": 6000, //'Waiting for trade collateral and buyer invoice'
"7": 8000, //'Waiting only for seller trade collateral'
2022-01-23 20:23:25 +00:00
"8": 8000, //'Waiting only for buyer invoice'
"9": 10000, //'Sending fiat - In chatroom'
"10": 10000, //'Fiat sent - In chatroom'
"11": 30000, //'In dispute'
"12": 999999, //'Collaboratively cancelled'
"13": 3000, //'Sending satoshis to buyer'
"14": 999999, //'Sucessful trade'
"15": 10000, //'Failed lightning network routing'
"16": 180000, //'Wait for dispute resolution'
"17": 180000, //'Maker lost dispute'
"18": 180000, //'Taker lost dispute'
}
}
completeSetState=(newStateVars)=>{
2022-01-23 20:23:25 +00:00
// In case the reply only has "bad_request"
// Do not substitute these two for "undefined" as
// otherStateVars will fail to assign values
if (newStateVars.currency == null){
newStateVars.currency = this.state.currency
newStateVars.amount = this.state.amount
2022-01-23 20:23:25 +00:00
newStateVars.status = this.state.status
}
var otherStateVars = {
amount: newStateVars.amount ? newStateVars.amount : null,
loading: false,
delay: this.setDelay(newStateVars.status),
currencyCode: this.getCurrencyCode(newStateVars.currency),
penalty: newStateVars.penalty, // in case penalty time has finished, it goes back to null
invoice_expired: newStateVars.invoice_expired // in case invoice had expired, it goes back to null when it is valid again
};
var completeStateVars = Object.assign({}, newStateVars, otherStateVars);
this.setState(completeStateVars);
}
2022-01-23 19:02:25 +00:00
getOrderDetails =(id)=> {
2022-01-09 01:23:13 +00:00
this.setState(null)
this.setState({orderId:id})
fetch('/api/order' + '?order_id=' + id)
.then((response) => response.json())
.then((data) => (this.completeSetState(data) & this.setState({pauseLoading:false})));
}
2022-01-09 01:23:13 +00:00
// These are used to refresh the data
componentDidMount() {
this.interval = setInterval(this.tick, this.state.delay);
}
componentDidUpdate() {
clearInterval(this.interval);
2022-01-09 01:23:13 +00:00
this.interval = setInterval(this.tick, this.state.delay);
}
2022-01-09 01:23:13 +00:00
componentWillUnmount() {
clearInterval(this.interval);
}
tick = () => {
this.getOrderDetails(this.state.orderId);
2022-01-09 01:23:13 +00:00
}
// Countdown Renderer callback with condition
countdownRenderer = ({ total, hours, minutes, seconds, completed }) => {
const { t } = this.props;
if (completed) {
// Render a completed state
return (<span> {t("The order has expired")}</span>);
} else {
var col = 'inherit'
var fraction_left = (total/1000) / this.state.total_secs_exp
2022-01-14 12:00:53 +00:00
// Make orange at 25% of time left
if (fraction_left < 0.25){col = 'orange'}
// Make red at 10% of time left
if (fraction_left < 0.1){col = 'red'}
2022-01-14 12:00:53 +00:00
// Render a countdown, bold when less than 25%
return (
fraction_left < 0.25 ? <b><span style={{color:col}}>{hours}h {zeroPad(minutes)}m {zeroPad(seconds)}s </span></b>
:<span style={{color:col}}>{hours}h {zeroPad(minutes)}m {zeroPad(seconds)}s </span>
);
}
};
timerRenderer(seconds){
var hours = parseInt(seconds/3600);
var minutes = parseInt((seconds-hours*3600)/60);
return(
<span>{hours>0 ? hours+"h":""} {minutes>0 ? zeroPad(minutes)+"m":""} </span>
)
}
// Countdown Renderer callback with condition
countdownPenaltyRenderer = ({ minutes, seconds, completed }) => {
const { t } = this.props;
if (completed) {
// Render a completed state
return (<span> {t("Penalty lifted, good to go!")}</span>);
} else {
return (
<span> {t("You cannot take an order yet! Wait {{timeMin}}m {{timeSec}}s",{timeMin: zeroPad(minutes), timeSec: zeroPad(seconds) })} </span>
);
}
};
handleTakeAmountChange = (e) => {
if (e.target.value != "" & e.target.value != null){
this.setState({takeAmount: parseFloat(e.target.value)})
}else{
this.setState({takeAmount: e.target.value})
}
}
amountHelperText=()=>{
const { t } = this.props;
if(this.state.takeAmount < this.state.min_amount & this.state.takeAmount != ""){
return t("Too low")
}else if (this.state.takeAmount > this.state.max_amount & this.state.takeAmount != ""){
return t("Too high")
}else{
return null
}
}
takeOrderButton = () => {
const { t } = this.props;
if(this.state.has_range){
return(
<Grid container xs={12} align="center" alignItems="stretch" justifyContent="center" style={{ display: "flex"}}>
<this.InactiveMakerDialog/>
<this.StoreTokenDialog/>
<div style={{maxWidth:120}}>
2022-05-02 19:28:34 +00:00
<Tooltip placement="top" enterTouchDelay={500} enterDelay={700} enterNextDelay={2000} title={t("Enter amount of fiat to exchange for bitcoin")}>
2022-03-24 15:43:31 +00:00
<Paper elevation={5} sx={{maxHeight:40}}>
<TextField
error={(this.state.takeAmount < this.state.min_amount || this.state.takeAmount > this.state.max_amount) & this.state.takeAmount != "" }
helperText={this.amountHelperText()}
label={t("Amount {{currencyCode}}", {currencyCode: this.state.currencyCode})}
size="small"
type="number"
2022-05-02 19:28:34 +00:00
required={true}
value={this.state.takeAmount}
inputProps={{
min:this.state.min_amount ,
max:this.state.max_amount ,
style: {textAlign:"center"}
}}
onChange={this.handleTakeAmountChange}
/>
</Paper>
</Tooltip>
</div>
2022-03-24 15:43:31 +00:00
<div style={{height:38, top:'1px', position:'relative', display: (this.state.takeAmount < this.state.min_amount || this.state.takeAmount > this.state.max_amount || this.state.takeAmount == "" || this.state.takeAmount == null) ? '':'none'}}>
2022-05-02 19:28:34 +00:00
<Tooltip placement="top" enterTouchDelay={0} enterDelay={500} enterNextDelay={1200} title={t("You must specify an amount first")}>
2022-03-24 15:43:31 +00:00
<Paper elevation={4}>
<Button sx={{height:38}} variant='contained' color='primary'
2022-03-24 15:43:31 +00:00
disabled={true}>
{t("Take Order")}
2022-03-24 15:43:31 +00:00
</Button>
</Paper>
</Tooltip>
</div>
<div style={{height:38, top:'1px', position:'relative', display: (this.state.takeAmount < this.state.min_amount || this.state.takeAmount > this.state.max_amount || this.state.takeAmount == "" || this.state.takeAmount == null) ? 'none':''}}>
<Paper elevation={4}>
<Button sx={{height:38}} variant='contained' color='primary'
onClick={this.props.copiedToken ? (this.state.maker_status=='Inactive' ? this.handleClickOpenInactiveMakerDialog : this.takeOrder) : (() => this.setState({openStoreToken:true}))}>
{t("Take Order")}
2022-03-24 15:43:31 +00:00
</Button>
</Paper>
</div>
</Grid>
)
}else{
return(
<>
<this.InactiveMakerDialog/>
<this.StoreTokenDialog/>
<Button sx={{height:38}} variant='contained' color='primary'
onClick={this.props.copiedToken ? (this.state.maker_status=='Inactive' ? this.handleClickOpenInactiveMakerDialog : this.takeOrder) : (() => this.setState({openStoreToken:true}))}>
{t("Take Order")}
</Button>
</>
)
}
}
countdownTakeOrderRenderer = ({ seconds, completed }) => {
if(isNaN(seconds)){return (<this.takeOrderButton/>)}
if (completed) {
// Render a completed state
return ( <this.takeOrderButton/>);
} else{
return(
2022-05-02 19:28:34 +00:00
<Tooltip enterTouchDelay={0} title={t("Wait until you can take an order")}><div>
<Button disabled={true} variant='contained' color='primary'>{t("Take Order")}</Button>
</div></Tooltip>)
}
};
LinearDeterminate =()=> {
const [progress, setProgress] = React.useState(0);
React.useEffect(() => {
const timer = setInterval(() => {
setProgress((oldProgress) => {
var left = calcTimeDelta( new Date(this.state.expires_at)).total /1000;
return (left / this.state.total_secs_exp) * 100;
});
}, 1000);
return () => {
clearInterval(timer);
};
}, []);
return (
<Box sx={{ width: '100%' }}>
<LinearProgress variant="determinate" value={progress} />
</Box>
);
}
takeOrder=()=>{
this.setState({loading:true})
const requestOptions = {
method: 'POST',
headers: {'Content-Type':'application/json', 'X-CSRFToken': getCookie('csrftoken'),},
body: JSON.stringify({
'action':'take',
'amount':this.state.takeAmount,
}),
};
fetch('/api/order/' + '?order_id=' + this.state.orderId, requestOptions)
.then((response) => response.json())
.then((data) => this.completeSetState(data));
}
2022-01-15 15:12:26 +00:00
// set delay to the one matching the order status. If null order status, delay goes to 9999999.
setDelay = (status)=>{
return status >= 0 ? this.statusToDelay[status.toString()] : 99999999;
}
getCurrencyCode(val){
let code = val ? currencyDict[val.toString()] : ""
2022-01-09 15:28:12 +00:00
return code
}
handleClickConfirmCancelButton=()=>{
this.setState({loading:true})
const requestOptions = {
method: 'POST',
headers: {'Content-Type':'application/json', 'X-CSRFToken': getCookie('csrftoken'),},
body: JSON.stringify({
'action':'cancel',
}),
};
fetch('/api/order/' + '?order_id=' + this.state.orderId, requestOptions)
.then((response) => response.json())
.then(() => (this.getOrderDetails(this.state.orderId) & this.setState({status:4})));
this.handleClickCloseConfirmCancelDialog();
}
handleClickOpenConfirmCancelDialog = () => {
this.setState({openCancel: true});
};
handleClickCloseConfirmCancelDialog = () => {
this.setState({openCancel: false});
};
CancelDialog =() =>{
const { t } = this.props;
return(
<Dialog
open={this.state.openCancel}
onClose={this.handleClickCloseConfirmCancelDialog}
aria-labelledby="cancel-dialog-title"
aria-describedby="cancel-dialog-description"
>
<DialogTitle id="cancel-dialog-title">
{t("Cancel the order?")}
</DialogTitle>
<DialogContent>
<DialogContentText id="cancel-dialog-description">
{t("If the order is cancelled now you will lose your bond.")}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={this.handleClickCloseConfirmCancelDialog} autoFocus>{t("Go back")}</Button>
<Button onClick={this.handleClickConfirmCancelButton}>{t("Confirm Cancel")}</Button>
</DialogActions>
</Dialog>
)
}
handleClickOpenInactiveMakerDialog = () => {
this.setState({openInactiveMaker: true});
};
handleClickCloseInactiveMakerDialog = () => {
this.setState({openInactiveMaker: false});
};
InactiveMakerDialog =() =>{
const { t } = this.props;
return(
<Dialog
open={this.state.openInactiveMaker}
onClose={this.handleClickCloseInactiveMakerDialog}
aria-labelledby="inactive-maker-dialog-title"
aria-describedby="inactive-maker-description"
>
<DialogTitle id="inactive-maker-dialog-title">
{t("The maker is away")}
</DialogTitle>
<DialogContent>
<DialogContentText id="cancel-dialog-description">
{t("By taking this order you risk wasting your time. If the maker does not proceed in time, you will be compensated in satoshis for 50% of the maker bond.")}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={this.handleClickCloseInactiveMakerDialog} autoFocus>{t("Go back")}</Button>
<Button onClick={this.takeOrder}>{t("Take Order")}</Button>
</DialogActions>
</Dialog>
)
}
StoreTokenDialog = () =>{
const { t } = this.props;
// If there is a robot cookie, prompt user to store it
// Else, prompt user to generate a robot
if (getCookie("robot_token")){
return(
<Dialog
open={this.state.openStoreToken}
onClose={() => this.setState({openStoreToken:false})}
>
<DialogTitle >
{t("Store your robot token")}
</DialogTitle>
<DialogContent>
<DialogContentText>
{t("You might need to recover your robot avatar in the future: store it safely. You can simply copy it into another application.")}
</DialogContentText>
<br/>
<Grid align="center">
<TextField
sx={{width:"100%", maxWidth:"550px"}}
disabled
label={t("Back it up!")}
value={getCookie("robot_token") }
variant='filled'
size='small'
InputProps={{
endAdornment:
<Tooltip disableHoverListener enterTouchDelay="0" title={t("Copied!")}>
<IconButton onClick= {()=> (navigator.clipboard.writeText(getCookie("robot_token")) & this.props.setAppState({copiedToken:true}))}>
<ContentCopy color={this.props.copiedToken ? "inherit" : "primary"}/>
</IconButton>
</Tooltip>,
}}
/>
</Grid>
</DialogContent>
<DialogActions>
<Button onClick={() => this.setState({openStoreToken:false})} autoFocus>{t("Go back")}</Button>
<Button onClick={() => this.setState({openStoreToken:false}) & (this.state.maker_status=='Inactive' ? this.handleClickOpenInactiveMakerDialog() : this.takeOrder())}>{t("Done")}</Button>
</DialogActions>
</Dialog>
)
}else{
return(
<Dialog
open={this.state.openStoreToken}
onClose={() => this.setState({openStoreToken:false})}
>
<DialogTitle>
{t("You do not have a robot avatar")}
</DialogTitle>
<DialogContent>
<DialogContentText>
{t("You need to generate a robot avatar in order to become an order maker")}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={() => this.setState({openStoreToken:false})} autoFocus>{t("Go back")}</Button>
<Button onClick={() => this.setState({openStoreToken:false})} to="/" component={LinkRouter}>{t("Generate Robot")}</Button>
</DialogActions>
</Dialog>
)
}
}
2022-01-23 19:02:25 +00:00
handleClickConfirmCollaborativeCancelButton=()=>{
const requestOptions = {
method: 'POST',
headers: {'Content-Type':'application/json', 'X-CSRFToken': getCookie('csrftoken'),},
body: JSON.stringify({
'action':'cancel',
}),
};
2022-05-03 22:51:52 +00:00
fetch('/api/order/' + '?order_id=' + this.state.orderId, requestOptions)
2022-01-23 19:02:25 +00:00
.then((response) => response.json())
.then(() => (this.getOrderDetails(this.state.orderId) & this.setState({status:4})));
2022-01-23 19:02:25 +00:00
this.handleClickCloseCollaborativeCancelDialog();
}
handleClickOpenCollaborativeCancelDialog = () => {
this.setState({openCollaborativeCancel: true});
};
handleClickCloseCollaborativeCancelDialog = () => {
this.setState({openCollaborativeCancel: false});
};
CollaborativeCancelDialog =() =>{
const { t } = this.props;
2022-01-23 19:02:25 +00:00
return(
<Dialog
open={this.state.openCollaborativeCancel}
onClose={this.handleClickCloseCollaborativeCancelDialog}
aria-labelledby="collaborative-cancel-dialog-title"
aria-describedby="collaborative-cancel-dialog-description"
>
<DialogTitle id="cancel-dialog-title">
{t("Collaborative cancel the order?")}
2022-01-23 19:02:25 +00:00
</DialogTitle>
<DialogContent>
<DialogContentText id="cancel-dialog-description">
{t("The trade escrow has been posted. The order can be cancelled only if both, maker and taker, agree to cancel.")}
2022-01-23 19:02:25 +00:00
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={this.handleClickCloseCollaborativeCancelDialog} autoFocus>{t("Go back")}</Button>
<Button onClick={this.handleClickConfirmCollaborativeCancelButton}>{t("Ask for Cancel")}</Button>
2022-01-23 19:02:25 +00:00
</DialogActions>
</Dialog>
)
}
BackButton = () => {
const { t } = this.props;
// If order has expired, show back button.
if (this.state.status == 5){
return(
<Grid item xs={12} align="center">
<Button variant='contained' color='secondary' onClick={this.props.history.goBack}>{t("Back")}</Button>
</Grid>
)}
return(null)
}
CancelButton = () => {
const { t } = this.props;
// If maker and Waiting for Bond. Or if taker and Waiting for bond.
// Simply allow to cancel without showing the cancel dialog.
if ((this.state.is_maker & [0,1,2].includes(this.state.status)) || this.state.is_taker & this.state.status == 3){
return(
<Grid item xs={12} align="center">
<Button variant='contained' color='secondary' onClick={this.handleClickConfirmCancelButton}>{t("Cancel")}</Button>
</Grid>
)}
// If the order does not yet have an escrow deposited. Show dialog
// to confirm forfeiting the bond
if ([3,6,7].includes(this.state.status)){
2022-01-23 19:02:25 +00:00
return(
<div id="openDialogCancelButton">
<Grid item xs={12} align="center">
<this.CancelDialog/>
<Button variant='contained' color='secondary' onClick={this.handleClickOpenConfirmCancelDialog}>{t("Cancel")}</Button>
2022-01-23 19:02:25 +00:00
</Grid>
</div>
)}
2022-01-23 19:02:25 +00:00
// If the escrow is Locked, show the collaborative cancel button.
2022-01-23 19:02:25 +00:00
if ([8,9].includes(this.state.status)){
return(
<Grid item xs={12} align="center">
2022-01-23 19:02:25 +00:00
<this.CollaborativeCancelDialog/>
<Button variant='contained' color='secondary' onClick={this.handleClickOpenCollaborativeCancelDialog}>{t("Collaborative Cancel")}</Button>
</Grid>
)}
2022-01-23 19:02:25 +00:00
// If none of the above do not return a cancel button.
return(null)
2022-01-08 15:34:09 +00:00
}
// Colors for the status badges
statusBadgeColor(status){
2022-02-03 18:06:30 +00:00
if(status=='Active'){return("success")}
if(status=='Seen recently'){return("warning")}
if(status=='Inactive'){return('error')}
}
orderBox=()=>{
const { t } = this.props;
return(
2022-01-27 22:51:57 +00:00
<Grid container spacing={1} >
<Grid item xs={12} align="center">
2022-01-27 22:51:57 +00:00
<MediaQuery minWidth={920}>
<Typography component="h5" variant="h5">
{t("Order Box")}
2022-01-27 22:51:57 +00:00
</Typography>
</MediaQuery>
<Paper elevation={12} style={{ padding: 8,}}>
<List dense="true">
2022-01-05 02:03:03 +00:00
<ListItem >
<ListItemAvatar sx={{ width: 56, height: 56 }}>
2022-05-02 19:28:34 +00:00
<Tooltip placement="top" enterTouchDelay={0} title={t(this.state.maker_status)} >
2022-02-03 18:06:30 +00:00
<Badge variant="dot" overlap="circular" badgeContent="" color={this.statusBadgeColor(this.state.maker_status)}>
<Badge overlap="circular" anchorOrigin={{horizontal: 'right', vertical: 'bottom'}} badgeContent={<div style={{position:"relative", left:"12px", top:"4px"}}> {!this.state.type ? <SendReceiveIcon sx={{transform: "scaleX(-1)"}} color="secondary"/> : <SendReceiveIcon color="primary"/>}</div>}>
<Avatar className="flippedSmallAvatar"
alt={this.state.maker_nick}
src={window.location.origin +'/static/assets/avatars/' + this.state.maker_nick + '.png'}
/>
</Badge>
</Badge>
</Tooltip>
</ListItemAvatar>
<ListItemText primary={this.state.maker_nick + (this.state.type ? " "+t("(Seller)") : " "+t("(Buyer)") )} secondary={t("Order maker")} align="right"/>
</ListItem>
2022-01-05 02:03:03 +00:00
{this.state.is_participant ?
2022-01-05 02:03:03 +00:00
<>
{this.state.taker_nick!='None' ?
2022-01-05 02:03:03 +00:00
<>
<Divider />
2022-01-05 02:03:03 +00:00
<ListItem align="left">
<ListItemText primary={this.state.taker_nick + (this.state.type ? " "+t("(Buyer)") : " "+t("(Seller)"))} secondary={t("Order taker")}/>
<ListItemAvatar >
2022-05-02 19:28:34 +00:00
<Tooltip enterTouchDelay={0} title={t(this.state.taker_status)} >
<Badge variant="dot" overlap="circular" badgeContent="" color={this.statusBadgeColor(this.state.taker_status)}>
<Badge overlap="circular" anchorOrigin={{horizontal: 'left', vertical: 'bottom'}} badgeContent={<div style={{position:"relative", right:"12px", top:"4px"}}> {this.state.type ? <SendReceiveIcon color="secondary"/> : <SendReceiveIcon sx={{transform: "scaleX(-1)"}} color="primary"/> }</div>}>
<Avatar className="smallAvatar"
alt={this.state.taker_nick}
src={window.location.origin +'/static/assets/avatars/' + this.state.taker_nick + '.png'}
/>
</Badge>
</Badge>
</Tooltip>
2022-01-05 02:03:03 +00:00
</ListItemAvatar>
</ListItem>
</>:
""
2022-01-05 02:03:03 +00:00
}
<Divider><Chip label={t("Order Details")}/></Divider>
<ListItem>
2022-01-14 12:00:53 +00:00
<ListItemIcon>
<ArticleIcon/>
</ListItemIcon>
<ListItemText primary={t(this.state.status_message)} secondary={t("Order status")}/>
</ListItem>
<Divider/>
2022-01-05 02:03:03 +00:00
</>
:<Divider><Chip label={t("Order Details")}/></Divider>
2022-01-05 02:03:03 +00:00
}
<ListItem>
2022-01-14 12:00:53 +00:00
<ListItemIcon>
<div style={{zoom:1.25,opacity: 0.7, '-ms-zoom': 1.25, '-webkit-zoom': 1.25,'-moz-transform': 'scale(1.25,1.25)', '-moz-transform-origin': 'left center'}}>
{getFlags(this.state.currencyCode)}
</div>
2022-01-14 12:00:53 +00:00
</ListItemIcon>
{this.state.has_range & this.state.amount == null ?
<ListItemText primary={pn(parseFloat(Number(this.state.min_amount).toPrecision(4)))
+"-" + pn(parseFloat(Number(this.state.max_amount).toPrecision(4))) +" "+this.state.currencyCode} secondary={t("Amount range")}/>
:
<ListItemText primary={pn(parseFloat(parseFloat(this.state.amount).toFixed(4)))
+" "+this.state.currencyCode} secondary={t("Amount")}/>
}
</ListItem>
<Divider />
<ListItem>
2022-01-14 12:00:53 +00:00
<ListItemIcon>
<PaymentsIcon/>
</ListItemIcon>
<ListItemText primary={<PaymentText size={20} othersText={t("Others")} verbose={true} text={this.state.payment_method}/>} secondary={this.state.currency==1000 ? t("Swap destination"):t("Accepted payment methods")}/>
</ListItem>
<Divider />
2022-01-14 12:00:53 +00:00
{/* If there is live Price and Premium data, show it. Otherwise show the order maker settings */}
<ListItem>
2022-01-14 12:00:53 +00:00
<ListItemIcon>
<PriceChangeIcon/>
</ListItemIcon>
{this.state.price_now?
<ListItemText primary={t("{{price}} {{currencyCode}}/BTC - Premium: {{premium}}%", {price: pn(this.state.price_now), currencyCode:this.state.currencyCode, premium: this.state.premium_now})} secondary={t("Price and Premium")}/>
2022-01-14 12:00:53 +00:00
:
(this.state.is_explicit ?
<ListItemText primary={pn(this.state.satoshis)} secondary={t("Amount of Satoshis")}/>
2022-01-14 12:00:53 +00:00
:
<ListItemText primary={parseFloat(parseFloat(this.state.premium).toFixed(2))+"%"} secondary={t("Premium over market price")}/>
2022-01-14 12:00:53 +00:00
)
}
</ListItem>
<Divider />
2022-01-05 02:03:03 +00:00
<ListItem >
<ListItemIcon>
2022-01-14 12:00:53 +00:00
<NumbersIcon/>
</ListItemIcon>
<Grid container xs={12}>
<Grid item xs={4.5}>
<ListItemText primary={this.state.orderId} secondary={t("Order ID")}/>
</Grid>
<Grid item xs={7.5}>
<Grid container>
<Grid item xs={2}>
<ListItemIcon sx={{position:"relative",top:"12px",left:"-5px"}}><HourglassTopIcon/></ListItemIcon>
</Grid>
<Grid item xs={10}>
<ListItemText
primary={this.timerRenderer(this.state.escrow_duration)}
secondary={t("Deposit timer")}>
</ListItemText>
</Grid>
</Grid>
</Grid>
</Grid>
</ListItem>
2022-01-05 02:03:03 +00:00
<Divider />
<ListItem>
2022-01-14 12:00:53 +00:00
<ListItemIcon>
<AccessTimeIcon/>
</ListItemIcon>
<ListItemText secondary={t("Expires in")}>
<Countdown date={new Date(this.state.expires_at)} renderer={this.countdownRenderer} />
</ListItemText>
2022-01-05 02:03:03 +00:00
</ListItem>
<this.LinearDeterminate />
</List>
2022-01-10 12:10:32 +00:00
{/* If the user has a penalty/limit */}
{this.state.penalty ?
2022-01-10 12:10:32 +00:00
<>
<Divider />
<Grid item xs={12} align="center">
<Alert severity="warning" sx={{maxWidth:360}}>
<Countdown date={new Date(this.state.penalty)} renderer={this.countdownPenaltyRenderer} />
</Alert>
2022-01-10 12:10:32 +00:00
</Grid>
</>
: null}
2022-01-23 19:02:25 +00:00
{/* If the counterparty asked for collaborative cancel */}
{this.state.pending_cancel ?
2022-01-23 19:02:25 +00:00
<>
<Divider />
<Grid item xs={12} align="center">
<Alert severity="warning" sx={{maxWidth:360}}>
{t("{{nickname}} is asking for a collaborative cancel", {nickname: this.state.is_maker ? this.state.taker_nick : this.state.maker_nick})}
</Alert>
2022-01-23 19:02:25 +00:00
</Grid>
</>
: null}
2022-01-23 19:02:25 +00:00
{/* If the user has asked for a collaborative cancel */}
{this.state.asked_for_cancel ?
2022-01-23 19:02:25 +00:00
<>
<Divider />
<Grid item xs={12} align="center">
<Alert severity="warning" sx={{maxWidth:360}}>
{t("You asked for a collaborative cancellation")}
</Alert>
2022-01-23 19:02:25 +00:00
</Grid>
</>
: null}
2022-01-05 02:03:03 +00:00
</Paper>
</Grid>
2022-01-23 19:02:25 +00:00
<Grid item xs={12} align="center">
{/* Participants can see the "Cancel" Button, but cannot see the "Back" or "Take Order" buttons */}
{this.state.is_participant ?
<>
<this.CancelButton/>
<this.BackButton/>
</>
2022-01-23 19:02:25 +00:00
:
<Grid container spacing={1}>
<Grid item xs={12} align="center">
<Countdown date={new Date(this.state.penalty)} renderer={this.countdownTakeOrderRenderer} />
2022-01-23 19:02:25 +00:00
</Grid>
<Grid item xs={12} align="center">
<Button variant='contained' color='secondary' onClick={this.props.history.goBack}>{t("Back")}</Button>
2022-01-23 19:02:25 +00:00
</Grid>
</Grid>
2022-01-23 19:02:25 +00:00
}
</Grid>
</Grid>
)
}
2022-01-27 22:51:57 +00:00
doubleOrderPageDesktop=()=>{
return(
<Grid container xs={12} align="center" spacing={2} >
<Grid item xs={6} align="left" style={{ width:330}} >
{this.orderBox()}
</Grid>
<Grid item xs={6} align="left">
<TradeBox push={this.props.history.push} getOrderDetails={this.getOrderDetails} pauseLoading={this.state.pauseLoading} width={330} data={this.state} completeSetState={this.completeSetState} />
2022-01-27 22:51:57 +00:00
</Grid>
</Grid>
)
}
2022-01-29 14:42:54 +00:00
a11yProps(index) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
};
}
2022-01-27 22:51:57 +00:00
doubleOrderPagePhone=()=>{
const { t } = this.props;
2022-01-29 14:42:54 +00:00
const [value, setValue] = React.useState(this.state.showContractBox);
2022-01-27 22:51:57 +00:00
const handleChange = (event, newValue) => {
2022-01-29 14:42:54 +00:00
this.setState({showContractBox:newValue})
2022-01-27 22:51:57 +00:00
setValue(newValue);
};
return(
<Box sx={{ width: '100%'}}>
2022-01-27 22:51:57 +00:00
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
2022-01-29 14:42:54 +00:00
<Tabs value={value} onChange={handleChange} variant="fullWidth" >
<Tab label={t("Order")} {...this.a11yProps(0)} />
<Tab label={t("Contract")} {...this.a11yProps(1)} />
2022-01-27 22:51:57 +00:00
</Tabs>
</Box>
2022-01-29 14:42:54 +00:00
<Grid container spacing={2}>
<Grid item >
<div style={{ width:330, display: this.state.showContractBox == 0 ? '':'none'}}>
{this.orderBox()}
</div>
<div style={{display: this.state.showContractBox == 1 ? '':'none'}}>
<TradeBox push={this.props.history.push} getOrderDetails={this.getOrderDetails} pauseLoading={this.state.pauseLoading} width={330} data={this.state} completeSetState={this.completeSetState} />
2022-01-29 14:42:54 +00:00
</div>
2022-01-27 22:51:57 +00:00
</Grid>
2022-01-29 14:42:54 +00:00
</Grid>
2022-01-27 22:51:57 +00:00
</Box>
);
}
2022-01-09 20:05:19 +00:00
orderDetailsPage (){
const { t } = this.props;
2022-01-09 20:05:19 +00:00
return(
this.state.bad_request ?
<div align='center'>
<Typography component="subtitle2" variant="subtitle2" color="secondary" >
{/* IMPLEMENT I18N for bad_request */}
{t(this.state.bad_request)}<br/>
</Typography>
<Button variant='contained' color='secondary' onClick={this.props.history.goBack}>{t("Back")}</Button>
</div>
:
(this.state.is_participant ?
2022-01-27 22:51:57 +00:00
<>
{/* Desktop View */}
<MediaQuery minWidth={920}>
<this.doubleOrderPageDesktop/>
</MediaQuery>
{/* SmarPhone View */}
<MediaQuery maxWidth={919}>
<this.doubleOrderPagePhone/>
</MediaQuery>
</>
:
2022-01-27 22:51:57 +00:00
<Grid item xs={12} align="center" style={{ width:330}}>
{this.orderBox()}
</Grid>)
2022-01-09 20:05:19 +00:00
)
}
render (){
return (
2022-01-09 20:05:19 +00:00
// Only so nothing shows while requesting the first batch of data
2022-01-14 14:19:25 +00:00
this.state.loading ? <CircularProgress /> : this.orderDetailsPage()
);
}
}
export default withTranslation()(OrderPage);