2022-01-08 13:08:03 +00:00
import React , { Component } from "react" ;
2022-02-04 18:07:09 +00:00
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" ;
2022-02-04 18:07:09 +00:00
import Countdown , { zeroPad } from 'react-countdown' ;
2022-01-13 19:22:54 +00:00
import Chat from "./Chat"
2022-01-27 22:51:57 +00:00
import MediaQuery from 'react-responsive'
2022-01-28 00:17:20 +00:00
import QrReader from 'react-qr-reader'
2022-01-13 19:22:54 +00:00
2022-01-14 13:31:54 +00:00
// Icons
import PercentIcon from '@mui/icons-material/Percent' ;
import BookIcon from '@mui/icons-material/Book' ;
2022-01-28 00:17:20 +00:00
import QrCodeScannerIcon from '@mui/icons-material/QrCodeScanner' ;
2022-02-22 00:50:25 +00:00
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 ) {
2022-02-17 02:45:18 +00:00
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 ) ;
2022-01-11 01:02:06 +00:00
this . state = {
2022-01-17 23:11:41 +00:00
openConfirmFiatReceived : false ,
openConfirmDispute : false ,
2022-01-11 01:02:06 +00:00
badInvoice : false ,
2022-01-16 21:54:42 +00:00
badStatement : false ,
2022-01-28 00:17:20 +00:00
qrscanner : false ,
2022-01-11 01:02:06 +00:00
}
2022-01-09 01:23:13 +00:00
}
2022-01-17 23:11:41 +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 ( ) ;
} ) ;
}
2022-01-17 23:11:41 +00:00
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 ( ) )
2022-01-22 23:05:03 +00:00
. then ( ( data ) => this . props . completeSetState ( data ) ) ;
2022-01-17 23:11:41 +00:00
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?" }
< / D i a l o g T i t l e >
< 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
2022-02-22 00:50:25 +00:00
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 .
2022-01-17 23:11:41 +00:00
< / D i a l o g C o n t e n t T e x t >
< / D i a l o g C o n t e n t >
< DialogActions >
< Button onClick = { this . handleClickCloseConfirmDispute } autoFocus > Disagree < / B u t t o n >
2022-01-23 19:02:25 +00:00
< Button onClick = { this . handleClickAgreeDisputeButton } > Agree and open dispute < / B u t t o n >
2022-01-17 23:11:41 +00:00
< / D i a l o g A c t i o n s >
< / D i a l o g >
)
}
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 + "?" }
< / D i a l o g T i t l e >
< 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 .
< / D i a l o g C o n t e n t T e x t >
< / D i a l o g C o n t e n t >
< DialogActions >
< Button onClick = { this . handleClickCloseConfirmFiatReceived } autoFocus > Go back < / B u t t o n >
< Button onClick = { this . handleClickTotallyConfirmFiatReceived } > Confirm < / B u t t o n >
< / D i a l o g A c t i o n s >
< / D i a l o g >
)
}
2022-01-09 12:14:11 +00:00
showQRInvoice = ( ) => {
2022-01-08 13:08:03 +00:00
return (
< Grid container spacing = { 1 } >
< Grid item xs = { 12 } align = "center" >
< Typography component = "body2" variant = "body2" >
2022-01-24 17:54:44 +00:00
Robots show commitment to their peers
2022-01-08 13:08:03 +00:00
< / T y p o g r a p h y >
< / G r i d >
< Grid item xs = { 12 } align = "center" >
2022-01-22 23:05:03 +00:00
{ this . props . data . is _maker ?
2022-01-10 18:47:16 +00:00
< Typography color = "primary" component = "subtitle1" variant = "subtitle1" >
2022-01-22 23:05:03 +00:00
< b > Lock { pn ( this . props . data . bond _satoshis ) } Sats to PUBLISH order < / b >
2022-01-08 13:08:03 +00:00
< / T y p o g r a p h y >
:
2022-01-10 18:47:16 +00:00
< Typography color = "primary" component = "subtitle1" variant = "subtitle1" >
2022-01-22 23:05:03 +00:00
< b > Lock { pn ( this . props . data . bond _satoshis ) } Sats to TAKE the order < / b >
2022-01-08 13:08:03 +00:00
< / T y p o g r a p h y >
}
< / G r i d >
< Grid item xs = { 12 } align = "center" >
2022-01-22 23:05:03 +00:00
< 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 < / B u t t o n >
2022-01-08 13:08:03 +00:00
< / G r i d >
< Grid item xs = { 12 } align = "center" >
2022-01-13 20:41:48 +00:00
< TextField
2022-01-08 13:08:03 +00:00
hiddenLabel
2022-01-14 14:19:25 +00:00
variant = "standard"
2022-01-08 13:08:03 +00:00
size = "small"
2022-01-22 23:05:03 +00:00
defaultValue = { this . props . data . bond _invoice }
2022-01-08 13:08:03 +00:00
disabled = "true"
2022-01-19 19:37:10 +00:00
helperText = "This is a hold invoice, it will freeze in your wallet. It will be charged only if you cancel or lose a dispute."
2022-01-08 13:08:03 +00:00
color = "secondary"
/ >
< / G r i d >
< / G r i d >
) ;
}
2022-01-09 20:05:19 +00:00
2022-01-12 12:57:03 +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" >
2022-01-22 23:05:03 +00:00
🔒 Your { this . props . data . is _maker ? 'maker' : 'taker' } bond is locked
2022-01-14 14:19:25 +00:00
< / T y p o g r a p h y >
2022-01-12 12:57:03 +00:00
< / G r i d >
) ;
}
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" >
2022-01-18 15:23:57 +00:00
< Typography color = "green" component = "subtitle1" variant = "subtitle1" >
2022-01-22 23:05:03 +00:00
< b > Deposit { pn ( this . props . data . escrow _satoshis ) } Sats as trade collateral < / b >
2022-01-08 17:19:30 +00:00
< / T y p o g r a p h y >
< / G r i d >
< Grid item xs = { 12 } align = "center" >
2022-01-22 23:05:03 +00:00
< 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 < / B u t t o n >
2022-01-08 17:19:30 +00:00
< / G r i d >
< Grid item xs = { 12 } align = "center" >
< TextField
hiddenLabel
variant = "filled"
size = "small"
2022-01-22 23:05:03 +00:00
defaultValue = { this . props . data . escrow _invoice }
2022-01-08 17:19:30 +00:00
disabled = "true"
2022-01-19 19:37:10 +00:00
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"
/ >
< / G r i d >
2022-01-12 12:57:03 +00:00
{ this . showBondIsLocked ( ) }
2022-01-08 17:19:30 +00:00
< / G r i d >
) ;
}
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 >
< / T y p o g r a p h y >
< / G r i d >
< Divider / >
< Grid item xs = { 12 } align = "center" >
2022-01-09 15:28:12 +00:00
< Typography component = "body2" variant = "body2" >
2022-02-13 17:55:44 +00:00
Please wait for the taker to lock a bond .
If the taker does not lock a bond in time , the order will be made
2022-01-28 00:17:20 +00:00
public again .
2022-01-09 01:23:13 +00:00
< / T y p o g r a p h y >
< / G r i d >
2022-01-12 12:57:03 +00:00
{ this . showBondIsLocked ( ) }
2022-01-09 01:23:13 +00:00
< / G r i d >
) ;
}
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" / >
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
< / T y p o g r a p h y >
< / G r i d >
< 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 >
2022-02-22 00:50:25 +00:00
< 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 >
< / T y p o g r a p h y >
< / L i s t I t e m >
2022-02-22 00:50:25 +00:00
< Grid item xs = { 12 } align = "center" >
{ this . props . data . tg _enabled ?
< Typography color = 'primary' component = "h6" variant = "h6" align = "center" > Telegram enabled < / T y p o g r a p h y >
:
< Button color = "primary" component = "a" target = "_blank" href = { "https://t.me/" + this . props . data . tg _bot _name + '?start=' + this . props . data . tg _token } >
< SendIcon / > Enable Telegram Notifications
< / B u t t o n >
}
< / G r i d >
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 / >
< / L i s t I t e m I c o n >
2022-01-22 23:05:03 +00:00
< ListItemText primary = { this . props . data . num _similar _orders } secondary = { "Public orders for " + this . props . data . currencyCode } / >
2022-01-08 15:34:09 +00:00
< / L i s t I t e m >
< Divider / >
< ListItem >
2022-01-14 13:31:54 +00:00
< ListItemIcon >
< PercentIcon / >
< / L i s t I t e m I c o n >
2022-01-22 23:05:03 +00:00
< 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
< / L i s t I t e m >
2022-01-12 12:57:03 +00:00
< Divider / >
2022-01-08 15:34:09 +00:00
< / L i s t >
< / G r i d >
2022-01-12 12:57:03 +00:00
{ this . showBondIsLocked ( ) }
2022-01-08 15:34:09 +00:00
< / G r i d >
)
}
2022-01-08 13:08:03 +00:00
2022-01-09 14:07:05 +00:00
handleInputInvoiceChanged = ( e ) => {
this . setState ( {
2022-01-11 01:02:06 +00:00
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 = ( ) => {
2022-01-11 01:02:06 +00:00
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 ( ) )
2022-01-11 01:02:06 +00:00
. then ( ( data ) => this . setState ( { badInvoice : data . bad _invoice } )
2022-01-22 23:05:03 +00:00
& this . props . completeSetState ( data ) ) ;
2022-01-09 14:07:05 +00:00
}
2022-01-09 12:14:11 +00:00
2022-01-16 21:54:42 +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 } )
2022-01-22 23:05:03 +00:00
& this . props . completeSetState ( data ) ) ;
2022-01-28 00:17:20 +00:00
}
handleScan = data => {
if ( data ) {
this . setState ( {
invoice : data
} )
}
}
handleError = err => {
console . error ( err )
}
handleQRbutton = ( ) => {
this . setState ( { qrscanner : ! this . state . qrscanner } ) ;
}
2022-01-16 21:54:42 +00:00
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" >
2022-02-03 21:51:42 +00:00
{ /* 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" >
2022-01-22 23:05:03 +00:00
< b > Submit a LN invoice for { pn ( this . props . data . invoice _amount ) } Sats < / b >
2022-01-09 14:07:05 +00:00
< / T y p o g r a p h y >
< / G r i d >
< 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
2022-01-22 23:05:03 +00:00
valid invoice for { pn ( this . props . data . invoice _amount ) } Satoshis .
2022-01-09 14:07:05 +00:00
< / T y p o g r a p h y >
< / G r i d >
2022-01-15 12:00:11 +00:00
< Grid item xs = { 12 } align = "center" >
< TextField
error = { this . state . badInvoice }
helperText = { this . state . badInvoice ? this . state . badInvoice : "" }
label = { "Payout Lightning Invoice" }
required
2022-01-28 00:17:20 +00:00
value = { this . state . invoice }
2022-01-15 12:00:11 +00:00
inputProps = { {
2022-01-27 22:51:57 +00:00
style : { textAlign : "center" } ,
maxHeight : 200 ,
2022-01-15 12:00:11 +00:00
} }
multiline
2022-01-28 00:17:20 +00:00
minRows = { 5 }
maxRows = { this . state . qrscanner ? 5 : 14 }
2022-01-15 12:00:11 +00:00
onChange = { this . handleInputInvoiceChanged }
/ >
< / G r i d >
2022-01-28 00:17:20 +00:00
{ this . state . qrscanner ?
< Grid item xs = { 12 } align = "center" >
< QrReader
delay = { 300 }
onError = { this . handleError }
onScan = { this . handleScan }
style = { { width : '75%' } }
/ >
< / G r i d >
: null }
2022-01-15 12:00:11 +00:00
< Grid item xs = { 12 } align = "center" >
2022-01-28 00:17:20 +00:00
< IconButton > < QrCodeScannerIcon onClick = { this . handleQRbutton } / > < / I c o n B u t t o n >
2022-01-17 23:11:41 +00:00
< Button onClick = { this . handleClickSubmitInvoiceButton } variant = 'contained' color = 'primary' > Submit < / B u t t o n >
2022-01-16 21:54:42 +00:00
< / G r i d >
{ this . showBondIsLocked ( ) }
< / G r i d >
)
}
// Asks the user for a dispute statement.
2022-01-22 23:05:03 +00:00
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 >
< / T y p o g r a p h y >
< / G r i d >
< Grid item xs = { 12 } align = "left" >
< Typography component = "body2" variant = "body2" >
We are waiting for your trade counterparty statement .
< / T y p o g r a p h y >
< / G r i d >
{ this . showBondIsLocked ( ) }
< / G r i d >
)
} 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 >
< / T y p o g r a p h y >
< / G r i d >
< 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
2022-02-22 00:50:25 +00:00
evidence . It is best to provide a burner email , XMPP or telegram username to follow up with the staff .
2022-01-22 23:05:03 +00:00
Disputes are solved at the discretion of real robots < i > ( aka humans ) < / i > , s o b e a s h e l p f u l
as possible to ensure a fair outcome . Max 5000 chars .
< / T y p o g r a p h y >
< / G r i d >
< 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 }
/ >
< / G r i d >
< Grid item xs = { 12 } align = "center" >
< Button onClick = { this . handleClickSubmitStatementButton } variant = 'contained' color = 'primary' > Submit < / B u t t o n >
< / G r i d >
{ this . showBondIsLocked ( ) }
2022-01-15 12:00:11 +00:00
< / G r i d >
2022-01-22 23:05:03 +00:00
) }
2022-01-09 14:07:05 +00:00
}
2022-01-25 14:46:02 +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 >
< / T y p o g r a p h y >
< / G r i d >
< 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 .
< / T y p o g r a p h y >
< / G r i d >
{ this . showBondIsLocked ( ) }
< / G r i d >
)
}
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
< / T y p o g r a p h y >
< / G r i d >
< 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
< / T y p o g r a p h y >
< / G r i d >
2022-01-12 12:57:03 +00:00
{ this . showBondIsLocked ( ) }
2022-01-09 15:28:12 +00:00
< / G r i d >
)
}
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
< / T y p o g r a p h y >
< / G r i d >
< 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 >
< / T y p o g r a p h y >
< / G r i d >
2022-01-12 12:57:03 +00:00
{ this . showBondIsLocked ( ) }
2022-01-09 15:28:12 +00:00
< / G r i d >
)
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 ( ) )
2022-01-22 23:05:03 +00:00
. then ( ( data ) => this . props . completeSetState ( data ) ) ;
2022-01-09 15:28:12 +00:00
}
2022-01-17 23:11:41 +00:00
2022-02-04 18:07:09 +00:00
handleRatingUserChange = ( e ) => {
2022-01-11 20:49:53 +00:00
const requestOptions = {
method : 'POST' ,
headers : { 'Content-Type' : 'application/json' , 'X-CSRFToken' : getCookie ( 'csrftoken' ) , } ,
body : JSON . stringify ( {
2022-02-04 18:07:09 +00:00
'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" ,
2022-01-11 20:49:53 +00:00
'rating' : e . target . value ,
} ) ,
} ;
fetch ( '/api/order/' + '?order_id=' + this . props . data . id , requestOptions )
. then ( ( response ) => response . json ( ) )
2022-01-22 23:05:03 +00:00
. then ( ( data ) => this . props . completeSetState ( data ) ) ;
2022-01-11 20:49:53 +00:00
}
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" >
2022-01-13 19:22:54 +00:00
< Button defaultValue = "confirm" variant = 'contained' color = 'secondary' onClick = { this . handleClickConfirmButton } > Confirm { this . props . data . currencyCode } sent < / B u t t o n >
2022-01-09 20:05:19 +00:00
< / G r i d >
< / G r i d >
)
}
showFiatReceivedButton ( ) {
return (
< Grid item xs = { 12 } align = "center" >
2022-01-17 23:11:41 +00:00
< Button defaultValue = "confirm" variant = 'contained' color = 'secondary' onClick = { this . handleClickOpenConfirmFiatReceived } > Confirm { this . props . data . currencyCode } received < / B u t t o n >
2022-01-09 20:05:19 +00:00
< / G r i d >
)
}
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" >
2022-01-17 23:11:41 +00:00
< Button color = "inherit" onClick = { this . handleClickOpenConfirmDispute } > Open Dispute < / B u t t o n >
2022-01-09 20:05:19 +00:00
< / G r i d >
)
}
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 >
< / T y p o g r a p h y >
< / G r i d >
< / G r i d >
)
}
2022-01-19 22:44:31 +00:00
showChat = ( ) => {
//In Chatroom - No fiat sent - showChat(showSendButton, showReveiceButton, showDisputeButton)
2022-01-22 23:05:03 +00:00
if ( this . props . data . is _buyer & this . props . data . status == 9 ) {
2022-01-19 22:44:31 +00:00
var showSendButton = true ;
var showReveiceButton = false ;
var showDisputeButton = true ;
}
2022-01-22 23:05:03 +00:00
if ( this . props . data . is _seller & this . props . data . status == 9 ) {
2022-01-19 22:44:31 +00:00
var showSendButton = false ;
var showReveiceButton = false ;
var showDisputeButton = true ;
}
//In Chatroom - Fiat sent - showChat(showSendButton, showReveiceButton, showDisputeButton)
2022-01-22 23:05:03 +00:00
if ( this . props . data . is _buyer & this . props . data . status == 10 ) {
2022-01-19 22:44:31 +00:00
var showSendButton = false ;
var showReveiceButton = false ;
var showDisputeButton = true ;
}
2022-01-22 23:05:03 +00:00
if ( this . props . data . is _seller & this . props . data . status == 10 ) {
2022-01-19 22:44:31 +00:00
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" >
2022-01-22 23:05:03 +00:00
< 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
< / T y p o g r a p h y >
< / G r i d >
2022-01-13 20:41:48 +00:00
< Grid item xs = { 12 } align = "center" >
2022-01-22 23:05:03 +00:00
{ this . props . data . is _seller ?
2022-01-13 20:41:48 +00:00
< 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
< / T y p o g r a p h y >
:
2022-01-13 20:41:48 +00:00
< 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
< / T y p o g r a p h y >
}
2022-01-13 20:41:48 +00:00
< Divider / >
2022-01-09 20:05:19 +00:00
< / G r i d >
2022-01-13 19:22:54 +00:00
2022-01-22 23:05:03 +00:00
< 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" >
2022-01-19 22:44:31 +00:00
{ showDisputeButton ? this . showOpenDisputeButton ( ) : "" }
{ showSendButton ? this . showFiatSentButton ( ) : "" }
{ showReveiceButton ? this . showFiatReceivedButton ( ) : "" }
2022-01-09 15:28:12 +00:00
< / G r i d >
2022-01-12 12:57:03 +00:00
{ this . showBondIsLocked ( ) }
2022-01-09 15:28:12 +00:00
< / G r i d >
)
}
2022-01-11 20:49:53 +00:00
showRateSelect ( ) {
return (
< Grid container spacing = { 1 } >
2022-01-23 12:30:41 +00:00
{ /* Make confirmation sound for Chat Open. */ }
< this . Sound soundFileName = "successful" / >
2022-01-11 20:49:53 +00:00
< Grid item xs = { 12 } align = "center" >
< Typography component = "h6" variant = "h6" >
🎉 Trade finished ! 🥳
< / T y p o g r a p h y >
< / G r i d >
< Grid item xs = { 12 } align = "center" >
< Typography component = "body2" variant = "body2" align = "center" >
2022-02-04 18:07:09 +00:00
What do you think of ⚡ < b > { this . props . data . is _maker ? this . props . data . taker _nick : this . props . data . maker _nick } < / b > ⚡ ?
2022-01-11 20:49:53 +00:00
< / T y p o g r a p h y >
< / G r i d >
< Grid item xs = { 12 } align = "center" >
2022-02-04 18:07:09 +00:00
< Rating name = "size-large" defaultValue = { 0 } size = "large" onChange = { this . handleRatingUserChange } / >
< / G r i d >
< Grid item xs = { 12 } align = "center" >
< Typography component = "body2" variant = "body2" align = "center" >
What do you think of 🤖 < b > RoboSats < / b > 🤖 ?
< / T y p o g r a p h y >
2022-01-11 20:49:53 +00:00
< / G r i d >
2022-02-04 18:07:09 +00:00
< Grid item xs = { 12 } align = "center" >
< Rating name = "size-large" defaultValue = { 0 } size = "large" onChange = { this . handleRatingRobosatsChange } / >
< / G r i d >
{ 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 >
< / T y p o g r a p h y >
< / G r i d >
: 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 > )
< / T y p o g r a p h y >
< / G r i d >
: null }
2022-01-11 20:49:53 +00:00
< Grid item xs = { 12 } align = "center" >
2022-02-01 00:45:58 +00:00
< Button color = 'primary' onClick = { ( ) => { this . props . push ( '/' ) } } > Start Again < / B u t t o n >
2022-01-11 20:49:53 +00:00
< / G r i d >
< / G r i d >
)
}
2022-01-09 12:14:11 +00:00
2022-01-23 21:56:26 +00:00
showSendingPayment ( ) {
return (
< Grid container spacing = { 1 } >
< Grid item xs = { 12 } align = "center" >
< Typography component = "h6" variant = "h6" >
Attempting Lightning Payment
< / T y p o g r a p h y >
< / G r i d >
< 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 .
< / T y p o g r a p h y >
2022-02-04 18:07:09 +00:00
< br / >
< Grid item xs = { 12 } align = "center" >
< CircularProgress / >
< / G r i d >
2022-01-23 21:56:26 +00:00
< / G r i d >
< / G r i d >
)
}
2022-02-04 18:07:09 +00:00
// Countdown Renderer callback with condition
countdownRenderer = ( { minutes , seconds , completed } ) => {
if ( completed ) {
// Render a completed state
return ( < div align = "center" > < span > Retrying ! < /span><br/ > < CircularProgress / > < / d i v > ) ;
} else {
return (
< span > { zeroPad ( minutes ) } m { zeroPad ( seconds ) } s < / s p a n >
) ;
}
} ;
2022-01-24 22:53:55 +00:00
showRoutingFailed = ( ) => {
2022-01-23 21:56:26 +00:00
// TODO If it has failed 3 times, ask for a new invoice.
2022-01-24 22:53:55 +00:00
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
< / T y p o g r a p h y >
< / G r i d >
< 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 .
2022-01-24 22:53:55 +00:00
< / T y p o g r a p h y >
< / G r i d >
< 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 >
< / T y p o g r a p h y >
< / G r i d >
< 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 }
/ >
< / G r i d >
< Grid item xs = { 12 } align = "center" >
< Button onClick = { this . handleClickSubmitInvoiceButton } variant = 'contained' color = 'primary' > Submit < / B u t t o n >
< / G r i d >
< / G r i d >
)
} else {
2022-01-23 21:56:26 +00:00
return (
< Grid container spacing = { 1 } >
< Grid item xs = { 12 } align = "center" >
< Typography component = "h6" variant = "h6" >
Lightning Routing Failed
< / T y p o g r a p h y >
< / G r i d >
< Grid item xs = { 12 } align = "center" >
< Typography component = "body2" variant = "body2" align = "center" >
2022-01-24 22:53:55 +00:00
RoboSats will try to pay your invoice 3 times every 5 minutes . If it keeps failing , you
2022-01-23 21:56:26 +00:00
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 .
< / T y p o g r a p h y >
< List >
2022-01-24 22:53:55 +00:00
< Divider / >
2022-01-23 21:56:26 +00:00
< ListItemText secondary = "Next attempt in" >
< Countdown date = { new Date ( this . props . data . next _retry _time ) } renderer = { this . countdownRenderer } / >
< / L i s t I t e m T e x t >
< / L i s t >
< / G r i d >
< / G r i d >
2022-01-24 22:53:55 +00:00
) }
2022-01-23 21:56:26 +00:00
}
2022-01-08 13:08:03 +00:00
render ( ) {
return (
2022-01-29 14:42:54 +00:00
< Grid container spacing = { 1 } style = { { width : this . props . width } } >
2022-01-17 23:11:41 +00:00
< this . ConfirmDisputeDialog / >
< this . ConfirmFiatReceivedDialog / >
2022-01-08 13:08:03 +00:00
< Grid item xs = { 12 } align = "center" >
2022-01-27 22:51:57 +00:00
< MediaQuery minWidth = { 920 } >
< Typography component = "h5" variant = "h5" >
Contract Box
< / T y p o g r a p h y >
< / M e d i a Q u e r y >
2022-01-08 13:08:03 +00:00
< Paper elevation = { 12 } style = { { padding : 8 , } } >
2022-01-09 12:14:11 +00:00
{ /* Maker and taker Bond request */ }
2022-01-22 23:05:03 +00:00
{ 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 */ }
2022-01-22 23:05:03 +00:00
{ 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) */ }
2022-01-22 23:05:03 +00:00
{ 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
2022-01-19 22:44:31 +00:00
{ /* In Chatroom */ }
2022-01-22 23:05:03 +00:00
{ this . props . data . status == 9 || this . props . data . status == 10 ? this . showChat ( ) : "" }
2022-01-09 12:14:11 +00:00
{ /* Trade Finished */ }
2022-01-23 21:56:26 +00:00
{ ( this . props . data . is _seller & [ 13 , 14 , 15 ] . includes ( this . props . data . status ) ) ? this . showRateSelect ( ) : "" }
2022-01-22 23:05:03 +00:00
{ ( this . props . data . is _buyer & this . props . data . status == 14 ) ? this . showRateSelect ( ) : "" }
2022-01-11 20:49:53 +00:00
{ /* Trade Finished - Payment Routing Failed */ }
2022-01-23 21:56:26 +00:00
{ 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 ( ) : "" }
2022-01-11 20:49:53 +00:00
2022-01-16 21:54:42 +00:00
{ /* Trade Finished - TODO Needs more planning */ }
2022-01-22 23:05:03 +00:00
{ this . props . data . status == 11 ? this . showInDisputeStatement ( ) : "" }
2022-01-25 14:46:02 +00:00
{ this . props . data . status == 16 ? this . showWaitForDisputeResolution ( ) : "" }
2022-01-11 20:49:53 +00:00
2022-01-18 15:45:04 +00:00
{ /* Order has expired */ }
2022-01-22 23:05:03 +00:00
{ this . props . data . status == 5 ? this . showOrderExpired ( ) : "" }
2022-01-09 12:14:11 +00:00
{ /* TODO */ }
{ /* */ }
{ /* */ }
2022-01-09 12:35:19 +00:00
2022-01-08 13:08:03 +00:00
< / P a p e r >
< / G r i d >
< / G r i d >
) ;
}
}