mirror of
https://github.com/RoboSats/robosats.git
synced 2025-01-18 12:11:35 +00:00
Add background order updates. Add confirm boxes for Dispute and Fiat Received
This commit is contained in:
parent
0db73c7c82
commit
28d18a4842
@ -1,11 +1,14 @@
|
||||
from datetime import time, timedelta
|
||||
from datetime import timedelta
|
||||
from django.utils import timezone
|
||||
from .lightning.node import LNNode
|
||||
from api.lightning.node import LNNode
|
||||
|
||||
from .models import Order, LNPayment, MarketTick, User, Currency
|
||||
from api.models import Order, LNPayment, MarketTick, User, Currency
|
||||
from decouple import config
|
||||
|
||||
from api.tasks import follow_send_payment
|
||||
|
||||
import math
|
||||
import ast
|
||||
|
||||
FEE = float(config('FEE'))
|
||||
BOND_SIZE = float(config('BOND_SIZE'))
|
||||
@ -140,6 +143,7 @@ class Logics():
|
||||
|
||||
cls.settle_bond(order.maker_bond)
|
||||
cls.settle_bond(order.taker_bond)
|
||||
cls.cancel_escrow(order)
|
||||
order.status = Order.Status.EXP
|
||||
order.maker = None
|
||||
order.taker = None
|
||||
@ -152,6 +156,7 @@ class Logics():
|
||||
if maker_is_seller:
|
||||
cls.settle_bond(order.maker_bond)
|
||||
cls.return_bond(order.taker_bond)
|
||||
cls.cancel_escrow(order)
|
||||
order.status = Order.Status.EXP
|
||||
order.maker = None
|
||||
order.taker = None
|
||||
@ -161,22 +166,25 @@ class Logics():
|
||||
# If maker is buyer, settle the taker's bond order goes back to public
|
||||
else:
|
||||
cls.settle_bond(order.taker_bond)
|
||||
cls.cancel_escrow(order)
|
||||
order.status = Order.Status.PUB
|
||||
order.taker = None
|
||||
order.taker_bond = None
|
||||
order.trade_escrow = None
|
||||
order.expires_at = order.created_at + timedelta(seconds=Order.t_to_expire[Order.Status.PUB])
|
||||
order.save()
|
||||
return True
|
||||
|
||||
elif order.status == Order.Status.WFI:
|
||||
# The trade could happen without a buyer invoice. However, this user
|
||||
# is likely AFK since he did not submit an invoice; will probably
|
||||
# desert the contract as well.
|
||||
# is likely AFK; will probably desert the contract as well.
|
||||
|
||||
maker_is_buyer = cls.is_buyer(order, order.maker)
|
||||
# If maker is buyer, settle the bond and order goes to expired
|
||||
if maker_is_buyer:
|
||||
cls.settle_bond(order.maker_bond)
|
||||
cls.return_bond(order.taker_bond)
|
||||
cls.return_escrow(order)
|
||||
order.status = Order.Status.EXP
|
||||
order.maker = None
|
||||
order.taker = None
|
||||
@ -186,17 +194,19 @@ class Logics():
|
||||
# If maker is seller settle the taker's bond, order goes back to public
|
||||
else:
|
||||
cls.settle_bond(order.taker_bond)
|
||||
cls.return_escrow(order)
|
||||
order.status = Order.Status.PUB
|
||||
order.taker = None
|
||||
order.taker_bond = None
|
||||
order.trade_escrow = None
|
||||
order.expires_at = order.created_at + timedelta(seconds=Order.t_to_expire[Order.Status.PUB])
|
||||
order.save()
|
||||
return True
|
||||
|
||||
elif order.status == Order.Status.CHA:
|
||||
# Another weird case. The time to confirm 'fiat sent' expired. Yet no dispute
|
||||
# was opened. A seller-scammer could persuade a buyer to not click "fiat sent"
|
||||
# as of now, we assume this is a dispute case by default.
|
||||
# was opened. Hint: a seller-scammer could persuade a buyer to not click "fiat
|
||||
# sent", we assume this is a dispute case by default.
|
||||
cls.open_dispute(order)
|
||||
return True
|
||||
|
||||
@ -219,12 +229,14 @@ class Logics():
|
||||
def open_dispute(cls, order, user=None):
|
||||
|
||||
# Always settle the escrow during a dispute (same as with 'Fiat Sent')
|
||||
# Dispute winner will have to submit a new invoice.
|
||||
|
||||
if not order.trade_escrow.status == LNPayment.Status.SETLED:
|
||||
cls.settle_escrow(order)
|
||||
|
||||
order.is_disputed = True
|
||||
order.status = Order.Status.DIS
|
||||
order.expires_at = order.created_at + timedelta(seconds=Order.t_to_expire[Order.Status.DIS])
|
||||
order.expires_at = timezone.now() + timedelta(seconds=Order.t_to_expire[Order.Status.DIS])
|
||||
order.save()
|
||||
|
||||
# User could be None if a dispute is open automatically due to weird expiration.
|
||||
@ -235,6 +247,7 @@ class Logics():
|
||||
profile.save()
|
||||
|
||||
return True, None
|
||||
|
||||
def dispute_statement(order, user, statement):
|
||||
''' Updates the dispute statements in DB'''
|
||||
if not order.status == Order.Status.DIS:
|
||||
@ -319,16 +332,18 @@ class Logics():
|
||||
def add_profile_rating(profile, rating):
|
||||
''' adds a new rating to a user profile'''
|
||||
|
||||
# TODO Unsafe, does not update ratings, it adds more ratings everytime a new rating is clicked.
|
||||
profile.total_ratings = profile.total_ratings + 1
|
||||
latest_ratings = profile.latest_ratings
|
||||
if len(latest_ratings) <= 1:
|
||||
if latest_ratings == None:
|
||||
profile.latest_ratings = [rating]
|
||||
profile.avg_rating = rating
|
||||
|
||||
else:
|
||||
latest_ratings = list(latest_ratings).append(rating)
|
||||
latest_ratings = ast.literal_eval(latest_ratings)
|
||||
latest_ratings.append(rating)
|
||||
profile.latest_ratings = latest_ratings
|
||||
profile.avg_rating = sum(latest_ratings) / len(latest_ratings)
|
||||
profile.avg_rating = sum(list(map(int, latest_ratings))) / len(latest_ratings) # Just an average, but it is a list of strings. Has to be converted to int.
|
||||
|
||||
profile.save()
|
||||
|
||||
@ -413,15 +428,20 @@ class Logics():
|
||||
else:
|
||||
return False, {'bad_request':'You cannot cancel this order'}
|
||||
|
||||
def publish_order(order):
|
||||
if order.status == Order.Status.WFB:
|
||||
order.status = Order.Status.PUB
|
||||
# With the bond confirmation the order is extended 'public_order_duration' hours
|
||||
order.expires_at = order.created_at + timedelta(seconds=Order.t_to_expire[Order.Status.PUB])
|
||||
order.save()
|
||||
return
|
||||
|
||||
@classmethod
|
||||
def is_maker_bond_locked(cls, order):
|
||||
if LNNode.validate_hold_invoice_locked(order.maker_bond.payment_hash):
|
||||
order.maker_bond.status = LNPayment.Status.LOCKED
|
||||
order.maker_bond.save()
|
||||
order.status = Order.Status.PUB
|
||||
# With the bond confirmation the order is extended 'public_order_duration' hours
|
||||
order.expires_at = order.created_at + timedelta(seconds=Order.t_to_expire[Order.Status.PUB])
|
||||
order.save()
|
||||
cls.publish_order(order)
|
||||
return True
|
||||
return False
|
||||
|
||||
@ -467,13 +487,12 @@ class Logics():
|
||||
return True, {'bond_invoice':hold_payment['invoice'], 'bond_satoshis':bond_satoshis}
|
||||
|
||||
@classmethod
|
||||
def is_taker_bond_locked(cls, order):
|
||||
if order.taker_bond.status == LNPayment.Status.LOCKED:
|
||||
return True
|
||||
def finalize_contract(cls, order):
|
||||
''' When the taker locks the taker_bond
|
||||
the contract is final '''
|
||||
|
||||
if LNNode.validate_hold_invoice_locked(order.taker_bond.payment_hash):
|
||||
# THE TRADE AMOUNT IS FINAL WITH THE CONFIRMATION OF THE TAKER BOND!
|
||||
# (This is the last update to "last_satoshis", it becomes the escrow amount next!)
|
||||
# (This is the last update to "last_satoshis", it becomes the escrow amount next)
|
||||
order.last_satoshis = cls.satoshis_now(order)
|
||||
order.taker_bond.status = LNPayment.Status.LOCKED
|
||||
order.taker_bond.save()
|
||||
@ -492,6 +511,14 @@ class Logics():
|
||||
order.status = Order.Status.WF2
|
||||
order.save()
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def is_taker_bond_locked(cls, order):
|
||||
if order.taker_bond.status == LNPayment.Status.LOCKED:
|
||||
return True
|
||||
if LNNode.validate_hold_invoice_locked(order.taker_bond.payment_hash):
|
||||
cls.finalize_contract(order)
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
@ -618,11 +645,17 @@ class Logics():
|
||||
order.trade_escrow.status = LNPayment.Status.RETNED
|
||||
return True
|
||||
|
||||
def cancel_escrow(order):
|
||||
'''returns the trade escrow'''
|
||||
# Same as return escrow, but used when the invoice was never LOCKED
|
||||
if LNNode.cancel_return_hold_invoice(order.trade_escrow.payment_hash):
|
||||
order.trade_escrow.status = LNPayment.Status.CANCEL
|
||||
return True
|
||||
|
||||
def return_bond(bond):
|
||||
'''returns a bond'''
|
||||
if bond == None:
|
||||
return
|
||||
|
||||
try:
|
||||
LNNode.cancel_return_hold_invoice(bond.payment_hash)
|
||||
bond.status = LNPayment.Status.RETNED
|
||||
@ -631,10 +664,12 @@ class Logics():
|
||||
if 'invoice already settled' in str(e):
|
||||
bond.status = LNPayment.Status.SETLED
|
||||
return True
|
||||
else:
|
||||
raise e
|
||||
|
||||
def cancel_bond(bond):
|
||||
'''cancel a bond'''
|
||||
# Same as return bond, but used when the invoice was never accepted
|
||||
# Same as return bond, but used when the invoice was never LOCKED
|
||||
if bond == None:
|
||||
return True
|
||||
try:
|
||||
@ -645,11 +680,12 @@ class Logics():
|
||||
if 'invoice already settled' in str(e):
|
||||
bond.status = LNPayment.Status.SETLED
|
||||
return True
|
||||
else:
|
||||
raise e
|
||||
|
||||
def pay_buyer_invoice(order):
|
||||
''' Pay buyer invoice'''
|
||||
# TODO ERROR HANDLING
|
||||
suceeded, context = LNNode.pay_invoice(order.buyer_invoice.invoice, order.buyer_invoice.num_satoshis)
|
||||
suceeded, context = follow_send_payment(order.buyer_invoice)
|
||||
return suceeded, context
|
||||
|
||||
@classmethod
|
||||
@ -703,11 +739,15 @@ class Logics():
|
||||
# If the trade is finished
|
||||
if order.status > Order.Status.PAY:
|
||||
# if maker, rates taker
|
||||
if order.maker == user:
|
||||
if order.maker == user and order.maker_rated == False:
|
||||
cls.add_profile_rating(order.taker.profile, rating)
|
||||
order.maker_rated = True
|
||||
order.save()
|
||||
# if taker, rates maker
|
||||
if order.taker == user:
|
||||
if order.taker == user and order.taker_rated == False:
|
||||
cls.add_profile_rating(order.maker.profile, rating)
|
||||
order.taker_rated = True
|
||||
order.save()
|
||||
else:
|
||||
return False, {'bad_request':'You cannot rate your counterparty yet.'}
|
||||
|
||||
|
@ -36,6 +36,7 @@ class Command(BaseCommand):
|
||||
context = str(order)+ " was "+ Order.Status(order.status).label
|
||||
if Logics.order_expires(order): # Order send to expire here
|
||||
debug['expired_orders'].append({idx:context})
|
||||
|
||||
self.stdout.write(str(timezone.now()))
|
||||
self.stdout.write(str(debug))
|
||||
|
||||
if debug['num_expired_orders'] > 0:
|
||||
self.stdout.write(str(timezone.now()))
|
||||
self.stdout.write(str(debug))
|
||||
|
@ -1,21 +1,25 @@
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
|
||||
from django.utils import timezone
|
||||
from api.lightning.node import LNNode
|
||||
from api.models import LNPayment, Order
|
||||
from api.logics import Logics
|
||||
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
from decouple import config
|
||||
from base64 import b64decode
|
||||
from api.models import LNPayment
|
||||
import time
|
||||
|
||||
MACAROON = b64decode(config('LND_MACAROON_BASE64'))
|
||||
|
||||
class Command(BaseCommand):
|
||||
'''
|
||||
Background: SubscribeInvoices stub iterator would be great to use here
|
||||
however it only sends updates when the invoice is OPEN (new) or SETTLED.
|
||||
Background: SubscribeInvoices stub iterator would be great to use here.
|
||||
However, it only sends updates when the invoice is OPEN (new) or SETTLED.
|
||||
We are very interested on the other two states (CANCELLED and ACCEPTED).
|
||||
Therefore, this thread (follow_invoices) will iterate over all LNpayment
|
||||
objects and do InvoiceLookupV2 to update their state 'live' '''
|
||||
objects and do InvoiceLookupV2 every X seconds to update their state 'live'
|
||||
'''
|
||||
|
||||
help = 'Follows all active hold invoices'
|
||||
|
||||
@ -27,10 +31,10 @@ class Command(BaseCommand):
|
||||
until settled or canceled'''
|
||||
|
||||
lnd_state_to_lnpayment_status = {
|
||||
0: LNPayment.Status.INVGEN,
|
||||
1: LNPayment.Status.SETLED,
|
||||
2: LNPayment.Status.CANCEL,
|
||||
3: LNPayment.Status.LOCKED
|
||||
0: LNPayment.Status.INVGEN, # OPEN
|
||||
1: LNPayment.Status.SETLED, # SETTLED
|
||||
2: LNPayment.Status.CANCEL, # CANCELLED
|
||||
3: LNPayment.Status.LOCKED # ACCEPTED
|
||||
}
|
||||
|
||||
stub = LNNode.invoicesstub
|
||||
@ -45,6 +49,7 @@ class Command(BaseCommand):
|
||||
debug = {}
|
||||
debug['num_active_invoices'] = len(queryset)
|
||||
debug['invoices'] = []
|
||||
at_least_one_changed = False
|
||||
|
||||
for idx, hold_lnpayment in enumerate(queryset):
|
||||
old_status = LNPayment.Status(hold_lnpayment.status).label
|
||||
@ -56,29 +61,55 @@ class Command(BaseCommand):
|
||||
|
||||
# If it fails at finding the invoice it has been canceled.
|
||||
# On RoboSats DB we make a distinction between cancelled and returned (LND does not)
|
||||
except:
|
||||
hold_lnpayment.status = LNPayment.Status.CANCEL
|
||||
continue
|
||||
except Exception as e:
|
||||
if 'unable to locate invoice' in str(e):
|
||||
hold_lnpayment.status = LNPayment.Status.CANCEL
|
||||
else:
|
||||
self.stdout.write(str(e))
|
||||
|
||||
new_status = LNPayment.Status(hold_lnpayment.status).label
|
||||
|
||||
# Only save the hold_payments that change (otherwise this function does not scale)
|
||||
changed = not old_status==new_status
|
||||
if changed:
|
||||
# self.handle_status_change(hold_lnpayment, old_status)
|
||||
hold_lnpayment.save()
|
||||
self.update_order_status(hold_lnpayment)
|
||||
|
||||
# Report for debugging
|
||||
new_status = LNPayment.Status(hold_lnpayment.status).label
|
||||
debug['invoices'].append({idx:{
|
||||
'payment_hash': str(hold_lnpayment.payment_hash),
|
||||
'status_changed': not old_status==new_status,
|
||||
'old_status': old_status,
|
||||
'new_status': new_status,
|
||||
}})
|
||||
# Report for debugging
|
||||
new_status = LNPayment.Status(hold_lnpayment.status).label
|
||||
debug['invoices'].append({idx:{
|
||||
'payment_hash': str(hold_lnpayment.payment_hash),
|
||||
'old_status': old_status,
|
||||
'new_status': new_status,
|
||||
}})
|
||||
|
||||
debug['time']=time.time()-t0
|
||||
|
||||
self.stdout.write(str(timezone.now())+str(debug))
|
||||
at_least_one_changed = at_least_one_changed or changed
|
||||
|
||||
debug['time']=time.time()-t0
|
||||
|
||||
if at_least_one_changed:
|
||||
self.stdout.write(str(timezone.now()))
|
||||
self.stdout.write(str(debug))
|
||||
|
||||
|
||||
|
||||
def update_order_status(self, lnpayment):
|
||||
''' Background process following LND hold invoices
|
||||
might catch LNpayments changing status. If they do,
|
||||
the order status might have to change status too.'''
|
||||
|
||||
# If the LNPayment goes to LOCKED (ACCEPTED)
|
||||
if lnpayment.status == LNPayment.Status.LOCKED:
|
||||
|
||||
# It is a maker bond => Publish order.
|
||||
order = lnpayment.order_made
|
||||
if not order == None:
|
||||
Logics.publish_order(order)
|
||||
return
|
||||
|
||||
# It is a taker bond => close contract.
|
||||
order = lnpayment.order_taken
|
||||
if not order == None:
|
||||
if order.status == Order.Status.TAK:
|
||||
Logics.finalize_contract(order)
|
||||
return
|
@ -146,16 +146,16 @@ class Order(models.Model):
|
||||
|
||||
# LNpayments
|
||||
# Order collateral
|
||||
maker_bond = models.ForeignKey(LNPayment, related_name='maker_bond', on_delete=models.SET_NULL, null=True, default=None, blank=True)
|
||||
taker_bond = models.ForeignKey(LNPayment, related_name='taker_bond', on_delete=models.SET_NULL, null=True, default=None, blank=True)
|
||||
trade_escrow = models.ForeignKey(LNPayment, related_name='trade_escrow', on_delete=models.SET_NULL, null=True, default=None, blank=True)
|
||||
maker_bond = models.OneToOneField(LNPayment, related_name='order_made', on_delete=models.SET_NULL, null=True, default=None, blank=True)
|
||||
taker_bond = models.OneToOneField(LNPayment, related_name='order_taken', on_delete=models.SET_NULL, null=True, default=None, blank=True)
|
||||
trade_escrow = models.OneToOneField(LNPayment, related_name='order_escrow', on_delete=models.SET_NULL, null=True, default=None, blank=True)
|
||||
|
||||
# buyer payment LN invoice
|
||||
buyer_invoice = models.ForeignKey(LNPayment, related_name='buyer_invoice', on_delete=models.SET_NULL, null=True, default=None, blank=True)
|
||||
|
||||
# Unused so far. Cancel LN invoices // these are only needed to charge lower-than-bond amounts. E.g., a taken order has a small cost if cancelled, to avoid DDOSing.
|
||||
# maker_cancel = models.ForeignKey(LNPayment, related_name='maker_cancel', on_delete=models.SET_NULL, null=True, default=None, blank=True)
|
||||
# taker_cancel = models.ForeignKey(LNPayment, related_name='taker_cancel', on_delete=models.SET_NULL, null=True, default=None, blank=True)
|
||||
# ratings
|
||||
maker_rated = models.BooleanField(default=False, null=False)
|
||||
taker_rated = models.BooleanField(default=False, null=False)
|
||||
|
||||
t_to_expire = {
|
||||
0 : int(config('EXP_MAKER_BOND_INVOICE')) , # 'Waiting for maker bond'
|
||||
@ -182,6 +182,7 @@ class Order(models.Model):
|
||||
def __str__(self):
|
||||
# Make relational back to ORDER
|
||||
return (f'Order {self.id}: {self.Types(self.type).label} BTC for {float(self.amount)} {self.currency}')
|
||||
|
||||
|
||||
@receiver(pre_delete, sender=Order)
|
||||
def delete_lnpayment_at_order_deletion(sender, instance, **kwargs):
|
||||
|
@ -61,7 +61,7 @@ export default class OrderPage extends Component {
|
||||
"8": 10000, //'Waiting only for buyer invoice'
|
||||
"9": 10000, //'Sending fiat - In chatroom'
|
||||
"10": 15000, //'Fiat sent - In chatroom'
|
||||
"11": 300000, //'In dispute'
|
||||
"11": 60000, //'In dispute'
|
||||
"12": 9999999,//'Collaboratively cancelled'
|
||||
"13": 120000, //'Sending satoshis to buyer'
|
||||
"14": 9999999,//'Sucessful trade'
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { Component } from "react";
|
||||
import { Link, Paper, Rating, Button, Grid, Typography, TextField, List, ListItem, ListItemText, Divider, ListItemIcon} from "@mui/material"
|
||||
import { Link, Paper, Rating, Button, Grid, Typography, TextField, List, ListItem, ListItemText, Divider, ListItemIcon, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle} from "@mui/material"
|
||||
import QRCode from "react-qr-code";
|
||||
|
||||
import Chat from "./Chat"
|
||||
@ -37,11 +37,100 @@ export default class TradeBox extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
openConfirmFiatReceived: false,
|
||||
openConfirmDispute: false,
|
||||
badInvoice: false,
|
||||
badStatement: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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.data = 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">
|
||||
The RoboSats staff will examine the statements and evidence provided by the participants.
|
||||
It is best if you provide a burner contact method on your statement for the staff to contact you.
|
||||
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>
|
||||
<Button onClick={this.handleClickAgreeDisputeButton}> Agree </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>
|
||||
)
|
||||
}
|
||||
|
||||
showQRInvoice=()=>{
|
||||
return (
|
||||
<Grid container spacing={1}>
|
||||
@ -275,7 +364,7 @@ export default class TradeBox extends Component {
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<Button onClick={this.handleClickSubmitStatementButton} variant='contained' color='primary'>Submit</Button>
|
||||
<Button onClick={this.handleClickSubmitInvoiceButton} variant='contained' color='primary'>Submit</Button>
|
||||
</Grid>
|
||||
|
||||
{this.showBondIsLocked()}
|
||||
@ -382,18 +471,7 @@ export default class TradeBox extends Component {
|
||||
.then((response) => response.json())
|
||||
.then((data) => (this.props.data = data));
|
||||
}
|
||||
handleClickOpenDisputeButton=()=>{
|
||||
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.data = data));
|
||||
}
|
||||
|
||||
handleRatingChange=(e)=>{
|
||||
const requestOptions = {
|
||||
method: 'POST',
|
||||
@ -419,11 +497,9 @@ handleRatingChange=(e)=>{
|
||||
}
|
||||
|
||||
showFiatReceivedButton(){
|
||||
// TODO, show alert and ask for double confirmation (Have you check you received the fiat? Confirming fiat received settles the trade.)
|
||||
// Ask for double confirmation.
|
||||
return(
|
||||
<Grid item xs={12} align="center">
|
||||
<Button defaultValue="confirm" variant='contained' color='secondary' onClick={this.handleClickConfirmButton}>Confirm {this.props.data.currencyCode} received</Button>
|
||||
<Button defaultValue="confirm" variant='contained' color='secondary' onClick={this.handleClickOpenConfirmFiatReceived}>Confirm {this.props.data.currencyCode} received</Button>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
@ -432,7 +508,7 @@ handleRatingChange=(e)=>{
|
||||
// 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.handleClickOpenDisputeButton}>Open Dispute</Button>
|
||||
<Button color="inherit" onClick={this.handleClickOpenConfirmDispute}>Open Dispute</Button>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
@ -487,7 +563,7 @@ handleRatingChange=(e)=>{
|
||||
<Rating name="size-large" defaultValue={2} size="large" onChange={this.handleRatingChange} />
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<Button color='primary' to='/' component={Link}>Start Again</Button>
|
||||
<Button color='primary' href='/' component="a">Start Again</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
@ -497,6 +573,8 @@ handleRatingChange=(e)=>{
|
||||
render() {
|
||||
return (
|
||||
<Grid container spacing={1} style={{ width:330}}>
|
||||
<this.ConfirmDisputeDialog/>
|
||||
<this.ConfirmFiatReceivedDialog/>
|
||||
<Grid item xs={12} align="center">
|
||||
<Typography component="h5" variant="h5">
|
||||
Contract Box
|
||||
|
Loading…
Reference in New Issue
Block a user