2022-01-02 00:19:18 +00:00
|
|
|
import React, { Component } from "react";
|
2022-01-10 12:10:32 +00:00
|
|
|
import { Alert, Paper, CircularProgress, Button , Grid, Typography, List, ListItem, ListItemText, ListItemAvatar, Avatar, Divider, Box, LinearProgress} from "@mui/material"
|
2022-01-08 13:08:03 +00:00
|
|
|
import TradeBox from "./TradeBox";
|
2022-01-02 00:19:18 +00:00
|
|
|
|
2022-01-05 02:03:03 +00:00
|
|
|
function msToTime(duration) {
|
|
|
|
var seconds = Math.floor((duration / 1000) % 60),
|
|
|
|
minutes = Math.floor((duration / (1000 * 60)) % 60),
|
|
|
|
hours = Math.floor((duration / (1000 * 60 * 60)) % 24);
|
|
|
|
|
|
|
|
minutes = (minutes < 10) ? "0" + minutes : minutes;
|
|
|
|
seconds = (seconds < 10) ? "0" + seconds : seconds;
|
|
|
|
|
|
|
|
return hours + "h " + minutes + "m " + seconds + "s";
|
|
|
|
}
|
|
|
|
|
2022-01-08 15:34:09 +00:00
|
|
|
// TO DO fix Progress bar to go from 100 to 0, from total_expiration time, showing time_left
|
|
|
|
function LinearDeterminate() {
|
|
|
|
const [progress, setProgress] = React.useState(0);
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
const timer = setInterval(() => {
|
|
|
|
setProgress((oldProgress) => {
|
|
|
|
if (oldProgress === 0) {
|
|
|
|
return 100;
|
|
|
|
}
|
|
|
|
const diff = 1;
|
|
|
|
return Math.max(oldProgress - diff, 0);
|
|
|
|
});
|
|
|
|
}, 500);
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
clearInterval(timer);
|
|
|
|
};
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Box sx={{ width: '100%' }}>
|
|
|
|
<LinearProgress variant="determinate" value={progress} />
|
|
|
|
</Box>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-01-05 00:13:08 +00:00
|
|
|
function getCookie(name) {
|
|
|
|
let cookieValue = null;
|
|
|
|
if (document.cookie && document.cookie !== '') {
|
|
|
|
const cookies = document.cookie.split(';');
|
|
|
|
for (let i = 0; i < cookies.length; i++) {
|
|
|
|
const cookie = cookies[i].trim();
|
|
|
|
// Does this cookie string begin with the name we want?
|
|
|
|
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
|
|
|
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cookieValue;
|
|
|
|
}
|
|
|
|
const csrftoken = getCookie('csrftoken');
|
|
|
|
|
2022-01-03 14:27:25 +00:00
|
|
|
// pretty numbers
|
|
|
|
function pn(x) {
|
|
|
|
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
|
|
}
|
|
|
|
|
2022-01-02 00:19:18 +00:00
|
|
|
export default class OrderPage extends Component {
|
2022-01-02 12:59:48 +00:00
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
this.state = {
|
2022-01-02 13:35:31 +00:00
|
|
|
isExplicit: false,
|
2022-01-11 20:49:53 +00:00
|
|
|
delay: 2000, // Refresh every 2 seconds by default
|
2022-01-08 12:48:03 +00:00
|
|
|
currencies_dict: {"1":"USD"}
|
2022-01-02 12:59:48 +00:00
|
|
|
};
|
|
|
|
this.orderId = this.props.match.params.orderId;
|
2022-01-08 12:48:03 +00:00
|
|
|
this.getCurrencyDict();
|
2022-01-02 13:24:35 +00:00
|
|
|
this.getOrderDetails();
|
2022-01-02 12:59:48 +00:00
|
|
|
}
|
2022-01-02 00:19:18 +00:00
|
|
|
|
2022-01-02 13:24:35 +00:00
|
|
|
getOrderDetails() {
|
2022-01-09 01:23:13 +00:00
|
|
|
this.setState(null)
|
2022-01-02 13:24:35 +00:00
|
|
|
fetch('/api/order' + '?order_id=' + this.orderId)
|
2022-01-02 12:59:48 +00:00
|
|
|
.then((response) => response.json())
|
2022-01-09 14:07:05 +00:00
|
|
|
.then((data) => {console.log(data) &
|
2022-01-02 12:59:48 +00:00
|
|
|
this.setState({
|
2022-01-09 14:07:05 +00:00
|
|
|
id: data.id,
|
2022-01-03 12:11:33 +00:00
|
|
|
statusCode: data.status,
|
|
|
|
statusText: data.status_message,
|
2022-01-02 12:59:48 +00:00
|
|
|
type: data.type,
|
|
|
|
currency: data.currency,
|
2022-01-05 00:13:08 +00:00
|
|
|
currencyCode: this.getCurrencyCode(data.currency),
|
2022-01-02 12:59:48 +00:00
|
|
|
amount: data.amount,
|
|
|
|
paymentMethod: data.payment_method,
|
2022-01-02 13:35:31 +00:00
|
|
|
isExplicit: data.is_explicit,
|
2022-01-03 12:11:33 +00:00
|
|
|
premium: data.premium,
|
|
|
|
satoshis: data.satoshis,
|
|
|
|
makerId: data.maker,
|
|
|
|
isParticipant: data.is_participant,
|
|
|
|
makerNick: data.maker_nick,
|
|
|
|
takerId: data.taker,
|
|
|
|
takerNick: data.taker_nick,
|
2022-01-08 13:08:03 +00:00
|
|
|
isMaker: data.is_maker,
|
|
|
|
isTaker: data.is_taker,
|
|
|
|
isBuyer: data.is_buyer,
|
|
|
|
isSeller: data.is_seller,
|
2022-01-10 12:10:32 +00:00
|
|
|
penalty: data.penalty,
|
2022-01-08 13:08:03 +00:00
|
|
|
expiresAt: data.expires_at,
|
|
|
|
badRequest: data.bad_request,
|
|
|
|
bondInvoice: data.bond_invoice,
|
|
|
|
bondSatoshis: data.bond_satoshis,
|
2022-01-08 17:19:30 +00:00
|
|
|
escrowInvoice: data.escrow_invoice,
|
|
|
|
escrowSatoshis: data.escrow_satoshis,
|
2022-01-09 12:14:11 +00:00
|
|
|
invoiceAmount: data.invoice_amount,
|
2022-01-11 20:49:53 +00:00
|
|
|
})
|
2022-01-02 12:59:48 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
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(prevProps, prevState) {
|
|
|
|
if (prevState.delay !== this.state.delay) {
|
|
|
|
clearInterval(this.interval);
|
|
|
|
this.interval = setInterval(this.tick, this.state.delay);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
|
|
clearInterval(this.interval);
|
|
|
|
}
|
|
|
|
tick = () => {
|
|
|
|
this.getOrderDetails();
|
|
|
|
}
|
|
|
|
|
2022-01-04 15:00:34 +00:00
|
|
|
// Fix to use proper react props
|
|
|
|
handleClickBackButton=()=>{
|
|
|
|
window.history.back();
|
|
|
|
}
|
2022-01-05 00:13:08 +00:00
|
|
|
|
|
|
|
handleClickTakeOrderButton=()=>{
|
|
|
|
console.log(this.state)
|
|
|
|
const requestOptions = {
|
|
|
|
method: 'POST',
|
2022-01-06 20:33:40 +00:00
|
|
|
headers: {'Content-Type':'application/json', 'X-CSRFToken': getCookie('csrftoken'),},
|
|
|
|
body: JSON.stringify({
|
|
|
|
'action':'take',
|
|
|
|
}),
|
2022-01-05 00:13:08 +00:00
|
|
|
};
|
|
|
|
fetch('/api/order/' + '?order_id=' + this.orderId, requestOptions)
|
|
|
|
.then((response) => response.json())
|
2022-01-11 20:49:53 +00:00
|
|
|
.then((data) => (this.setState({badRequest:data.bad_request})
|
|
|
|
& console.log(data)
|
|
|
|
& this.getOrderDetails(data.id)));
|
2022-01-05 00:13:08 +00:00
|
|
|
}
|
2022-01-08 12:48:03 +00:00
|
|
|
getCurrencyDict() {
|
2022-01-09 14:29:10 +00:00
|
|
|
fetch('/static/assets/currencies.json')
|
2022-01-08 12:48:03 +00:00
|
|
|
.then((response) => response.json())
|
|
|
|
.then((data) =>
|
|
|
|
this.setState({
|
|
|
|
currencies_dict: data
|
|
|
|
}));
|
|
|
|
}
|
2022-01-09 12:35:19 +00:00
|
|
|
|
2022-01-08 12:48:03 +00:00
|
|
|
getCurrencyCode(val){
|
2022-01-09 15:28:12 +00:00
|
|
|
let code = val ? this.state.currencies_dict[val.toString()] : ""
|
|
|
|
return code
|
2022-01-08 12:48:03 +00:00
|
|
|
}
|
2022-01-05 00:13:08 +00:00
|
|
|
|
2022-01-08 15:34:09 +00:00
|
|
|
handleClickCancelOrderButton=()=>{
|
|
|
|
console.log(this.state)
|
|
|
|
const requestOptions = {
|
|
|
|
method: 'POST',
|
|
|
|
headers: {'Content-Type':'application/json', 'X-CSRFToken': getCookie('csrftoken'),},
|
|
|
|
body: JSON.stringify({
|
|
|
|
'action':'cancel',
|
|
|
|
}),
|
|
|
|
};
|
|
|
|
fetch('/api/order/' + '?order_id=' + this.orderId, requestOptions)
|
|
|
|
.then((response) => response.json())
|
|
|
|
.then((data) => (console.log(data) & this.getOrderDetails(data.id)));
|
|
|
|
}
|
|
|
|
|
2022-01-08 13:08:03 +00:00
|
|
|
orderBox=()=>{
|
|
|
|
return(
|
2022-01-03 12:11:33 +00:00
|
|
|
<Grid container spacing={1}>
|
|
|
|
<Grid item xs={12} align="center">
|
|
|
|
<Typography component="h5" variant="h5">
|
2022-01-08 20:14:19 +00:00
|
|
|
{this.state.type ? "Sell " : "Buy "} Order Details
|
2022-01-03 12:11:33 +00:00
|
|
|
</Typography>
|
2022-01-03 19:15:13 +00:00
|
|
|
<Paper elevation={12} style={{ padding: 8,}}>
|
2022-01-05 00:13:08 +00:00
|
|
|
<List dense="true">
|
2022-01-05 02:03:03 +00:00
|
|
|
<ListItem >
|
2022-01-03 12:11:33 +00:00
|
|
|
<ListItemAvatar sx={{ width: 56, height: 56 }}>
|
|
|
|
<Avatar
|
|
|
|
alt={this.state.makerNick}
|
|
|
|
src={window.location.origin +'/static/assets/avatars/' + this.state.makerNick + '.png'}
|
|
|
|
/>
|
|
|
|
</ListItemAvatar>
|
2022-01-09 14:07:05 +00:00
|
|
|
<ListItemText primary={this.state.makerNick + (this.state.type ? " (Seller)" : " (Buyer)")} secondary="Order maker" align="right"/>
|
2022-01-03 12:11:33 +00:00
|
|
|
</ListItem>
|
|
|
|
<Divider />
|
2022-01-05 02:03:03 +00:00
|
|
|
|
|
|
|
{this.state.isParticipant ?
|
|
|
|
<>
|
|
|
|
{this.state.takerNick!='None' ?
|
|
|
|
<>
|
|
|
|
<ListItem align="left">
|
2022-01-09 14:07:05 +00:00
|
|
|
<ListItemText primary={this.state.takerNick + (this.state.type ? " (Buyer)" : " (Seller)")} secondary="Order taker"/>
|
2022-01-05 02:03:03 +00:00
|
|
|
<ListItemAvatar >
|
|
|
|
<Avatar
|
|
|
|
alt={this.state.makerNick}
|
|
|
|
src={window.location.origin +'/static/assets/avatars/' + this.state.takerNick + '.png'}
|
|
|
|
/>
|
|
|
|
</ListItemAvatar>
|
|
|
|
</ListItem>
|
|
|
|
<Divider />
|
|
|
|
</>:
|
2022-01-05 12:18:54 +00:00
|
|
|
""
|
2022-01-05 02:03:03 +00:00
|
|
|
}
|
2022-01-05 12:18:54 +00:00
|
|
|
<ListItem>
|
|
|
|
<ListItemText primary={this.state.statusText} secondary="Order status"/>
|
|
|
|
</ListItem>
|
|
|
|
<Divider />
|
2022-01-05 02:03:03 +00:00
|
|
|
</>
|
|
|
|
:""
|
|
|
|
}
|
|
|
|
|
2022-01-03 12:11:33 +00:00
|
|
|
<ListItem>
|
2022-01-08 15:34:09 +00:00
|
|
|
<ListItemText primary={parseFloat(parseFloat(this.state.amount).toFixed(4))+" "+this.state.currencyCode} secondary="Amount"/>
|
2022-01-03 12:11:33 +00:00
|
|
|
</ListItem>
|
|
|
|
<Divider />
|
|
|
|
<ListItem>
|
|
|
|
<ListItemText primary={this.state.paymentMethod} secondary="Accepted payment methods"/>
|
|
|
|
</ListItem>
|
|
|
|
<Divider />
|
|
|
|
<ListItem>
|
|
|
|
{this.state.isExplicit ?
|
2022-01-03 14:27:25 +00:00
|
|
|
<ListItemText primary={pn(this.state.satoshis)} secondary="Amount of Satoshis"/>
|
2022-01-03 12:11:33 +00:00
|
|
|
:
|
2022-01-03 14:27:25 +00:00
|
|
|
<ListItemText primary={parseFloat(parseFloat(this.state.premium).toFixed(2))+"%"} secondary="Premium over market price"/>
|
2022-01-03 12:11:33 +00:00
|
|
|
}
|
|
|
|
</ListItem>
|
|
|
|
<Divider />
|
2022-01-05 02:03:03 +00:00
|
|
|
|
2022-01-03 12:11:33 +00:00
|
|
|
<ListItem>
|
|
|
|
<ListItemText primary={'#'+this.orderId} secondary="Order ID"/>
|
|
|
|
</ListItem>
|
2022-01-05 02:03:03 +00:00
|
|
|
<Divider />
|
|
|
|
<ListItem>
|
2022-01-08 17:19:30 +00:00
|
|
|
<ListItemText primary={msToTime( new Date(this.state.expiresAt) - Date.now())} secondary="Expires"/>
|
2022-01-05 02:03:03 +00:00
|
|
|
</ListItem>
|
2022-01-08 15:34:09 +00:00
|
|
|
<LinearDeterminate />
|
2022-01-03 12:11:33 +00:00
|
|
|
</List>
|
2022-01-10 12:10:32 +00:00
|
|
|
|
|
|
|
{/* If the user has a penalty/limit */}
|
|
|
|
{this.state.penalty ?
|
|
|
|
<>
|
|
|
|
<Divider />
|
|
|
|
<Grid item xs={12} align="center">
|
|
|
|
<Alert severity="warning" sx={{maxWidth:360}}>
|
|
|
|
You cannot take an order yet! Wait {this.state.penalty} seconds
|
|
|
|
</Alert>
|
|
|
|
</Grid>
|
|
|
|
</>
|
|
|
|
: null}
|
2022-01-05 02:03:03 +00:00
|
|
|
|
2022-01-05 00:13:08 +00:00
|
|
|
</Paper>
|
2022-01-08 13:08:03 +00:00
|
|
|
</Grid>
|
2022-01-03 12:11:33 +00:00
|
|
|
|
2022-01-08 13:08:03 +00:00
|
|
|
{/* Participants cannot see the Back or Take Order buttons */}
|
|
|
|
{this.state.isParticipant ? "" :
|
|
|
|
<>
|
|
|
|
<Grid item xs={12} align="center">
|
|
|
|
<Button variant='contained' color='primary' onClick={this.handleClickTakeOrderButton}>Take Order</Button>
|
|
|
|
</Grid>
|
|
|
|
<Grid item xs={12} align="center">
|
|
|
|
<Button variant='contained' color='secondary' onClick={this.handleClickBackButton}>Back</Button>
|
|
|
|
</Grid>
|
|
|
|
</>
|
|
|
|
}
|
2022-01-08 15:34:09 +00:00
|
|
|
|
2022-01-11 20:49:53 +00:00
|
|
|
{/* Makers can cancel before trade escrow deposited (status <9)*/}
|
|
|
|
{/* Only free cancel before bond locked (status 0)*/}
|
|
|
|
{this.state.isMaker & this.state.statusCode < 9 ?
|
2022-01-08 15:34:09 +00:00
|
|
|
<Grid item xs={12} align="center">
|
|
|
|
<Button variant='contained' color='secondary' onClick={this.handleClickCancelOrderButton}>Cancel</Button>
|
|
|
|
</Grid>
|
|
|
|
:""}
|
|
|
|
|
|
|
|
{/* Takers can cancel before commiting the bond (status 3)*/}
|
|
|
|
{this.state.isTaker & this.state.statusCode == 3 ?
|
|
|
|
<Grid item xs={12} align="center">
|
|
|
|
<Button variant='contained' color='secondary' onClick={this.handleClickCancelOrderButton}>Cancel</Button>
|
|
|
|
</Grid>
|
|
|
|
:""}
|
|
|
|
|
2022-01-08 13:08:03 +00:00
|
|
|
</Grid>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-01-09 20:05:19 +00:00
|
|
|
orderDetailsPage (){
|
|
|
|
return(
|
2022-01-08 13:08:03 +00:00
|
|
|
this.state.badRequest ?
|
|
|
|
<div align='center'>
|
|
|
|
<Typography component="subtitle2" variant="subtitle2" color="secondary" >
|
|
|
|
{this.state.badRequest}<br/>
|
|
|
|
</Typography>
|
|
|
|
<Button variant='contained' color='secondary' onClick={this.handleClickBackButton}>Back</Button>
|
|
|
|
</div>
|
|
|
|
:
|
|
|
|
(this.state.isParticipant ?
|
|
|
|
<Grid container xs={12} align="center" spacing={2}>
|
|
|
|
<Grid item xs={6} align="left">
|
|
|
|
{this.orderBox()}
|
|
|
|
</Grid>
|
|
|
|
<Grid item xs={6} align="left">
|
|
|
|
<TradeBox data={this.state}/>
|
|
|
|
</Grid>
|
2022-01-03 12:11:33 +00:00
|
|
|
</Grid>
|
2022-01-08 13:08:03 +00:00
|
|
|
:
|
2022-01-04 15:00:34 +00:00
|
|
|
<Grid item xs={12} align="center">
|
2022-01-08 13:08:03 +00:00
|
|
|
{this.orderBox()}
|
|
|
|
</Grid>)
|
2022-01-09 20:05:19 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
render (){
|
|
|
|
return (
|
|
|
|
// Only so nothing shows while requesting the first batch of data
|
2022-01-10 10:13:54 +00:00
|
|
|
(this.state.statusCode == null & this.state.badRequest == null) ? <CircularProgress /> : this.orderDetailsPage()
|
2022-01-02 12:59:48 +00:00
|
|
|
);
|
|
|
|
}
|
2022-01-09 14:29:10 +00:00
|
|
|
}
|