robosats/frontend/src/components/TradeBox.js

936 lines
34 KiB
JavaScript
Raw Normal View History

import React, { Component } from "react";
import { IconButton, Paper, Rating, Button, CircularProgress, Grid, Typography, TextField, List, ListItem, ListItemText, Divider, ListItemIcon, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle} from "@mui/material"
2022-01-09 01:23:13 +00:00
import QRCode from "react-qr-code";
import Countdown, { zeroPad} from 'react-countdown';
import Chat from "./Chat"
2022-01-27 22:51:57 +00:00
import MediaQuery from 'react-responsive'
import QrReader from 'react-qr-reader'
2022-01-14 13:31:54 +00:00
// Icons
import PercentIcon from '@mui/icons-material/Percent';
import BookIcon from '@mui/icons-material/Book';
import QrCodeScannerIcon from '@mui/icons-material/QrCodeScanner';
import SendIcon from '@mui/icons-material/Send';
2022-01-14 13:31:54 +00:00
2022-01-09 14:07:05 +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;
}
}
2022-01-09 01:23:13 +00:00
}
2022-01-09 14:07:05 +00:00
return cookieValue;
}
const csrftoken = getCookie('csrftoken');
2022-01-09 01:23:13 +00:00
2022-01-09 14:07:05 +00:00
// pretty numbers
function pn(x) {
var parts = x.toString().split(".");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
return parts.join(".");
2022-01-09 14:07:05 +00:00
}
2022-01-09 01:23:13 +00:00
2022-01-09 14:07:05 +00:00
export default class TradeBox extends Component {
constructor(props) {
super(props);
this.state = {
openConfirmFiatReceived: false,
openConfirmDispute: false,
openEnableTelegram: false,
badInvoice: false,
badStatement: false,
qrscanner: false,
}
2022-01-09 01:23:13 +00:00
}
2022-01-23 12:30:41 +00:00
Sound = ({soundFileName}) => (
// Four filenames: "locked-invoice", "taker-found", "open-chat", "sucessful"
<audio autoPlay src={`/static/assets/sounds/${soundFileName}.mp3`} />
)
togglePlay = () => {
this.setState({ playSound: !this.state.playSound }, () => {
this.state.playSound ? this.audio.play() : this.audio.pause();
});
}
handleClickOpenConfirmDispute = () => {
this.setState({openConfirmDispute: true});
};
handleClickCloseConfirmDispute = () => {
this.setState({openConfirmDispute: false});
};
handleClickAgreeDisputeButton=()=>{
const requestOptions = {
method: 'POST',
headers: {'Content-Type':'application/json', 'X-CSRFToken': getCookie('csrftoken'),},
body: JSON.stringify({
'action': "dispute",
}),
};
fetch('/api/order/' + '?order_id=' + this.props.data.id, requestOptions)
.then((response) => response.json())
.then((data) => this.props.completeSetState(data));
this.handleClickCloseConfirmDispute();
}
ConfirmDisputeDialog =() =>{
return(
<Dialog
open={this.state.openConfirmDispute}
onClose={this.handleClickCloseConfirmDispute}
aria-labelledby="open-dispute-dialog-title"
aria-describedby="open-dispute-dialog-description"
>
<DialogTitle id="open-dispute-dialog-title">
{"Do you want to open a dispute?"}
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
2022-01-23 19:02:25 +00:00
The RoboSats staff will examine the statements and evidence provided. You need to build
a complete case, as the staff cannot read the chat. It is best to provide a burner contact
2022-01-23 19:02:25 +00:00
method with your statement. The satoshis in the trade escrow will be sent to the dispute winner,
while the dispute loser will lose the bond.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={this.handleClickCloseConfirmDispute} autoFocus>Disagree</Button>
2022-01-23 19:02:25 +00:00
<Button onClick={this.handleClickAgreeDisputeButton}> Agree and open dispute </Button>
</DialogActions>
</Dialog>
)
}
handleClickOpenConfirmFiatReceived = () => {
this.setState({openConfirmFiatReceived: true});
};
handleClickCloseConfirmFiatReceived = () => {
this.setState({openConfirmFiatReceived: false});
};
handleClickTotallyConfirmFiatReceived = () =>{
this.handleClickConfirmButton();
this.handleClickCloseConfirmFiatReceived();
};
ConfirmFiatReceivedDialog =() =>{
return(
<Dialog
open={this.state.openConfirmFiatReceived}
onClose={this.handleClickCloseConfirmFiatReceived}
aria-labelledby="fiat-received-dialog-title"
aria-describedby="fiat-received-dialog-description"
>
<DialogTitle id="open-dispute-dialog-title">
{"Confirm you received " +this.props.data.currencyCode+ "?"}
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Confirming that you received the fiat will finalize the trade. The satoshis
in the escrow will be released to the buyer. Only confirm after the {this.props.data.currencyCode+ " "}
has arrived to your account. In addition, if you have received {this.props.data.currencyCode+ " "}
and do not confirm the receipt, you risk losing your bond.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={this.handleClickCloseConfirmFiatReceived} autoFocus>Go back</Button>
<Button onClick={this.handleClickTotallyConfirmFiatReceived}> Confirm </Button>
</DialogActions>
</Dialog>
)
}
2022-01-09 12:14:11 +00:00
showQRInvoice=()=>{
return (
<Grid container spacing={1}>
<Grid item xs={12} align="center">
<Typography component="body2" variant="body2">
Robots show commitment to their peers
</Typography>
</Grid>
<Grid item xs={12} align="center">
{this.props.data.is_maker ?
2022-01-10 18:47:16 +00:00
<Typography color="primary" component="subtitle1" variant="subtitle1">
<b>Lock {pn(this.props.data.bond_satoshis)} Sats to PUBLISH order </b>
</Typography>
:
2022-01-10 18:47:16 +00:00
<Typography color="primary" component="subtitle1" variant="subtitle1">
<b>Lock {pn(this.props.data.bond_satoshis)} Sats to TAKE the order </b>
</Typography>
}
</Grid>
<Grid item xs={12} align="center">
<QRCode value={this.props.data.bond_invoice} size={305}/>
<Button size="small" color="inherit" onClick={() => {navigator.clipboard.writeText(this.props.data.bond_invoice)}} align="center"> 📋Copy to clipboard</Button>
</Grid>
<Grid item xs={12} align="center">
<TextField
hiddenLabel
2022-01-14 14:19:25 +00:00
variant="standard"
size="small"
defaultValue={this.props.data.bond_invoice}
disabled="true"
helperText="This is a hold invoice, it will freeze in your wallet. It will be charged only if you cancel or lose a dispute."
color = "secondary"
/>
</Grid>
</Grid>
);
}
2022-01-09 20:05:19 +00:00
showBondIsLocked=()=>{
return (
<Grid item xs={12} align="center">
2022-01-14 14:19:25 +00:00
<Typography color="primary" component="subtitle1" variant="subtitle1" align="center">
🔒 Your {this.props.data.is_maker ? 'maker' : 'taker'} bond is locked
2022-01-14 14:19:25 +00:00
</Typography>
</Grid>
);
}
2022-01-09 12:14:11 +00:00
showEscrowQRInvoice=()=>{
2022-01-08 17:19:30 +00:00
return (
<Grid container spacing={1}>
2022-01-23 12:30:41 +00:00
{/* Make confirmation sound for HTLC received. */}
<this.Sound soundFileName="locked-invoice"/>
2022-01-08 17:19:30 +00:00
<Grid item xs={12} align="center">
<Typography color="green" component="subtitle1" variant="subtitle1">
<b>Deposit {pn(this.props.data.escrow_satoshis)} Sats as trade collateral </b>
2022-01-08 17:19:30 +00:00
</Typography>
</Grid>
<Grid item xs={12} align="center">
<QRCode value={this.props.data.escrow_invoice} size={305}/>
<Button size="small" color="inherit" onClick={() => {navigator.clipboard.writeText(this.props.data.escrow_invoice)}} align="center"> 📋Copy to clipboard</Button>
2022-01-08 17:19:30 +00:00
</Grid>
<Grid item xs={12} align="center">
<TextField
hiddenLabel
variant="filled"
size="small"
defaultValue={this.props.data.escrow_invoice}
2022-01-08 17:19:30 +00:00
disabled="true"
helperText={"This is a hold invoice, it will freeze in your wallet. It will be released to the buyer once you confirm to have received the "+this.props.data.currencyCode+"."}
2022-01-08 17:19:30 +00:00
color = "secondary"
/>
</Grid>
{this.showBondIsLocked()}
2022-01-08 17:19:30 +00:00
</Grid>
);
}
2022-01-09 01:23:13 +00:00
showTakerFound=()=>{
return (
<Grid container spacing={1}>
2022-01-23 12:30:41 +00:00
{/* Make bell sound when taker is found */}
<this.Sound soundFileName="taker-found"/>
2022-01-09 01:23:13 +00:00
<Grid item xs={12} align="center">
<Typography component="subtitle1" variant="subtitle1">
<b>A taker has been found! </b>
</Typography>
</Grid>
<Divider/>
<Grid item xs={12} align="center">
2022-01-09 15:28:12 +00:00
<Typography component="body2" variant="body2">
Please wait for the taker to lock a bond.
If the taker does not lock a bond in time, the order will be made
public again.
2022-01-09 01:23:13 +00:00
</Typography>
</Grid>
{this.showBondIsLocked()}
2022-01-09 01:23:13 +00:00
</Grid>
);
}
handleClickOpenTelegramDialog = () => {
this.setState({openEnableTelegram: true});
};
handleClickCloseEnableTelegramDialog = () => {
this.setState({openEnableTelegram: false});
};
handleClickEnableTelegram = () =>{
window.open("https://t.me/"+this.props.data.tg_bot_name+'?start='+this.props.data.tg_token, '_blank').focus()
this.handleClickCloseEnableTelegramDialog();
};
EnableTelegramDialog =() =>{
return(
<Dialog
open={this.state.openEnableTelegram}
onClose={this.handleClickCloseEnableTelegramDialog}
aria-labelledby="enable-telegram-dialog-title"
aria-describedby="enable-telegram-dialog-description"
>
<DialogTitle id="open-dispute-dialog-title">
Enable TG Notifications
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
You will be taken to a conversation with RoboSats telegram bot.
Simply open the chat and press "Start". Note that by enabling
telegram notifications you might lower your level of anonimity.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={this.handleClickCloseEnableTelegramDialog}>Go back</Button>
<Button onClick={this.handleClickEnableTelegram} autoFocus> Enable </Button>
</DialogActions>
</Dialog>
)
}
2022-01-08 15:34:09 +00:00
showMakerWait=()=>{
return (
<Grid container spacing={1}>
2022-01-23 12:30:41 +00:00
{/* Make confirmation sound for HTLC received. */}
<this.Sound soundFileName="locked-invoice"/>
<this.EnableTelegramDialog/>
2022-01-08 15:34:09 +00:00
<Grid item xs={12} align="center">
<Typography component="subtitle1" variant="subtitle1">
2022-01-10 18:47:16 +00:00
<b> Your order is public. Wait for a taker. </b>
2022-01-08 15:34:09 +00:00
</Typography>
</Grid>
<Grid item xs={12} align="center">
<List dense="true">
<Divider/>
<ListItem>
<Typography component="body2" variant="body2" align="left">
<p>Be patient while robots check the book.
It might take some time. This box will ring 🔊 once a robot takes your order. </p>
<p>Please note that if your premium is excessive, or your currency or payment
2022-01-08 15:34:09 +00:00
methods are not popular, your order might expire untaken. Your bond will
return to you (no action needed).</p>
</Typography>
</ListItem>
<Grid item xs={12} align="center">
{this.props.data.tg_enabled ?
<Typography color='primary' component="h6" variant="h6" align="center"> Telegram enabled</Typography>
:
<Button color="primary" onClick={this.handleClickOpenTelegramDialog}>
<SendIcon/>Enable Telegram Notifications
</Button>
}
</Grid>
2022-01-08 15:34:09 +00:00
{/* TODO API sends data for a more confortable wait */}
<Divider/>
<ListItem>
2022-01-14 13:31:54 +00:00
<ListItemIcon>
<BookIcon/>
</ListItemIcon>
<ListItemText primary={this.props.data.num_similar_orders} secondary={"Public orders for " + this.props.data.currencyCode}/>
2022-01-08 15:34:09 +00:00
</ListItem>
<Divider/>
<ListItem>
2022-01-14 13:31:54 +00:00
<ListItemIcon>
<PercentIcon/>
</ListItemIcon>
<ListItemText primary={"Premium rank " + this.props.data.premium_percentile*100+"%"}
2022-01-18 18:24:45 +00:00
secondary={"Among public " + this.props.data.currencyCode + " orders (higher is cheaper)"} />
2022-01-08 15:34:09 +00:00
</ListItem>
<Divider/>
2022-01-08 15:34:09 +00:00
</List>
</Grid>
{this.showBondIsLocked()}
2022-01-08 15:34:09 +00:00
</Grid>
)
}
2022-01-09 14:07:05 +00:00
handleInputInvoiceChanged=(e)=>{
this.setState({
invoice: e.target.value,
badInvoice: false,
2022-01-09 14:07:05 +00:00
});
}
2022-01-09 12:14:11 +00:00
2022-01-09 14:07:05 +00:00
handleClickSubmitInvoiceButton=()=>{
this.setState({badInvoice:false});
2022-01-09 14:07:05 +00:00
const requestOptions = {
method: 'POST',
headers: {'Content-Type':'application/json', 'X-CSRFToken': getCookie('csrftoken'),},
body: JSON.stringify({
'action':'update_invoice',
'invoice': this.state.invoice,
}),
};
fetch('/api/order/' + '?order_id=' + this.props.data.id, requestOptions)
.then((response) => response.json())
.then((data) => this.setState({badInvoice:data.bad_invoice})
& this.props.completeSetState(data));
2022-01-09 14:07:05 +00:00
}
2022-01-09 12:14:11 +00:00
handleInputDisputeChanged=(e)=>{
this.setState({
statement: e.target.value,
badStatement: false,
});
}
handleClickSubmitStatementButton=()=>{
this.setState({badInvoice:false});
const requestOptions = {
method: 'POST',
headers: {'Content-Type':'application/json', 'X-CSRFToken': getCookie('csrftoken'),},
body: JSON.stringify({
'action':'submit_statement',
'statement': this.state.statement,
}),
};
fetch('/api/order/' + '?order_id=' + this.props.data.id, requestOptions)
.then((response) => response.json())
.then((data) => this.setState({badStatement:data.bad_statement})
& this.props.completeSetState(data));
}
handleScan = data => {
if (data) {
this.setState({
invoice: data
})
}
}
handleError = err => {
console.error(err)
}
handleQRbutton = () => {
this.setState({qrscanner: !this.state.qrscanner});
}
2022-01-09 14:07:05 +00:00
showInputInvoice(){
return (
2022-01-09 12:14:11 +00:00
2022-01-09 14:07:05 +00:00
<Grid container spacing={1}>
<Grid item xs={12} align="center">
{/* Make confirmation sound for HTLC received. */}
<this.Sound soundFileName="locked-invoice"/>
2022-01-10 18:47:16 +00:00
<Typography color="primary" component="subtitle1" variant="subtitle1">
<b> Submit a LN invoice for {pn(this.props.data.invoice_amount)} Sats </b>
2022-01-09 14:07:05 +00:00
</Typography>
</Grid>
<Grid item xs={12} align="left">
<Typography component="body2" variant="body2">
The taker is committed! Before letting you send {" "+ parseFloat(parseFloat(this.props.data.amount).toFixed(4))+
" "+ this.props.data.currencyCode}, we want to make sure you are able to receive the BTC. Please provide a
valid invoice for {pn(this.props.data.invoice_amount)} Satoshis.
2022-01-09 14:07:05 +00:00
</Typography>
</Grid>
<Grid item xs={12} align="center">
<TextField
error={this.state.badInvoice}
helperText={this.state.badInvoice ? this.state.badInvoice : "" }
label={"Payout Lightning Invoice"}
required
value={this.state.invoice}
inputProps={{
2022-01-27 22:51:57 +00:00
style: {textAlign:"center"},
maxHeight: 200,
}}
multiline
minRows={5}
maxRows={this.state.qrscanner ? 5 : 14}
onChange={this.handleInputInvoiceChanged}
/>
</Grid>
{this.state.qrscanner ?
<Grid item xs={12} align="center">
<QrReader
delay={300}
onError={this.handleError}
onScan={this.handleScan}
style={{ width: '75%' }}
/>
</Grid>
: null }
<Grid item xs={12} align="center">
<IconButton><QrCodeScannerIcon onClick={this.handleQRbutton}/></IconButton>
<Button onClick={this.handleClickSubmitInvoiceButton} variant='contained' color='primary'>Submit</Button>
</Grid>
{this.showBondIsLocked()}
</Grid>
)
}
// Asks the user for a dispute statement.
showInDisputeStatement=()=>{
if(this.props.data.statement_submitted){
return (
<Grid container spacing={1}>
<Grid item xs={12} align="center">
<Typography color="primary" component="subtitle1" variant="subtitle1">
<b> We have received your statement </b>
</Typography>
</Grid>
<Grid item xs={12} align="left">
<Typography component="body2" variant="body2">
We are waiting for your trade counterparty statement.
</Typography>
</Grid>
{this.showBondIsLocked()}
</Grid>
)
}else{
return (
// TODO Option to upload files
<Grid container spacing={1}>
<Grid item xs={12} align="center">
<Typography color="primary" component="subtitle1" variant="subtitle1">
<b> A dispute has been opened </b>
</Typography>
</Grid>
<Grid item xs={12} align="left">
<Typography component="body2" variant="body2">
Please, submit your statement. Be clear and specific about what happened and provide the necessary
evidence. It is best to provide a burner email, XMPP or telegram username to follow up with the staff.
Disputes are solved at the discretion of real robots <i>(aka humans)</i>, so be as helpful
as possible to ensure a fair outcome. Max 5000 chars.
</Typography>
</Grid>
<Grid item xs={12} align="center">
<TextField
error={this.state.badStatement}
helperText={this.state.badStatement ? this.state.badStatement : "" }
label={"Submit dispute statement"}
required
inputProps={{
style: {textAlign:"center"}
}}
multiline
rows={4}
onChange={this.handleInputDisputeChanged}
/>
</Grid>
<Grid item xs={12} align="center">
<Button onClick={this.handleClickSubmitStatementButton} variant='contained' color='primary'>Submit</Button>
</Grid>
{this.showBondIsLocked()}
</Grid>
)}
2022-01-09 14:07:05 +00:00
}
showWaitForDisputeResolution=()=>{
return (
<Grid container spacing={1}>
<Grid item xs={12} align="center">
<Typography color="primary" component="subtitle1" variant="subtitle1">
<b> We have the statements </b>
</Typography>
</Grid>
<Grid item xs={12} align="left">
<Typography component="body2" variant="body2">
Wait for the staff to resolve the dispute. The dispute winner
will be asked to submit a LN invoice.
</Typography>
</Grid>
{this.showBondIsLocked()}
</Grid>
)
}
2022-01-09 14:07:05 +00:00
showWaitingForEscrow(){
2022-01-09 15:28:12 +00:00
return(
<Grid container spacing={1}>
<Grid item xs={12} align="center">
<Typography component="subtitle1" variant="subtitle1">
2022-01-19 13:32:54 +00:00
<b>Your invoice looks good!🎉</b>
2022-01-09 15:28:12 +00:00
</Typography>
</Grid>
<Grid item xs={12} align="center">
<Typography component="body2" variant="body2" align="left">
2022-01-23 20:23:25 +00:00
<p>We are waiting for the seller lock the trade amount. </p>
<p> Just hang on for a moment. If the seller does not deposit,
you will get your bond back automatically.</p>
2022-01-09 15:28:12 +00:00
</Typography>
</Grid>
{this.showBondIsLocked()}
2022-01-09 15:28:12 +00:00
</Grid>
)
}
showWaitingForBuyerInvoice(){
return(
<Grid container spacing={1}>
2022-01-23 12:30:41 +00:00
{/* Make confirmation sound for HTLC received. */}
<this.Sound soundFileName="locked-invoice"/>
2022-01-09 15:28:12 +00:00
<Grid item xs={12} align="center">
<Typography component="subtitle1" variant="subtitle1">
2022-01-19 13:32:54 +00:00
<b>The trade collateral is locked! 🎉 </b>
2022-01-09 15:28:12 +00:00
</Typography>
</Grid>
<Grid item xs={12} align="center">
<Typography component="body2" variant="body2" align="left">
<p> We are waiting for the buyer to post a lightning invoice. Once
he does, you will be able to directly communicate the fiat payment
details. </p>
<p> Just hang on for a moment. If the buyer does not cooperate,
you will get back the trade collateral and your bond automatically.</p>
</Typography>
</Grid>
{this.showBondIsLocked()}
2022-01-09 15:28:12 +00:00
</Grid>
)
2022-01-09 14:07:05 +00:00
}
2022-01-09 12:14:11 +00:00
2022-01-09 20:05:19 +00:00
handleClickConfirmButton=()=>{
2022-01-09 15:28:12 +00:00
const requestOptions = {
method: 'POST',
headers: {'Content-Type':'application/json', 'X-CSRFToken': getCookie('csrftoken'),},
body: JSON.stringify({
2022-01-09 20:05:19 +00:00
'action': "confirm",
2022-01-09 15:28:12 +00:00
}),
};
fetch('/api/order/' + '?order_id=' + this.props.data.id, requestOptions)
.then((response) => response.json())
.then((data) => this.props.completeSetState(data));
2022-01-09 15:28:12 +00:00
}
handleRatingUserChange=(e)=>{
const requestOptions = {
method: 'POST',
headers: {'Content-Type':'application/json', 'X-CSRFToken': getCookie('csrftoken'),},
body: JSON.stringify({
'action': "rate_user",
'rating': e.target.value,
}),
};
fetch('/api/order/' + '?order_id=' + this.props.data.id, requestOptions)
.then((response) => response.json())
.then((data) => this.props.completeSetState(data));
}
handleRatingRobosatsChange=(e)=>{
if (this.state.rating_platform != null){
return null
}
this.setState({rating_platform:e.target.value});
const requestOptions = {
method: 'POST',
headers: {'Content-Type':'application/json', 'X-CSRFToken': getCookie('csrftoken'),},
body: JSON.stringify({
'action': "rate_platform",
'rating': e.target.value,
}),
};
fetch('/api/order/' + '?order_id=' + this.props.data.id, requestOptions)
.then((response) => response.json())
.then((data) => this.props.completeSetState(data));
}
2022-01-09 12:14:11 +00:00
2022-01-09 15:28:12 +00:00
showFiatSentButton(){
return(
<Grid container spacing={1}>
<Grid item xs={12} align="center">
<Button defaultValue="confirm" variant='contained' color='secondary' onClick={this.handleClickConfirmButton}>Confirm {this.props.data.currencyCode} sent</Button>
2022-01-09 20:05:19 +00:00
</Grid>
</Grid>
)
}
showFiatReceivedButton(){
return(
<Grid item xs={12} align="center">
<Button defaultValue="confirm" variant='contained' color='secondary' onClick={this.handleClickOpenConfirmFiatReceived}>Confirm {this.props.data.currencyCode} received</Button>
2022-01-09 20:05:19 +00:00
</Grid>
)
}
showOpenDisputeButton(){
// TODO, show alert about how opening a dispute might involve giving away personal data and might mean losing the bond. Ask for double confirmation.
return(
<Grid item xs={12} align="center">
<Button color="inherit" onClick={this.handleClickOpenConfirmDispute}>Open Dispute</Button>
2022-01-09 20:05:19 +00:00
</Grid>
)
}
2022-01-18 15:45:04 +00:00
showOrderExpired(){
return(
<Grid container spacing={1}>
<Grid item xs={12} align="center">
<Typography component="subtitle1" variant="subtitle1">
<b>The order has expired</b>
</Typography>
</Grid>
</Grid>
)
}
showChat=()=>{
//In Chatroom - No fiat sent - showChat(showSendButton, showReveiceButton, showDisputeButton)
if(this.props.data.is_buyer & this.props.data.status == 9){
var showSendButton=true;
var showReveiceButton=false;
var showDisputeButton=true;
}
if(this.props.data.is_seller & this.props.data.status == 9){
var showSendButton=false;
var showReveiceButton=false;
var showDisputeButton=true;
}
//In Chatroom - Fiat sent - showChat(showSendButton, showReveiceButton, showDisputeButton)
if(this.props.data.is_buyer & this.props.data.status == 10){
var showSendButton=false;
var showReveiceButton=false;
var showDisputeButton=true;
}
if(this.props.data.is_seller & this.props.data.status == 10){
var showSendButton=false;
var showReveiceButton=true;
var showDisputeButton=true;
}
2022-01-09 20:05:19 +00:00
return(
<Grid container spacing={1}>
2022-01-23 12:30:41 +00:00
{/* Make confirmation sound for Chat Open. */}
<this.Sound soundFileName="chat-open"/>
2022-01-09 20:05:19 +00:00
<Grid item xs={12} align="center">
<Typography component="subtitle1" variant="subtitle1">
<b>Chatting with {this.props.data.is_maker ? this.props.data.taker_nick : this.props.data.maker_nick}</b>
2022-01-09 20:05:19 +00:00
</Typography>
</Grid>
<Grid item xs={12} align="center">
{this.props.data.is_seller ?
<Typography component="body2" variant="body2" align="center">
2022-01-19 13:32:54 +00:00
Say hi! Be helpful and concise. Let them know how to send you {this.props.data.currencyCode}.
2022-01-09 20:05:19 +00:00
</Typography>
:
<Typography component="body2" variant="body2" align="center">
Say hi! Ask for payment details and click "Confirm Sent" as soon as the payment is sent.
2022-01-09 20:05:19 +00:00
</Typography>
}
<Divider/>
2022-01-09 20:05:19 +00:00
</Grid>
<Chat orderId={this.props.data.id} ur_nick={this.props.data.ur_nick}/>
2022-01-09 20:05:19 +00:00
<Grid item xs={12} align="center">
{showDisputeButton ? this.showOpenDisputeButton() : ""}
{showSendButton ? this.showFiatSentButton() : ""}
{showReveiceButton ? this.showFiatReceivedButton() : ""}
2022-01-09 15:28:12 +00:00
</Grid>
{this.showBondIsLocked()}
2022-01-09 15:28:12 +00:00
</Grid>
)
}
showRateSelect(){
return(
<Grid container spacing={1}>
2022-01-23 12:30:41 +00:00
{/* Make confirmation sound for Chat Open. */}
<this.Sound soundFileName="successful"/>
<Grid item xs={12} align="center">
<Typography component="h6" variant="h6">
🎉Trade finished!🥳
</Typography>
</Grid>
<Grid item xs={12} align="center">
<Typography component="body2" variant="body2" align="center">
What do you think of <b>{this.props.data.is_maker ? this.props.data.taker_nick : this.props.data.maker_nick}</b>?
</Typography>
</Grid>
<Grid item xs={12} align="center">
<Rating name="size-large" defaultValue={0} size="large" onChange={this.handleRatingUserChange} />
</Grid>
<Grid item xs={12} align="center">
<Typography component="body2" variant="body2" align="center">
What do you think of 🤖<b>RoboSats</b>🤖?
</Typography>
</Grid>
<Grid item xs={12} align="center">
<Rating name="size-large" defaultValue={0} size="large" onChange={this.handleRatingRobosatsChange} />
</Grid>
{this.state.rating_platform==5 ?
<Grid item xs={12} align="center">
<Typography component="body2" variant="body2" align="center">
<p>Thank you! RoboSats loves you too </p>
<p>RoboSats gets better with more liquidity and users. Tell a bitcoiner friend about Robosats!</p>
</Typography>
</Grid>
: null}
{this.state.rating_platform!=5 & this.state.rating_platform!=null ?
<Grid item xs={12} align="center">
<Typography component="body2" variant="body2" align="center">
Thank you for using Robosats! Let us know what you did not like and how the platform could improve
(<a href="https://t.me/robosats">Telegram</a> / <a href="https://github.com/Reckless-Satoshi/robosats/issues">Github</a>)
</Typography>
</Grid>
: null}
<Grid item xs={12} align="center">
2022-02-01 00:45:58 +00:00
<Button color='primary' onClick={() => {this.props.push('/')}}>Start Again</Button>
</Grid>
</Grid>
)
}
2022-01-09 12:14:11 +00:00
showSendingPayment(){
return(
<Grid container spacing={1}>
<Grid item xs={12} align="center">
<Typography component="h6" variant="h6">
Attempting Lightning Payment
</Typography>
</Grid>
<Grid item xs={12} align="center">
<Typography component="body2" variant="body2" align="center">
RoboSats is trying to pay your lightning invoice. Remember that lightning nodes must
be online in order to receive payments.
</Typography>
<br/>
<Grid item xs={12} align="center">
<CircularProgress/>
</Grid>
</Grid>
</Grid>
)
}
// Countdown Renderer callback with condition
countdownRenderer = ({ minutes, seconds, completed }) => {
if (completed) {
// Render a completed state
return (<div align="center"><span> Retrying! </span><br/><CircularProgress/></div> );
} else {
return (
<span>{zeroPad(minutes)}m {zeroPad(seconds)}s </span>
);
}
};
showRoutingFailed=()=>{
// TODO If it has failed 3 times, ask for a new invoice.
if(this.props.data.invoice_expired){
return(
<Grid container spacing={1}>
<Grid item xs={12} align="center">
<Typography component="h6" variant="h6">
Lightning Routing Failed
</Typography>
</Grid>
<Grid item xs={12} align="center">
<Typography component="body2" variant="body2" align="center">
2022-02-06 14:50:42 +00:00
Your invoice has expires or more than 3 payments attempts have been made.
</Typography>
</Grid>
<Grid item xs={12} align="center">
<Typography color="primary" component="subtitle1" variant="subtitle1">
<b> Submit a LN invoice for {pn(this.props.data.invoice_amount)} Sats </b>
</Typography>
</Grid>
<Grid item xs={12} align="center">
<TextField
error={this.state.badInvoice}
helperText={this.state.badInvoice ? this.state.badInvoice : "" }
label={"Payout Lightning Invoice"}
required
inputProps={{
style: {textAlign:"center"}
}}
multiline
onChange={this.handleInputInvoiceChanged}
/>
</Grid>
<Grid item xs={12} align="center">
<Button onClick={this.handleClickSubmitInvoiceButton} variant='contained' color='primary'>Submit</Button>
</Grid>
</Grid>
)
}else{
return(
<Grid container spacing={1}>
<Grid item xs={12} align="center">
<Typography component="h6" variant="h6">
Lightning Routing Failed
</Typography>
</Grid>
<Grid item xs={12} align="center">
<Typography component="body2" variant="body2" align="center">
RoboSats will try to pay your invoice 3 times every 5 minutes. If it keeps failing, you
will be able to submit a new invoice. Check whether you have enough inboud liquidity.
Remember that lightning nodes must be online in order to receive payments.
</Typography>
<List>
<Divider/>
<ListItemText secondary="Next attempt in">
<Countdown date={new Date(this.props.data.next_retry_time)} renderer={this.countdownRenderer} />
</ListItemText>
</List>
</Grid>
</Grid>
)}
}
render() {
return (
2022-01-29 14:42:54 +00:00
<Grid container spacing={1} style={{ width:this.props.width}}>
<this.ConfirmDisputeDialog/>
<this.ConfirmFiatReceivedDialog/>
<Grid item xs={12} align="center">
2022-01-27 22:51:57 +00:00
<MediaQuery minWidth={920}>
<Typography component="h5" variant="h5">
Contract Box
</Typography>
</MediaQuery>
<Paper elevation={12} style={{ padding: 8,}}>
2022-01-09 12:14:11 +00:00
{/* Maker and taker Bond request */}
{this.props.data.is_maker & this.props.data.status == 0 ? this.showQRInvoice() : ""}
{this.props.data.is_taker & this.props.data.status == 3 ? this.showQRInvoice() : ""}
2022-01-09 12:14:11 +00:00
{/* Waiting for taker and taker bond request */}
{this.props.data.is_maker & this.props.data.status == 1 ? this.showMakerWait() : ""}
{this.props.data.is_maker & this.props.data.status == 3 ? this.showTakerFound() : ""}
2022-01-09 12:14:11 +00:00
{/* Send Invoice (buyer) and deposit collateral (seller) */}
{this.props.data.is_seller & (this.props.data.status == 6 || this.props.data.status == 7 ) ? this.showEscrowQRInvoice() : ""}
{this.props.data.is_buyer & (this.props.data.status == 6 || this.props.data.status == 8 )? this.showInputInvoice() : ""}
{this.props.data.is_buyer & this.props.data.status == 7 ? this.showWaitingForEscrow() : ""}
{this.props.data.is_seller & this.props.data.status == 8 ? this.showWaitingForBuyerInvoice() : ""}
2022-01-09 12:14:11 +00:00
{/* In Chatroom */}
{this.props.data.status == 9 || this.props.data.status == 10 ? this.showChat(): ""}
2022-01-09 12:14:11 +00:00
{/* Trade Finished */}
{(this.props.data.is_seller & [13,14,15].includes(this.props.data.status)) ? this.showRateSelect() : ""}
{(this.props.data.is_buyer & this.props.data.status == 14) ? this.showRateSelect() : ""}
{/* Trade Finished - Payment Routing Failed */}
{this.props.data.is_buyer & this.props.data.status == 13 ? this.showSendingPayment() : ""}
{/* Trade Finished - Payment Routing Failed */}
{this.props.data.is_buyer & this.props.data.status == 15 ? this.showRoutingFailed() : ""}
{/* Trade Finished - TODO Needs more planning */}
{this.props.data.status == 11 ? this.showInDisputeStatement() : ""}
{this.props.data.status == 16 ? this.showWaitForDisputeResolution() : ""}
2022-01-18 15:45:04 +00:00
{/* Order has expired */}
{this.props.data.status == 5 ? this.showOrderExpired() : ""}
2022-01-09 12:14:11 +00:00
{/* TODO */}
{/* */}
{/* */}
2022-01-09 12:35:19 +00:00
</Paper>
</Grid>
</Grid>
);
}
}