Fix avatar persistance and login issues. Add onchain payment cleanup.

This commit is contained in:
Reckless_Satoshi 2022-06-20 10:56:08 -07:00
parent 7eb29fb57e
commit 6f7cfb5147
No known key found for this signature in database
GPG Key ID: 9C4585B561315571
9 changed files with 99 additions and 39 deletions

View File

@ -70,7 +70,7 @@ class OrderAdmin(AdminChangeLinksMixin, admin.ModelAdmin):
"taker_bond", "taker_bond",
"trade_escrow", "trade_escrow",
) )
list_filter = ("is_disputed", "is_fiat_sent", "type", "currency", "status") list_filter = ("is_disputed", "is_fiat_sent", "is_swap","type", "currency", "status")
search_fields = ["id","amount","min_amount","max_amount"] search_fields = ["id","amount","min_amount","max_amount"]
def amt(self, obj): def amt(self, obj):

View File

@ -293,6 +293,10 @@ class Logics:
Order.Status.MLD, Order.Status.MLD,
] ]
# in any case, if order is_swap and there is an onchain_payment, cancel it.
if not order.status in does_not_expire:
cls.cancel_onchain_payment(order)
if order.status in does_not_expire: if order.status in does_not_expire:
return False return False
@ -600,7 +604,7 @@ class Logics:
valid = cls.create_onchain_payment(order, user, preliminary_amount=context["invoice_amount"]) valid = cls.create_onchain_payment(order, user, preliminary_amount=context["invoice_amount"])
if not valid: if not valid:
context["swap_allowed"] = False context["swap_allowed"] = False
context["swap_failure_reason"] = "Not enough onchain liquidity available to offer a SWAP" context["swap_failure_reason"] = "Not enough onchain liquidity available to offer a swap"
return True, context return True, context
context["swap_allowed"] = True context["swap_allowed"] = True
@ -720,9 +724,7 @@ class Logics:
} }
# cancel onchain_payout if existing # cancel onchain_payout if existing
if order.payout_tx: cls.cancel_onchain_payment(order)
order.payout_tx.status = OnchainPayment.Status.CANCE
order.payout_tx.save()
num_satoshis = cls.payout_amount(order, user)[1]["invoice_amount"] num_satoshis = cls.payout_amount(order, user)[1]["invoice_amount"]
payout = LNNode.validate_ln_invoice(invoice, num_satoshis) payout = LNNode.validate_ln_invoice(invoice, num_satoshis)
@ -894,9 +896,12 @@ class Logics:
# 4.a) When maker cancel after bond (before escrow) # 4.a) When maker cancel after bond (before escrow)
"""The order into cancelled status if maker cancels.""" """The order into cancelled status if maker cancels."""
elif (order.status in [Order.Status.WF2,Order.Status.WFE] and order.maker == user): elif (order.status in [Order.Status.WF2,Order.Status.WFE] and order.maker == user):
# cancel onchain payment if existing
cls.cancel_onchain_payment(order)
# Settle the maker bond (Maker loses the bond for canceling an ongoing trade) # Settle the maker bond (Maker loses the bond for canceling an ongoing trade)
valid = cls.settle_bond(order.maker_bond) valid = cls.settle_bond(order.maker_bond)
cls.return_bond(order.taker_bond) # returns taker bond cls.return_bond(order.taker_bond) # returns taker bond
if valid: if valid:
order.status = Order.Status.UCA order.status = Order.Status.UCA
order.save() order.save()
@ -905,9 +910,11 @@ class Logics:
return True, None return True, None
# 4.b) When taker cancel after bond (before escrow) # 4.b) When taker cancel after bond (before escrow)
"""The order into cancelled status if maker cancels.""" """The order into cancelled status if mtker cancels."""
elif (order.status in [Order.Status.WF2, Order.Status.WFE] elif (order.status in [Order.Status.WF2, Order.Status.WFE]
and order.taker == user): and order.taker == user):
# cancel onchain payment if existing
cls.cancel_onchain_payment(order)
# Settle the maker bond (Maker loses the bond for canceling an ongoing trade) # Settle the maker bond (Maker loses the bond for canceling an ongoing trade)
valid = cls.settle_bond(order.taker_bond) valid = cls.settle_bond(order.taker_bond)
if valid: if valid:
@ -957,6 +964,8 @@ class Logics:
def collaborative_cancel(cls, order): def collaborative_cancel(cls, order):
if not order.status in [Order.Status.WFI, Order.Status.CHA]: if not order.status in [Order.Status.WFI, Order.Status.CHA]:
return return
# cancel onchain payment if existing
cls.cancel_onchain_payment(order)
cls.return_bond(order.maker_bond) cls.return_bond(order.maker_bond)
cls.return_bond(order.taker_bond) cls.return_bond(order.taker_bond)
cls.return_escrow(order) cls.return_escrow(order)
@ -1300,6 +1309,16 @@ class Logics:
else: else:
raise e raise e
def cancel_onchain_payment(order):
''' Cancel onchain_payment if existing '''
if order.payout_tx:
order.payout_tx.status = OnchainPayment.Status.CANCE
order.payout_tx.save()
return True
else:
return False
def cancel_bond(bond): def cancel_bond(bond):
"""cancel a bond""" """cancel a bond"""
# Same as return bond, but used when the invoice was never LOCKED # Same as return bond, but used when the invoice was never LOCKED

View File

@ -251,12 +251,7 @@ class OnchainPayment(models.Model):
default=None) default=None)
def __str__(self): def __str__(self):
if self.txid: return f"TX-{str(self.id)}: {self.Concepts(self.concept).label} - {self.Status(self.status).label}"
txname = str(self.txid)[:8]
else:
txname = str(self.id)
return f"TX-{txname}: {self.Concepts(self.concept).label} - {self.Status(self.status).label}"
class Meta: class Meta:
verbose_name = "Onchain payment" verbose_name = "Onchain payment"

View File

@ -167,15 +167,17 @@ def follow_send_payment(hash):
context = {"routing_failed": "The payout invoice has expired"} context = {"routing_failed": "The payout invoice has expired"}
return False, context return False, context
@shared_task(name="lnpayments_cleansing") @shared_task(name="payments_cleansing")
def lnpayments_cleansing(): def payments_cleansing():
""" """
Deletes cancelled lnpayments (hodl invoices never locked) that Deletes cancelled payments (hodl invoices never locked) that
belong to orders expired more than 3 days ago belong to orders expired more than 3 days ago.
Deletes cancelled onchain_payments
""" """
from django.db.models import Q from django.db.models import Q
from api.models import LNPayment from api.models import LNPayment
from api.models import OnchainPayment
from datetime import timedelta from datetime import timedelta
from django.utils import timezone from django.utils import timezone
@ -191,7 +193,7 @@ def lnpayments_cleansing():
# And do not have an active trade, any past contract or any reward. # And do not have an active trade, any past contract or any reward.
deleted_lnpayments = [] deleted_lnpayments = []
for lnpayment in queryset: for lnpayment in queryset:
# Try an except. In case some chatroom is already missing. # Try and except. In case some payment is already missing.
try: try:
name = str(lnpayment) name = str(lnpayment)
lnpayment.delete() lnpayment.delete()
@ -199,9 +201,27 @@ def lnpayments_cleansing():
except: except:
pass pass
# same for onchain payments
queryset = OnchainPayment.objects.filter(Q(status=OnchainPayment.Status.CANCEL),
Q(order_paid_TX__expires_at__lt=finished_time))
# And do not have an active trade, any past contract or any reward.
deleted_onchainpayments = []
for onchainpayment in queryset:
# Try and except. In case some payment is already missing.
try:
name = str(onchainpayment)
onchainpayment.delete()
deleted_onchainpayments.append(name)
except:
pass
results = { results = {
"num_deleted": len(deleted_lnpayments), "num_lnpayments_deleted": len(deleted_lnpayments),
"deleted_lnpayments": deleted_lnpayments, "deleted_lnpayments": deleted_lnpayments,
"num_onchainpayments_deleted": len(deleted_onchainpayments),
"deleted_onchainpayments": deleted_onchainpayments,
} }
return results return results

View File

@ -572,13 +572,13 @@ class UserView(APIView):
# If an existing user opens the main page by mistake, we do not want it to create a new nickname/profile for him # If an existing user opens the main page by mistake, we do not want it to create a new nickname/profile for him
if request.user.is_authenticated: if request.user.is_authenticated:
context = {"nickname": request.user.username} context = {"nickname": request.user.username}
not_participant, _, _ = Logics.validate_already_maker_or_taker( not_participant, _, order = Logics.validate_already_maker_or_taker(
request.user) request.user)
# Does not allow this 'mistake' if an active order # Does not allow this 'mistake' if an active order
if not not_participant: if not not_participant:
context[ context["active_order_id"] = order.id
"bad_request"] = f"You are already logged in as {request.user} and have an active order" context["bad_request"] = f"You are already logged in as {request.user} and have an active order"
return Response(context, status.HTTP_400_BAD_REQUEST) return Response(context, status.HTTP_400_BAD_REQUEST)
# Deprecated, kept temporarily for legacy reasons # Deprecated, kept temporarily for legacy reasons
@ -643,13 +643,13 @@ class UserView(APIView):
# If an existing user opens the main page by mistake, we do not want it to create a new nickname/profile for him # If an existing user opens the main page by mistake, we do not want it to create a new nickname/profile for him
if request.user.is_authenticated: if request.user.is_authenticated:
context = {"nickname": request.user.username} context = {"nickname": request.user.username}
not_participant, _, _ = Logics.validate_already_maker_or_taker( not_participant, _, order = Logics.validate_already_maker_or_taker(
request.user) request.user)
# Does not allow this 'mistake' if an active order # Does not allow this 'mistake' if an active order
if not not_participant: if not not_participant:
context[ context["active_order_id"] = order.id
"bad_request"] = f"You are already logged in as {request.user} and have an active order" context["bad_request"] = f"You are already logged in as {request.user} and have an active order"
return Response(context, status.HTTP_400_BAD_REQUEST) return Response(context, status.HTTP_400_BAD_REQUEST)
# The new way. The token is never sent. Only its SHA256 # The new way. The token is never sent. Only its SHA256
@ -748,6 +748,16 @@ class UserView(APIView):
login(request, user) login(request, user)
context["public_key"] = user.profile.public_key context["public_key"] = user.profile.public_key
context["encrypted_private_key"] = user.profile.encrypted_private_key context["encrypted_private_key"] = user.profile.encrypted_private_key
# return active order or last made order if any
has_no_active_order, _, order = Logics.validate_already_maker_or_taker(request.user)
if not has_no_active_order:
context["active_order_id"] = order.id
else:
last_order = Order.objects.filter(Q(maker=request.user) | Q(taker=request.user)).last()
if last_order:
context["last_order_id"] = last_order.id
# Sends the welcome back message, only if created +3 mins ago # Sends the welcome back message, only if created +3 mins ago
if request.user.date_joined < (timezone.now() - timedelta(minutes=3)): if request.user.date_joined < (timezone.now() - timedelta(minutes=3)):
context["found"] = "We found your Robot avatar. Welcome back!" context["found"] = "We found your Robot avatar. Welcome back!"

View File

@ -64,9 +64,10 @@ class BottomBar extends Component {
fetch('/api/info/') fetch('/api/info/')
.then((response) => response.json()) .then((response) => response.json())
.then((data) => this.setState(data) .then((data) => this.setState(data)
& this.setState({active_order_id: data.active_order_id ? data.active_order_id : null, & this.props.setAppState({nickname:data.nickname,
last_order_id: data.last_order_id ? data.last_order_id : null}) loading:false,
& this.props.setAppState({nickname:data.nickname, loading:false})); activeOrderId: data.active_order_id ? data.active_order_id : null,
lastOrderId: data.last_order_id ? data.last_order_id : null}));
} }
handleClickOpenStatsForNerds = () => { handleClickOpenStatsForNerds = () => {
@ -129,7 +130,7 @@ class BottomBar extends Component {
bottomBarDesktop =()=>{ bottomBarDesktop =()=>{
const { t } = this.props; const { t } = this.props;
var hasRewards = this.state.earned_rewards > 0 ? true: false; var hasRewards = this.state.earned_rewards > 0 ? true: false;
var hasOrder = this.state.active_order_id > 0 & !this.state.profileShown & this.props.avatarLoaded ? true : false; var hasOrder = this.props.activeOrderId > 0 & !this.state.profileShown & this.props.avatarLoaded ? true : false;
return( return(
<Paper elevation={6} style={{height:40}}> <Paper elevation={6} style={{height:40}}>
@ -144,7 +145,7 @@ bottomBarDesktop =()=>{
(hasOrder ? t("You have an active order"):"")} (hasOrder ? t("You have an active order"):"")}
> >
<ListItemAvatar sx={{ width: 30, height: 30 }} > <ListItemAvatar sx={{ width: 30, height: 30 }} >
<Badge badgeContent={(this.state.active_order_id > 0 & !this.state.profileShown) ? "": null} color="primary"> <Badge badgeContent={(this.props.activeOrderId > 0 & !this.props.profileShown) ? "": null} color="primary">
<Avatar className='flippedSmallAvatar' sx={{margin: 0, top: -13}} <Avatar className='flippedSmallAvatar' sx={{margin: 0, top: -13}}
alt={this.props.nickname} alt={this.props.nickname}
imgProps={{ imgProps={{
@ -164,6 +165,7 @@ bottomBarDesktop =()=>{
<ListItem className="bottomItem"> <ListItem className="bottomItem">
<ListItemIcon size="small"> <ListItemIcon size="small">
<IconButton <IconButton
disabled={!this.showProfileButton()}
color="primary" color="primary"
onClick={()=> this.props.setAppState({buyChecked: false, sellChecked: true, type:0}) & this.getInfo()} onClick={()=> this.props.setAppState({buyChecked: false, sellChecked: true, type:0}) & this.getInfo()}
to={`/book/`} to={`/book/`}
@ -183,6 +185,7 @@ bottomBarDesktop =()=>{
<ListItem className="bottomItem"> <ListItem className="bottomItem">
<ListItemIcon size="small"> <ListItemIcon size="small">
<IconButton <IconButton
disabled={!this.showProfileButton()}
color="primary" color="primary"
onClick={()=> this.props.setAppState({buyChecked: true, sellChecked: false, type:1}) & this.getInfo()} onClick={()=> this.props.setAppState({buyChecked: true, sellChecked: false, type:1}) & this.getInfo()}
to={`/book/`} to={`/book/`}
@ -201,7 +204,9 @@ bottomBarDesktop =()=>{
<Grid item xs={1.9}> <Grid item xs={1.9}>
<ListItem className="bottomItem"> <ListItem className="bottomItem">
<ListItemIcon size="small"> <ListItemIcon size="small">
<IconButton color="primary" <IconButton
disabled={!this.showProfileButton()}
color="primary"
onClick={()=> this.getInfo()} onClick={()=> this.getInfo()}
to={`/`} to={`/`}
component={LinkRouter} > component={LinkRouter} >
@ -350,6 +355,7 @@ bottomBarPhone =()=>{
<Grid item xs={1.6} align="center"> <Grid item xs={1.6} align="center">
<Tooltip enterTouchDelay={300} title={t("Number of public BUY orders")}> <Tooltip enterTouchDelay={300} title={t("Number of public BUY orders")}>
<IconButton <IconButton
disabled={!this.showProfileButton()}
color="primary" color="primary"
onClick={()=> this.props.setAppState({buyChecked: false, sellChecked: true, type:0}) & this.getInfo()} onClick={()=> this.props.setAppState({buyChecked: false, sellChecked: true, type:0}) & this.getInfo()}
to={`/book/`} to={`/book/`}
@ -364,6 +370,7 @@ bottomBarPhone =()=>{
<Grid item xs={1.6} align="center"> <Grid item xs={1.6} align="center">
<Tooltip enterTouchDelay={300} title={t("Number of public SELL orders")}> <Tooltip enterTouchDelay={300} title={t("Number of public SELL orders")}>
<IconButton <IconButton
disabled={!this.showProfileButton()}
color="primary" color="primary"
onClick={()=> this.props.setAppState({buyChecked: true, sellChecked: false, type:1}) & this.getInfo()} onClick={()=> this.props.setAppState({buyChecked: true, sellChecked: false, type:1}) & this.getInfo()}
to={`/book/`} to={`/book/`}
@ -377,7 +384,9 @@ bottomBarPhone =()=>{
<Grid item xs={1.6} align="center"> <Grid item xs={1.6} align="center">
<Tooltip enterTouchDelay={300} title={t("Today active robots")}> <Tooltip enterTouchDelay={300} title={t("Today active robots")}>
<IconButton color="primary" <IconButton
disabled={!this.showProfileButton()}
color="primary"
onClick={()=> this.getInfo()} onClick={()=> this.getInfo()}
to={`/`} to={`/`}
component={LinkRouter} > component={LinkRouter} >
@ -453,8 +462,8 @@ bottomBarPhone =()=>{
isOpen={this.state.openProfile} isOpen={this.state.openProfile}
handleClickCloseProfile={this.handleClickCloseProfile} handleClickCloseProfile={this.handleClickCloseProfile}
nickname={this.props.nickname} nickname={this.props.nickname}
activeOrderId={this.state.active_order_id} activeOrderId={this.props.activeOrderId}
lastOrderId={this.state.last_order_id} lastOrderId={this.props.lastOrderId}
referralCode={this.state.referral_code} referralCode={this.state.referral_code}
handleSubmitInvoiceClicked={this.handleSubmitInvoiceClicked} handleSubmitInvoiceClicked={this.handleSubmitInvoiceClicked}
host={this.getHost()} host={this.getHost()}

View File

@ -22,6 +22,8 @@ export default class HomePage extends Component {
bookCurrencyCode:'ANY', bookCurrencyCode:'ANY',
bookOrders:new Array(), bookOrders:new Array(),
bookLoading: true, bookLoading: true,
activeOrderId: null,
lastOrderId: null,
} }
} }

View File

@ -92,12 +92,16 @@ class UserGenPage extends Component {
(data.bad_request ? this.props.setAppState({ (data.bad_request ? this.props.setAppState({
nickname: data.nickname, nickname: data.nickname,
avatarLoaded: false, avatarLoaded: false,
activeOrderId: data.active_order_id ? data.active_order_id : null,
lastOrderId: data.last_order_id ? data.last_order_id : null,
}) })
: :
(this.props.setAppState({ (this.props.setAppState({
nickname: data.nickname, nickname: data.nickname,
token: token, token: token,
avatarLoaded: false, avatarLoaded: false,
activeOrderId: data.active_order_id ? data.active_order_id : null,
lastOrderId: data.last_order_id ? data.last_order_id : null,
})) & writeCookie("robot_token",token) })) & writeCookie("robot_token",token)
& writeCookie("pub_key",data.public_key.split('\n').join('\\')) & writeCookie("pub_key",data.public_key.split('\n').join('\\'))
& writeCookie("enc_priv_key",data.encrypted_private_key.split('\n').join('\\'))) & writeCookie("enc_priv_key",data.encrypted_private_key.split('\n').join('\\')))
@ -175,7 +179,7 @@ class UserGenPage extends Component {
<div> <div>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography component="h5" variant="h5"> <Typography component="h5" variant="h5">
<b>{this.state.nickname ? <b>{this.state.nickname && getCookie("sessionid") ?
<div style={{display:'flex', alignItems:'center', justifyContent:'center', flexWrap:'wrap', height:'45px'}}> <div style={{display:'flex', alignItems:'center', justifyContent:'center', flexWrap:'wrap', height:'45px'}}>
<BoltIcon sx={{ color: "#fcba03", height: '33px',width: '33px'}}/><a>{this.state.nickname}</a><BoltIcon sx={{ color: "#fcba03", height: '33px',width: '33px'}}/> <BoltIcon sx={{ color: "#fcba03", height: '33px',width: '33px'}}/><a>{this.state.nickname}</a><BoltIcon sx={{ color: "#fcba03", height: '33px',width: '33px'}}/>
</div> </div>
@ -185,11 +189,12 @@ class UserGenPage extends Component {
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Tooltip enterTouchDelay={0} title={t("This is your trading avatar")}> <Tooltip enterTouchDelay={0} title={t("This is your trading avatar")}>
<div style={{ maxWidth: 200, maxHeight: 200 }}> <div style={{ maxWidth: 200, maxHeight: 200 }}>
<Image className='newAvatar' <Image
className='newAvatar'
disableError={true} disableError={true}
cover={true} cover={true}
color='null' color='null'
src={this.state.avatar_url || ""} src={getCookie("sessionid") ? this.state.avatar_url || "" : ""}
/> />
</div> </div>
</Tooltip><br/> </Tooltip><br/>

View File

@ -15,7 +15,7 @@ export const getCookie = (name) => {
}; };
export const writeCookie = (key,value) => { export const writeCookie = (key,value) => {
document.cookie=`${key}=${value};path=/`; document.cookie=`${key}=${value};path=/;SameSite=Strict`;
} }
export const deleteCookie = (name) => { export const deleteCookie = (name) => {