mirror of
https://github.com/RoboSats/robosats.git
synced 2025-01-19 04:31:35 +00:00
Add maker selects public duration
This commit is contained in:
parent
967c441bb4
commit
a36f23b572
@ -82,7 +82,7 @@ EXP_TAKER_BOND_INVOICE = 200
|
|||||||
# Time a order is public in the book HOURS
|
# Time a order is public in the book HOURS
|
||||||
DEFAULT_PUBLIC_ORDER_DURATION = 24
|
DEFAULT_PUBLIC_ORDER_DURATION = 24
|
||||||
MAX_PUBLIC_ORDER_DURATION = 24
|
MAX_PUBLIC_ORDER_DURATION = 24
|
||||||
MIN_PUBLIC_ORDER_DURATION = 0.25
|
MIN_PUBLIC_ORDER_DURATION = 0.166
|
||||||
|
|
||||||
# Time to provide a valid invoice and the trade escrow MINUTES
|
# Time to provide a valid invoice and the trade escrow MINUTES
|
||||||
INVOICE_AND_ESCROW_DURATION = 30
|
INVOICE_AND_ESCROW_DURATION = 30
|
||||||
|
11
api/admin.py
11
api/admin.py
@ -13,21 +13,25 @@ class ProfileInline(admin.StackedInline):
|
|||||||
can_delete = False
|
can_delete = False
|
||||||
fields = ("avatar_tag", )
|
fields = ("avatar_tag", )
|
||||||
readonly_fields = ["avatar_tag"]
|
readonly_fields = ["avatar_tag"]
|
||||||
|
show_change_link = True
|
||||||
|
|
||||||
# extended users with avatars
|
# extended users with avatars
|
||||||
@admin.register(User)
|
@admin.register(User)
|
||||||
class EUserAdmin(UserAdmin):
|
class EUserAdmin(AdminChangeLinksMixin, UserAdmin):
|
||||||
inlines = [ProfileInline]
|
inlines = [ProfileInline]
|
||||||
list_display = (
|
list_display = (
|
||||||
"avatar_tag",
|
"avatar_tag",
|
||||||
"id",
|
"id",
|
||||||
|
"profile_link",
|
||||||
"username",
|
"username",
|
||||||
"last_login",
|
"last_login",
|
||||||
"date_joined",
|
"date_joined",
|
||||||
"is_staff",
|
"is_staff",
|
||||||
)
|
)
|
||||||
list_display_links = ("id", "username")
|
list_display_links = ("id", "username")
|
||||||
|
change_links = (
|
||||||
|
"profile",
|
||||||
|
)
|
||||||
ordering = ("-id", )
|
ordering = ("-id", )
|
||||||
|
|
||||||
def avatar_tag(self, obj):
|
def avatar_tag(self, obj):
|
||||||
@ -95,7 +99,7 @@ class LNPaymentAdmin(AdminChangeLinksMixin, admin.ModelAdmin):
|
|||||||
)
|
)
|
||||||
list_filter = ("type", "concept", "status")
|
list_filter = ("type", "concept", "status")
|
||||||
ordering = ("-expires_at", )
|
ordering = ("-expires_at", )
|
||||||
search_fields = ["payment_hash","num_satoshis"]
|
search_fields = ["payment_hash","num_satoshis","sender__username","receiver__username","description"]
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Profile)
|
@admin.register(Profile)
|
||||||
@ -119,6 +123,7 @@ class UserProfileAdmin(AdminChangeLinksMixin, admin.ModelAdmin):
|
|||||||
list_display_links = ("avatar_tag", "id")
|
list_display_links = ("avatar_tag", "id")
|
||||||
change_links = ["user"]
|
change_links = ["user"]
|
||||||
readonly_fields = ["avatar_tag"]
|
readonly_fields = ["avatar_tag"]
|
||||||
|
search_fields = ["user__username","id"]
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Currency)
|
@admin.register(Currency)
|
||||||
|
@ -27,7 +27,6 @@ EXP_TAKER_BOND_INVOICE = int(config("EXP_TAKER_BOND_INVOICE"))
|
|||||||
BOND_EXPIRY = int(config("BOND_EXPIRY"))
|
BOND_EXPIRY = int(config("BOND_EXPIRY"))
|
||||||
ESCROW_EXPIRY = int(config("ESCROW_EXPIRY"))
|
ESCROW_EXPIRY = int(config("ESCROW_EXPIRY"))
|
||||||
|
|
||||||
PUBLIC_ORDER_DURATION = int(config("PUBLIC_ORDER_DURATION"))
|
|
||||||
INVOICE_AND_ESCROW_DURATION = int(config("INVOICE_AND_ESCROW_DURATION"))
|
INVOICE_AND_ESCROW_DURATION = int(config("INVOICE_AND_ESCROW_DURATION"))
|
||||||
FIAT_EXCHANGE_DURATION = int(config("FIAT_EXCHANGE_DURATION"))
|
FIAT_EXCHANGE_DURATION = int(config("FIAT_EXCHANGE_DURATION"))
|
||||||
|
|
||||||
@ -129,7 +128,7 @@ class Logics:
|
|||||||
order.taker = user
|
order.taker = user
|
||||||
order.status = Order.Status.TAK
|
order.status = Order.Status.TAK
|
||||||
order.expires_at = timezone.now() + timedelta(
|
order.expires_at = timezone.now() + timedelta(
|
||||||
seconds=Order.t_to_expire[Order.Status.TAK])
|
seconds=order.t_to_expire(Order.Status.TAK))
|
||||||
order.save()
|
order.save()
|
||||||
# send_message.delay(order.id,'order_taken') # Too spammy
|
# send_message.delay(order.id,'order_taken') # Too spammy
|
||||||
return True, None
|
return True, None
|
||||||
@ -336,7 +335,7 @@ class Logics:
|
|||||||
order.is_disputed = True
|
order.is_disputed = True
|
||||||
order.status = Order.Status.DIS
|
order.status = Order.Status.DIS
|
||||||
order.expires_at = timezone.now() + timedelta(
|
order.expires_at = timezone.now() + timedelta(
|
||||||
seconds=Order.t_to_expire[Order.Status.DIS])
|
seconds=order.t_to_expire(Order.Status.DIS))
|
||||||
order.save()
|
order.save()
|
||||||
|
|
||||||
# User could be None if a dispute is open automatically due to weird expiration.
|
# User could be None if a dispute is open automatically due to weird expiration.
|
||||||
@ -380,7 +379,7 @@ class Logics:
|
|||||||
if order.maker_statement not in [None,""] and order.taker_statement not in [None,""]:
|
if order.maker_statement not in [None,""] and order.taker_statement not in [None,""]:
|
||||||
order.status = Order.Status.WFR
|
order.status = Order.Status.WFR
|
||||||
order.expires_at = timezone.now() + timedelta(
|
order.expires_at = timezone.now() + timedelta(
|
||||||
seconds=Order.t_to_expire[Order.Status.WFR])
|
seconds=order.t_to_expire(Order.Status.WFR))
|
||||||
|
|
||||||
order.save()
|
order.save()
|
||||||
return True, None
|
return True, None
|
||||||
@ -472,7 +471,7 @@ class Logics:
|
|||||||
if order.status == Order.Status.WFI:
|
if order.status == Order.Status.WFI:
|
||||||
order.status = Order.Status.CHA
|
order.status = Order.Status.CHA
|
||||||
order.expires_at = timezone.now() + timedelta(
|
order.expires_at = timezone.now() + timedelta(
|
||||||
seconds=Order.t_to_expire[Order.Status.CHA])
|
seconds=order.t_to_expire(Order.Status.CHA))
|
||||||
|
|
||||||
# If the order status is 'Waiting for both'. Move forward to 'waiting for escrow'
|
# If the order status is 'Waiting for both'. Move forward to 'waiting for escrow'
|
||||||
if order.status == Order.Status.WF2:
|
if order.status == Order.Status.WF2:
|
||||||
@ -483,7 +482,7 @@ class Logics:
|
|||||||
elif order.trade_escrow.status == LNPayment.Status.LOCKED:
|
elif order.trade_escrow.status == LNPayment.Status.LOCKED:
|
||||||
order.status = Order.Status.CHA
|
order.status = Order.Status.CHA
|
||||||
order.expires_at = timezone.now() + timedelta(
|
order.expires_at = timezone.now() + timedelta(
|
||||||
seconds=Order.t_to_expire[Order.Status.CHA])
|
seconds=order.t_to_expire(Order.Status.CHA))
|
||||||
else:
|
else:
|
||||||
order.status = Order.Status.WFE
|
order.status = Order.Status.WFE
|
||||||
|
|
||||||
@ -661,7 +660,7 @@ class Logics:
|
|||||||
def publish_order(order):
|
def publish_order(order):
|
||||||
order.status = Order.Status.PUB
|
order.status = Order.Status.PUB
|
||||||
order.expires_at = order.created_at + timedelta(
|
order.expires_at = order.created_at + timedelta(
|
||||||
seconds=Order.t_to_expire[Order.Status.PUB])
|
seconds=order.t_to_expire(Order.Status.PUB))
|
||||||
order.save()
|
order.save()
|
||||||
# send_message.delay(order.id,'order_published') # too spammy
|
# send_message.delay(order.id,'order_published') # too spammy
|
||||||
return
|
return
|
||||||
@ -708,7 +707,7 @@ class Logics:
|
|||||||
hold_payment = LNNode.gen_hold_invoice(
|
hold_payment = LNNode.gen_hold_invoice(
|
||||||
bond_satoshis,
|
bond_satoshis,
|
||||||
description,
|
description,
|
||||||
invoice_expiry=Order.t_to_expire[Order.Status.WFB],
|
invoice_expiry=order.t_to_expire(Order.Status.WFB),
|
||||||
cltv_expiry_secs=BOND_EXPIRY * 3600,
|
cltv_expiry_secs=BOND_EXPIRY * 3600,
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -759,7 +758,7 @@ class Logics:
|
|||||||
|
|
||||||
# With the bond confirmation the order is extended 'public_order_duration' hours
|
# With the bond confirmation the order is extended 'public_order_duration' hours
|
||||||
order.expires_at = timezone.now() + timedelta(
|
order.expires_at = timezone.now() + timedelta(
|
||||||
seconds=Order.t_to_expire[Order.Status.WF2])
|
seconds=order.t_to_expire(Order.Status.WF2))
|
||||||
order.status = Order.Status.WF2
|
order.status = Order.Status.WF2
|
||||||
order.save()
|
order.save()
|
||||||
|
|
||||||
@ -822,7 +821,7 @@ class Logics:
|
|||||||
hold_payment = LNNode.gen_hold_invoice(
|
hold_payment = LNNode.gen_hold_invoice(
|
||||||
bond_satoshis,
|
bond_satoshis,
|
||||||
description,
|
description,
|
||||||
invoice_expiry=Order.t_to_expire[Order.Status.TAK],
|
invoice_expiry=order.t_to_expire(Order.Status.TAK),
|
||||||
cltv_expiry_secs=BOND_EXPIRY * 3600,
|
cltv_expiry_secs=BOND_EXPIRY * 3600,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -850,7 +849,7 @@ class Logics:
|
|||||||
)
|
)
|
||||||
|
|
||||||
order.expires_at = timezone.now() + timedelta(
|
order.expires_at = timezone.now() + timedelta(
|
||||||
seconds=Order.t_to_expire[Order.Status.TAK])
|
seconds=order.t_to_expire(Order.Status.TAK))
|
||||||
order.save()
|
order.save()
|
||||||
return True, {
|
return True, {
|
||||||
"bond_invoice": hold_payment["invoice"],
|
"bond_invoice": hold_payment["invoice"],
|
||||||
@ -866,7 +865,7 @@ class Logics:
|
|||||||
elif order.status == Order.Status.WFE:
|
elif order.status == Order.Status.WFE:
|
||||||
order.status = Order.Status.CHA
|
order.status = Order.Status.CHA
|
||||||
order.expires_at = timezone.now() + timedelta(
|
order.expires_at = timezone.now() + timedelta(
|
||||||
seconds=Order.t_to_expire[Order.Status.CHA])
|
seconds=order.t_to_expire(Order.Status.CHA))
|
||||||
order.save()
|
order.save()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -909,7 +908,7 @@ class Logics:
|
|||||||
hold_payment = LNNode.gen_hold_invoice(
|
hold_payment = LNNode.gen_hold_invoice(
|
||||||
escrow_satoshis,
|
escrow_satoshis,
|
||||||
description,
|
description,
|
||||||
invoice_expiry=Order.t_to_expire[Order.Status.WF2],
|
invoice_expiry=order.t_to_expire(Order.Status.WF2),
|
||||||
cltv_expiry_secs=ESCROW_EXPIRY * 3600,
|
cltv_expiry_secs=ESCROW_EXPIRY * 3600,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -45,11 +45,17 @@ class Command(BaseCommand):
|
|||||||
except:
|
except:
|
||||||
print(f'No profile with token {token}')
|
print(f'No profile with token {token}')
|
||||||
continue
|
continue
|
||||||
profile.telegram_chat_id = result['message']['from']['id']
|
|
||||||
profile.telegram_lang_code = result['message']['from']['language_code']
|
attempts = 5
|
||||||
self.telegram.welcome(profile.user)
|
while attempts >= 0:
|
||||||
profile.telegram_enabled = True
|
try:
|
||||||
profile.save()
|
profile.telegram_chat_id = result['message']['from']['id']
|
||||||
|
profile.telegram_lang_code = result['message']['from']['language_code']
|
||||||
|
self.telegram.welcome(profile.user)
|
||||||
|
profile.telegram_enabled = True
|
||||||
|
profile.save()
|
||||||
|
except:
|
||||||
|
attempts = attempts - 1
|
||||||
|
|
||||||
offset = response['result'][-1]['update_id']
|
offset = response['result'][-1]['update_id']
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ from django.core.validators import (
|
|||||||
MinValueValidator,
|
MinValueValidator,
|
||||||
validate_comma_separated_integer_list,
|
validate_comma_separated_integer_list,
|
||||||
)
|
)
|
||||||
|
from django.utils import timezone
|
||||||
from django.db.models.signals import post_save, pre_delete
|
from django.db.models.signals import post_save, pre_delete
|
||||||
from django.template.defaultfilters import truncatechars
|
from django.template.defaultfilters import truncatechars
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
@ -38,7 +39,7 @@ class Currency(models.Model):
|
|||||||
null=True,
|
null=True,
|
||||||
validators=[MinValueValidator(0)],
|
validators=[MinValueValidator(0)],
|
||||||
)
|
)
|
||||||
timestamp = models.DateTimeField(auto_now_add=True)
|
timestamp = models.DateTimeField(default=timezone.now)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
# returns currency label ( 3 letters code)
|
# returns currency label ( 3 letters code)
|
||||||
@ -181,7 +182,7 @@ class Order(models.Model):
|
|||||||
status = models.PositiveSmallIntegerField(choices=Status.choices,
|
status = models.PositiveSmallIntegerField(choices=Status.choices,
|
||||||
null=False,
|
null=False,
|
||||||
default=Status.WFB)
|
default=Status.WFB)
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(default=timezone.now)
|
||||||
expires_at = models.DateTimeField()
|
expires_at = models.DateTimeField()
|
||||||
|
|
||||||
# order details
|
# order details
|
||||||
@ -218,6 +219,17 @@ class Order(models.Model):
|
|||||||
],
|
],
|
||||||
blank=True,
|
blank=True,
|
||||||
)
|
)
|
||||||
|
# optionally makers can choose the public order duration length (seconds)
|
||||||
|
public_duration = models.PositiveBigIntegerField(
|
||||||
|
default=60*60*int(config("DEFAULT_PUBLIC_ORDER_DURATION"))-1,
|
||||||
|
null=False,
|
||||||
|
validators=[
|
||||||
|
MinValueValidator(60*60*float(config("MIN_PUBLIC_ORDER_DURATION"))), # Min is 10 minutes
|
||||||
|
MaxValueValidator(60*60*float(config("MAX_PUBLIC_ORDER_DURATION"))), # Max is 24 Hours
|
||||||
|
],
|
||||||
|
blank=False,
|
||||||
|
)
|
||||||
|
|
||||||
# how many sats at creation and at last check (relevant for marked to market)
|
# how many sats at creation and at last check (relevant for marked to market)
|
||||||
t0_satoshis = models.PositiveBigIntegerField(
|
t0_satoshis = models.PositiveBigIntegerField(
|
||||||
null=True,
|
null=True,
|
||||||
@ -311,31 +323,35 @@ class Order(models.Model):
|
|||||||
maker_platform_rated = models.BooleanField(default=False, null=False)
|
maker_platform_rated = models.BooleanField(default=False, null=False)
|
||||||
taker_platform_rated = models.BooleanField(default=False, null=False)
|
taker_platform_rated = models.BooleanField(default=False, null=False)
|
||||||
|
|
||||||
t_to_expire = {
|
|
||||||
0: int(config("EXP_MAKER_BOND_INVOICE")), # 'Waiting for maker bond'
|
|
||||||
1: 60 * 60 * int(config("PUBLIC_ORDER_DURATION")), # 'Public'
|
|
||||||
2: 0, # 'Deleted'
|
|
||||||
3: int(config("EXP_TAKER_BOND_INVOICE")), # 'Waiting for taker bond'
|
|
||||||
4: 0, # 'Cancelled'
|
|
||||||
5: 0, # 'Expired'
|
|
||||||
6: 60 * int(config("INVOICE_AND_ESCROW_DURATION")), # 'Waiting for trade collateral and buyer invoice'
|
|
||||||
7: 60 * int(config("INVOICE_AND_ESCROW_DURATION")), # 'Waiting only for seller trade collateral'
|
|
||||||
8: 60 * int(config("INVOICE_AND_ESCROW_DURATION")), # 'Waiting only for buyer invoice'
|
|
||||||
9: 60 * 60 * int(config("FIAT_EXCHANGE_DURATION")), # 'Sending fiat - In chatroom'
|
|
||||||
10: 60 * 60 * int(config("FIAT_EXCHANGE_DURATION")),# 'Fiat sent - In chatroom'
|
|
||||||
11: 1 * 24 * 60 * 60, # 'In dispute'
|
|
||||||
12: 0, # 'Collaboratively cancelled'
|
|
||||||
13: 24 * 60 * 60, # 'Sending satoshis to buyer'
|
|
||||||
14: 24 * 60 * 60, # 'Sucessful trade'
|
|
||||||
15: 24 * 60 * 60, # 'Failed lightning network routing'
|
|
||||||
16: 10 * 24 * 60 * 60, # 'Wait for dispute resolution'
|
|
||||||
17: 24 * 60 * 60, # 'Maker lost dispute'
|
|
||||||
18: 24 * 60 * 60, # 'Taker lost dispute'
|
|
||||||
}
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"Order {self.id}: {self.Types(self.type).label} BTC for {float(self.amount)} {self.currency}"
|
return f"Order {self.id}: {self.Types(self.type).label} BTC for {float(self.amount)} {self.currency}"
|
||||||
|
|
||||||
|
def t_to_expire(self, status):
|
||||||
|
|
||||||
|
t_to_expire = {
|
||||||
|
0: int(config("EXP_MAKER_BOND_INVOICE")), # 'Waiting for maker bond'
|
||||||
|
1: self.public_duration, # 'Public'
|
||||||
|
2: 0, # 'Deleted'
|
||||||
|
3: int(config("EXP_TAKER_BOND_INVOICE")), # 'Waiting for taker bond'
|
||||||
|
4: 0, # 'Cancelled'
|
||||||
|
5: 0, # 'Expired'
|
||||||
|
6: 60 * int(config("INVOICE_AND_ESCROW_DURATION")), # 'Waiting for trade collateral and buyer invoice'
|
||||||
|
7: 60 * int(config("INVOICE_AND_ESCROW_DURATION")), # 'Waiting only for seller trade collateral'
|
||||||
|
8: 60 * int(config("INVOICE_AND_ESCROW_DURATION")), # 'Waiting only for buyer invoice'
|
||||||
|
9: 60 * 60 * int(config("FIAT_EXCHANGE_DURATION")), # 'Sending fiat - In chatroom'
|
||||||
|
10: 60 * 60 * int(config("FIAT_EXCHANGE_DURATION")),# 'Fiat sent - In chatroom'
|
||||||
|
11: 1 * 24 * 60 * 60, # 'In dispute'
|
||||||
|
12: 0, # 'Collaboratively cancelled'
|
||||||
|
13: 24 * 60 * 60, # 'Sending satoshis to buyer'
|
||||||
|
14: 24 * 60 * 60, # 'Sucessful trade'
|
||||||
|
15: 24 * 60 * 60, # 'Failed lightning network routing'
|
||||||
|
16: 10 * 24 * 60 * 60, # 'Wait for dispute resolution'
|
||||||
|
17: 24 * 60 * 60, # 'Maker lost dispute'
|
||||||
|
18: 24 * 60 * 60, # 'Taker lost dispute'
|
||||||
|
}
|
||||||
|
|
||||||
|
return t_to_expire[status]
|
||||||
|
|
||||||
|
|
||||||
@receiver(pre_delete, sender=Order)
|
@receiver(pre_delete, sender=Order)
|
||||||
def delete_lnpayment_at_order_deletion(sender, instance, **kwargs):
|
def delete_lnpayment_at_order_deletion(sender, instance, **kwargs):
|
||||||
@ -393,7 +409,7 @@ class Profile(models.Model):
|
|||||||
null=False
|
null=False
|
||||||
)
|
)
|
||||||
telegram_lang_code = models.CharField(
|
telegram_lang_code = models.CharField(
|
||||||
max_length=4,
|
max_length=10,
|
||||||
null=True,
|
null=True,
|
||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
@ -529,7 +545,7 @@ class MarketTick(models.Model):
|
|||||||
currency = models.ForeignKey(Currency,
|
currency = models.ForeignKey(Currency,
|
||||||
null=True,
|
null=True,
|
||||||
on_delete=models.SET_NULL)
|
on_delete=models.SET_NULL)
|
||||||
timestamp = models.DateTimeField(auto_now_add=True)
|
timestamp = models.DateTimeField(default=timezone.now)
|
||||||
|
|
||||||
# Relevant to keep record of the historical fee, so the insight on the premium can be better analyzed
|
# Relevant to keep record of the historical fee, so the insight on the premium can be better analyzed
|
||||||
fee = models.DecimalField(
|
fee = models.DecimalField(
|
||||||
|
@ -35,9 +35,9 @@ class MakeOrderSerializer(serializers.ModelSerializer):
|
|||||||
"is_explicit",
|
"is_explicit",
|
||||||
"premium",
|
"premium",
|
||||||
"satoshis",
|
"satoshis",
|
||||||
|
"public_duration",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class UpdateOrderSerializer(serializers.Serializer):
|
class UpdateOrderSerializer(serializers.Serializer):
|
||||||
invoice = serializers.CharField(max_length=2000,
|
invoice = serializers.CharField(max_length=2000,
|
||||||
allow_null=True,
|
allow_null=True,
|
||||||
|
@ -191,6 +191,9 @@ def send_message(order_id, message):
|
|||||||
from api.messages import Telegram
|
from api.messages import Telegram
|
||||||
telegram = Telegram()
|
telegram = Telegram()
|
||||||
|
|
||||||
|
if message == 'welcome':
|
||||||
|
telegram.welcome(order)
|
||||||
|
|
||||||
if message == 'order_taken':
|
if message == 'order_taken':
|
||||||
telegram.order_taken(order)
|
telegram.order_taken(order)
|
||||||
|
|
||||||
|
@ -72,6 +72,7 @@ class MakerView(CreateAPIView):
|
|||||||
premium = serializer.data.get("premium")
|
premium = serializer.data.get("premium")
|
||||||
satoshis = serializer.data.get("satoshis")
|
satoshis = serializer.data.get("satoshis")
|
||||||
is_explicit = serializer.data.get("is_explicit")
|
is_explicit = serializer.data.get("is_explicit")
|
||||||
|
public_duration = serializer.data.get("public_duration")
|
||||||
|
|
||||||
valid, context, _ = Logics.validate_already_maker_or_taker(
|
valid, context, _ = Logics.validate_already_maker_or_taker(
|
||||||
request.user)
|
request.user)
|
||||||
@ -90,6 +91,7 @@ class MakerView(CreateAPIView):
|
|||||||
expires_at=timezone.now() + timedelta(
|
expires_at=timezone.now() + timedelta(
|
||||||
seconds=EXP_MAKER_BOND_INVOICE), # TODO Move to class method
|
seconds=EXP_MAKER_BOND_INVOICE), # TODO Move to class method
|
||||||
maker=request.user,
|
maker=request.user,
|
||||||
|
public_duration=public_duration,
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO move to Order class method when new instance is created!
|
# TODO move to Order class method when new instance is created!
|
||||||
@ -155,7 +157,7 @@ class OrderView(viewsets.ViewSet):
|
|||||||
)
|
)
|
||||||
|
|
||||||
data = ListOrderSerializer(order).data
|
data = ListOrderSerializer(order).data
|
||||||
data["total_secs_exp"] = Order.t_to_expire[order.status]
|
data["total_secs_exp"] = order.t_to_expire(order.status)
|
||||||
|
|
||||||
# if user is under a limit (penalty), inform him.
|
# if user is under a limit (penalty), inform him.
|
||||||
is_penalized, time_out = Logics.is_penalized(request.user)
|
is_penalized, time_out = Logics.is_penalized(request.user)
|
||||||
|
@ -33,7 +33,7 @@ export default class App extends Component {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<ThemeProvider theme={this.state.dark ? this.darkTheme : this.lightTheme}>
|
<ThemeProvider theme={this.state.dark ? this.darkTheme : this.lightTheme}>
|
||||||
<UnsafeAlert/>
|
<UnsafeAlert className="unsafeAlert"/>
|
||||||
<HomePage setAppState={this.setAppState}/>
|
<HomePage setAppState={this.setAppState}/>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
|
@ -6,6 +6,7 @@ import { Link } from 'react-router-dom'
|
|||||||
import getFlags from './getFlags'
|
import getFlags from './getFlags'
|
||||||
|
|
||||||
import LockIcon from '@mui/icons-material/Lock';
|
import LockIcon from '@mui/icons-material/Lock';
|
||||||
|
import SelfImprovementIcon from '@mui/icons-material/SelfImprovement';
|
||||||
|
|
||||||
function getCookie(name) {
|
function getCookie(name) {
|
||||||
let cookieValue = null;
|
let cookieValue = null;
|
||||||
@ -57,7 +58,8 @@ export default class MakerPage extends Component {
|
|||||||
currencies_dict: {"1":"USD"},
|
currencies_dict: {"1":"USD"},
|
||||||
showAdvanced: false,
|
showAdvanced: false,
|
||||||
allowBondless: false,
|
allowBondless: false,
|
||||||
publicExpiryTime: Date.now() + 86400000,
|
publicExpiryTime: new Date(0, 0, 0, 23, 59),
|
||||||
|
publicDuration: 23*60*60 + 59*60,
|
||||||
enableAmountRange: false,
|
enableAmountRange: false,
|
||||||
minAmount: null,
|
minAmount: null,
|
||||||
bondSize: 1,
|
bondSize: 1,
|
||||||
@ -149,6 +151,7 @@ export default class MakerPage extends Component {
|
|||||||
is_explicit: this.state.is_explicit,
|
is_explicit: this.state.is_explicit,
|
||||||
premium: this.state.is_explicit ? null: this.state.premium,
|
premium: this.state.is_explicit ? null: this.state.premium,
|
||||||
satoshis: this.state.is_explicit ? this.state.satoshis: null,
|
satoshis: this.state.is_explicit ? this.state.satoshis: null,
|
||||||
|
public_duration: this.state.publicDuration,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
fetch("/api/make/",requestOptions)
|
fetch("/api/make/",requestOptions)
|
||||||
@ -335,31 +338,65 @@ export default class MakerPage extends Component {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleChangePublicDuration = (date) => {
|
||||||
|
console.log(date)
|
||||||
|
let d = new Date(date),
|
||||||
|
hours = d.getHours(),
|
||||||
|
minutes = d.getMinutes();
|
||||||
|
|
||||||
|
var total_secs = hours*60*60 + minutes * 60;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
changedPublicExpiryTime: true,
|
||||||
|
publicExpiryTime: date,
|
||||||
|
publicDuration: total_secs,
|
||||||
|
badDuration: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
AdvancedMakerOptions = () => {
|
AdvancedMakerOptions = () => {
|
||||||
return(
|
return(
|
||||||
<Paper elevation={12} style={{ padding: 8, width:280, align:'center'}}>
|
<Paper elevation={12} style={{ padding: 8, width:280, align:'center'}}>
|
||||||
<Grid container xs={12} spacing={1}>
|
<Grid container xs={12} spacing={1}>
|
||||||
|
<Grid item xs={12} align="center" spacing={1}>
|
||||||
|
<br/>
|
||||||
|
<LocalizationProvider dateAdapter={DateFnsUtils}>
|
||||||
|
<TimePicker
|
||||||
|
ampm={false}
|
||||||
|
openTo="hours"
|
||||||
|
views={['hours', 'minutes']}
|
||||||
|
inputFormat="HH:mm"
|
||||||
|
mask="__:__"
|
||||||
|
renderInput={(props) => <TextField {...props} />}
|
||||||
|
label="Public Duration (HH:mm)"
|
||||||
|
value={this.state.publicExpiryTime}
|
||||||
|
onChange={this.handleChangePublicDuration}
|
||||||
|
minTime={new Date(0, 0, 0, 0, 10)}
|
||||||
|
maxTime={new Date(0, 0, 0, 23, 59)}
|
||||||
|
/>
|
||||||
|
</LocalizationProvider>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<Grid item xs={12} align="center" spacing={1}>
|
<Grid item xs={12} align="center" spacing={1}>
|
||||||
<FormControl align="center">
|
<FormControl align="center">
|
||||||
<FormHelperText>
|
<FormHelperText>
|
||||||
<Tooltip enterTouchDelay="0" title={"Let the taker chose an amount within the range"}>
|
<Tooltip enterTouchDelay="0" title={"COMING SOON - Let the taker chose an amount within the range"}>
|
||||||
<div align="center">
|
<div align="center">
|
||||||
Amount Range
|
Amount Range
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</FormHelperText>
|
</FormHelperText>
|
||||||
<Grid container xs={12} align="center">
|
<Grid container xs={12} align="left">
|
||||||
<Grid item xs={2} align="center">
|
<Grid item xs={3} align="left">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
disabled
|
disabled
|
||||||
//disabled={this.state.amount == null}
|
//disabled={this.state.amount == null}
|
||||||
onChange={()=>this.setState({enableAmountRange:!this.state.enableAmountRange})}/>
|
onChange={()=>this.setState({enableAmountRange:!this.state.enableAmountRange})}/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid xs={1}/>
|
<Grid item xs={9} align="left">
|
||||||
<Grid item xs={9} align="center">
|
|
||||||
<Slider
|
<Slider
|
||||||
sx={{width:170, align:"center"}}
|
sx={{width:140, align:"center"}}
|
||||||
disabled={!this.state.enableAmountRange}
|
disabled={!this.state.enableAmountRange}
|
||||||
aria-label="Amount Range"
|
aria-label="Amount Range"
|
||||||
defaultValue={this.state.amount}
|
defaultValue={this.state.amount}
|
||||||
@ -372,7 +409,7 @@ export default class MakerPage extends Component {
|
|||||||
marks={this.state.amount == null ?
|
marks={this.state.amount == null ?
|
||||||
null
|
null
|
||||||
:
|
:
|
||||||
[{value: this.state.amount*this.minAmountFraction,label: pn(this.state.amount*this.minAmountFraction)+" "+ this.state.currencyCode},
|
[{value: this.state.amount*this.minAmountFraction,label: parseFloat(parseFloat(this.state.amount*this.minAmountFraction).toFixed(4))+" "+ this.state.currencyCode},
|
||||||
{value: this.state.amount,label: this.state.amount+" "+this.state.currencyCode}]}
|
{value: this.state.amount,label: this.state.amount+" "+this.state.currencyCode}]}
|
||||||
min={this.state.amount*this.minAmountFraction}
|
min={this.state.amount*this.minAmountFraction}
|
||||||
max={this.state.amount}
|
max={this.state.amount}
|
||||||
@ -383,22 +420,10 @@ export default class MakerPage extends Component {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item xs={12} align="center" spacing={1}>
|
|
||||||
<LocalizationProvider dateAdapter={DateFnsUtils}>
|
|
||||||
<TimePicker
|
|
||||||
disabled
|
|
||||||
renderInput={(props) => <TextField {...props} />}
|
|
||||||
label="Public Order Expiry Time"
|
|
||||||
value={this.state.publicExpiryTime}
|
|
||||||
onChange={(newValue) => {this.setState({publicExpiryTime: newValue})}}
|
|
||||||
/>
|
|
||||||
</LocalizationProvider>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Grid item xs={12} align="center" spacing={1}>
|
<Grid item xs={12} align="center" spacing={1}>
|
||||||
<FormControl align="center">
|
<FormControl align="center">
|
||||||
<FormHelperText>
|
<FormHelperText>
|
||||||
<Tooltip enterTouchDelay="0" title={"Increase for a higher safety assurance"}>
|
<Tooltip enterTouchDelay="0" title={"COMING SOON - Increase for a higher safety assurance"}>
|
||||||
<div align="center" style={{display:'flex',flexWrap:'wrap', transform: 'translate(20%, 0)'}}>
|
<div align="center" style={{display:'flex',flexWrap:'wrap', transform: 'translate(20%, 0)'}}>
|
||||||
<LockIcon sx={{height:20,width:20}}/> Fidelity Bond Size
|
<LockIcon sx={{height:20,width:20}}/> Fidelity Bond Size
|
||||||
</div>
|
</div>
|
||||||
@ -425,7 +450,7 @@ export default class MakerPage extends Component {
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item xs={12} align="center" spacing={1}>
|
<Grid item xs={12} align="center" spacing={1}>
|
||||||
<Tooltip enterTouchDelay="0" title={"High risk! Limited to "+ this.maxBondlessSats/1000 +"K Sats"}>
|
<Tooltip enterTouchDelay="0" title={"COMING SOON - High risk! Limited to "+ this.maxBondlessSats/1000 +"K Sats"}>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
label={<a>Allow bondless taker (<a href="https://git.robosats.com" target="_blank">info</a>)</a>}
|
label={<a>Allow bondless taker (<a href="https://git.robosats.com" target="_blank">info</a>)</a>}
|
||||||
control={
|
control={
|
||||||
@ -454,16 +479,18 @@ export default class MakerPage extends Component {
|
|||||||
</Grid> */}
|
</Grid> */}
|
||||||
<Grid item xs={12} align="center">
|
<Grid item xs={12} align="center">
|
||||||
<div className="advancedSwitch">
|
<div className="advancedSwitch">
|
||||||
<Tooltip enterTouchDelay="0" title="Coming soon">
|
{/* <Tooltip enterTouchDelay="0" title="Coming soon"> */}
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
|
size="small"
|
||||||
|
disableTypography={true}
|
||||||
|
label={<Typography variant="body2">Advanced</Typography>}
|
||||||
labelPlacement="start" control={
|
labelPlacement="start" control={
|
||||||
<Switch
|
<Switch
|
||||||
//disabled
|
size="small"
|
||||||
checked={this.state.showAdvanced}
|
checked={this.state.showAdvanced}
|
||||||
onChange={()=> this.setState({showAdvanced: !this.state.showAdvanced})}/>}
|
onChange={()=> this.setState({showAdvanced: !this.state.showAdvanced})}/>}
|
||||||
label="Advanced"
|
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
{/* </Tooltip> */}
|
||||||
</div>
|
</div>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
@ -25,9 +25,9 @@ export default class UnsafeAlert extends Component {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
(!this.safe_urls.includes(this.getHost()) & this.state.show) ?
|
(!this.safe_urls.includes(this.getHost()) & this.state.show) ?
|
||||||
<>
|
<div>
|
||||||
<MediaQuery minWidth={800}>
|
<MediaQuery minWidth={800}>
|
||||||
<Alert severity="warning"
|
<Alert severity="warning" sx={{maxHeight:100, zIndex:9999}} z-index={9999}
|
||||||
action={<Button onClick={() => this.setState({show:false})}>Hide</Button>}
|
action={<Button onClick={() => this.setState({show:false})}>Hide</Button>}
|
||||||
>
|
>
|
||||||
<AlertTitle>You are not using RoboSats privately</AlertTitle>
|
<AlertTitle>You are not using RoboSats privately</AlertTitle>
|
||||||
@ -37,7 +37,7 @@ export default class UnsafeAlert extends Component {
|
|||||||
</MediaQuery>
|
</MediaQuery>
|
||||||
|
|
||||||
<MediaQuery maxWidth={799}>
|
<MediaQuery maxWidth={799}>
|
||||||
<Alert severity="warning" >
|
<Alert severity="warning" sx={{maxHeight:100, zIndex:9999}} z-index={9999}>
|
||||||
<AlertTitle>You are not using RoboSats privately</AlertTitle>
|
<AlertTitle>You are not using RoboSats privately</AlertTitle>
|
||||||
You will not be able to complete a
|
You will not be able to complete a
|
||||||
trade. Use <a href='https://www.torproject.org/download/' target="_blank">Tor Browser</a> and visit the <a href='http://robosats6tkf3eva7x2voqso3a5wcorsnw34jveyxfqi2fu7oyheasid.onion' target="_blank">Onion</a> site.
|
trade. Use <a href='https://www.torproject.org/download/' target="_blank">Tor Browser</a> and visit the <a href='http://robosats6tkf3eva7x2voqso3a5wcorsnw34jveyxfqi2fu7oyheasid.onion' target="_blank">Onion</a> site.
|
||||||
@ -48,7 +48,7 @@ export default class UnsafeAlert extends Component {
|
|||||||
</div>
|
</div>
|
||||||
</Alert>
|
</Alert>
|
||||||
</MediaQuery>
|
</MediaQuery>
|
||||||
</>
|
</div>
|
||||||
:
|
:
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
|
@ -151,7 +151,7 @@ export default class UserGenPage extends Component {
|
|||||||
return (
|
return (
|
||||||
<Grid container spacing={1}>
|
<Grid container spacing={1}>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
<div style={{height:40}}/>
|
<div className='clickTrough'/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} align="center" sx={{width:370, height:260}}>
|
<Grid item xs={12} align="center" sx={{width:370, height:260}}>
|
||||||
{!this.state.loadingRobot ?
|
{!this.state.loadingRobot ?
|
||||||
@ -250,7 +250,7 @@ export default class UserGenPage extends Component {
|
|||||||
|
|
||||||
<Grid item xs={12} align="center" spacing={2} sx={{width:370}}>
|
<Grid item xs={12} align="center" spacing={2} sx={{width:370}}>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
<div style={{height:30}}/>
|
<div style={{height:40}}/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<div style={{width:370, left:30}}>
|
<div style={{width:370, left:30}}>
|
||||||
<Grid container xs={12} align="center">
|
<Grid container xs={12} align="center">
|
||||||
@ -266,7 +266,6 @@ export default class UserGenPage extends Component {
|
|||||||
</Grid>
|
</Grid>
|
||||||
</div>
|
</div>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -18,15 +18,18 @@ body {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.amboss{
|
|
||||||
fill:url(#SVGID_1_);
|
|
||||||
}
|
|
||||||
|
|
||||||
.appCenter {
|
.appCenter {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%,-50%) translate(0,-20px);
|
transform: translate(-50%,-50%) translate(0,-20px);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clickTrough{
|
||||||
|
height: 50px;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bottomBar {
|
.bottomBar {
|
||||||
@ -36,10 +39,14 @@ body {
|
|||||||
height: 40px;
|
height: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.amboss{
|
||||||
|
fill:url(#SVGID_1_);
|
||||||
|
}
|
||||||
|
|
||||||
.advancedSwitch{
|
.advancedSwitch{
|
||||||
width: 240;
|
width: 20;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(55px, 0px);
|
transform: translate(62px, 0px);
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
@ -91,6 +91,7 @@ TEMPLATES = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
WSGI_APPLICATION = "robosats.wsgi.application"
|
WSGI_APPLICATION = "robosats.wsgi.application"
|
||||||
|
USE_TZ = True
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases
|
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases
|
||||||
|
Loading…
Reference in New Issue
Block a user