Revamp UI

This commit is contained in:
Reckless_Satoshi 2022-01-09 17:12:58 -08:00
parent bb9cafadd8
commit 9bc6757ba3
No known key found for this signature in database
GPG Key ID: 9C4585B561315571
7 changed files with 137 additions and 71 deletions

View File

@ -36,9 +36,9 @@ class Logics():
def validate_order_size(order):
'''Checks if order is withing limits at t0'''
if order.t0_satoshis > MAX_TRADE:
return False, {'bad_request': f'Your order is too big. It is worth {order.t0_satoshis} now. But maximum is {MAX_TRADE}'}
return False, {'bad_request': 'Your order is too big. It is worth '+'{:,}'.format(order.t0_satoshis)+' Sats now. But limit is '+'{:,}'.format(MAX_TRADE)+ ' Sats'}
if order.t0_satoshis < MIN_TRADE:
return False, {'bad_request': f'Your order is too small. It is worth {order.t0_satoshis} now. But minimum is {MIN_TRADE}'}
return False, {'bad_request': 'Your order is too small. It is worth '+'{:,}'.format(order.t0_satoshis)+' Sats now. But limit is '+'{:,}'.format(MIN_TRADE)+ ' Sats'}
return True, None
def take(order, user):
@ -66,7 +66,22 @@ class Logics():
satoshis_now = (float(order.amount) / premium_rate) * 100*1000*1000
return int(satoshis_now)
def price_and_premium_now(order):
''' computes order premium live '''
exchange_rate = get_exchange_rate(Order.currency_dict[str(order.currency)])
if not order.is_explicit:
premium = order.premium
price = exchange_rate
else:
exchange_rate = get_exchange_rate(Order.currency_dict[str(order.currency)])
order_rate = float(order.amount) / (float(order.satoshis) / 100000000)
premium = order_rate / exchange_rate - 1
price = order_rate
premium = int(premium*100) # 2 decimals left
return price, premium
def order_expires(order):
order.status = Order.Status.EXP
order.maker = None

View File

@ -1,7 +1,11 @@
from decouple import config
import requests
import ring
storage = {}
@ring.dict(storage, expire=60) #keeps in cache for 60 secs
def get_exchange_rate(currency):
# TODO Add fallback Public APIs and error handling
# Think about polling price data in a different way (e.g. store locally every t seconds)

View File

@ -369,6 +369,9 @@ class BookView(ListAPIView):
data = ListOrderSerializer(order).data
data['maker_nick'] = str(order.maker)
# Compute current premium for those orders that are explicitly priced.
data['price'], data['premium'] = Logics.price_and_premium_now(order)
for key in ('status','taker'): # Non participants should not see the status or who is the taker
del data[key]

View File

@ -1,5 +1,5 @@
import React, { Component } from "react";
import { Button , Divider, ListItemButton, Typography, Grid, Select, MenuItem, FormControl, FormHelperText, List, ListItem, ListItemText, Avatar, RouterLink, ListItemAvatar} from "@mui/material";
import { Box, Button , Divider, ListItemButton, Typography, Grid, Select, MenuItem, FormControl, FormHelperText, List, ListItem, ListItemText, Avatar, RouterLink, ListItemAvatar} from "@mui/material";
import { Link } from 'react-router-dom'
export default class BookPage extends Component {
@ -65,14 +65,51 @@ export default class BookPage extends Component {
bookListItems=()=>{
return (this.state.orders.map((order) =>
<ListItemButton>
<ListItemAvatar >
<Avatar
alt={order.maker_nick}
src={window.location.origin +'/static/assets/avatars/' + order.maker_nick + '.png'}
/>
</ListItemAvatar>
</ListItemButton>
<>
<ListItemButton value={order.id} onClick={() => this.handleCardClick(order.id)}>
<ListItemAvatar >
<Avatar
alt={order.maker_nick}
src={window.location.origin +'/static/assets/avatars/' + order.maker_nick + '.png'}
/>
</ListItemAvatar>
<ListItemText>
<Typography variant="h6">
{order.maker_nick+" "}
</Typography>
</ListItemText>
<ListItemText align='left'>
<Typography variant="subtitle1">
<b>{order.type ? " Sells ": " Buys "} BTC </b> for {parseFloat(
parseFloat(order.amount).toFixed(4))+" "+ this.getCurrencyCode(order.currency)+" "}
</Typography>
</ListItemText>
<ListItemText align='left'>
<Typography variant="subtitle1">
via <b>{order.payment_method}</b>
</Typography>
</ListItemText>
<ListItemText align='right'>
<Typography variant="subtitle1">
at <b>{this.pn(order.price) + " " + this.getCurrencyCode(order.currency)}/BTC</b>
</Typography>
</ListItemText>
<ListItemText align='right'>
<Typography variant="subtitle1">
{order.premium > 1 ? "🔴" : "🔵" } <b>{parseFloat(parseFloat(order.premium).toFixed(4))}%</b>
</Typography>
</ListItemText>
</ListItemButton>
<Divider/>
</>
));
}
@ -133,7 +170,7 @@ export default class BookPage extends Component {
return (
<Grid className='orderBook' container spacing={1}>
<Grid item xs={12} align="center">
<Typography component="h4" variant="h4">
<Typography component="h2" variant="h2">
Order Book
</Typography>
</Grid>
@ -202,11 +239,9 @@ export default class BookPage extends Component {
</Typography>
</Grid>)
:
<Grid item>
<List>
{this.bookListItems()}
<List sx={{ width: '120%', overflow: 'auto',}} >
{this.bookListItems()}
</List>
</Grid>
}
<Grid item xs={12} align="center">
<Button color="secondary" variant="contained" to="/" component={Link}>

View File

@ -23,8 +23,10 @@ export default class MakerPage extends Component {
defaultCurrency = 1;
defaultCurrencyCode = 'USD';
defaultAmount = 0 ;
defaultPaymentMethod = "Not specified";
defaultPaymentMethod = "not specified";
defaultPremium = 0;
minTradeSats = 10000;
maxTradeSats = 500000;
constructor(props) {
super(props);
@ -69,9 +71,16 @@ export default class MakerPage extends Component {
});
}
handleSatoshisChange=(e)=>{
var bad_sats = e.target.value > this.maxTradeSats ?
("Must be less than "+this.maxTradeSats):
(e.target.value < this.minTradeSats ?
("Must be more than "+this.minTradeSats): null)
this.setState({
satoshis: e.target.value,
});
satoshis: e.target.value,
badSatoshis: bad_sats,
})
;
}
handleClickRelative=(e)=>{
this.setState({
@ -125,21 +134,19 @@ export default class MakerPage extends Component {
render() {
return (
<Grid container spacing={1}>
<Grid item xs={12} align="center">
<Grid container xs={12} align="center" spacing={1}>
<Grid item xs={12} align="center">
<Typography component="h4" variant="h4">
Make an Order
<Typography component="h2" variant="h2">
Order Maker
</Typography>
</Grid>
<Paper elevation={12} style={{ padding: 8,}}>
<Grid item xs={12} align="center">
<Grid item xs={12} align="center" spacing={1}>
<Paper elevation={12} style={{ padding: 8, width:350, align:'center'}}>
<Grid item xs={12} align="center" spacing={1}>
<FormControl component="fieldset">
<FormHelperText>
<div align='center'>
Choose Buy or Sell Bitcoin
</div>
</FormHelperText>
<FormHelperText>
Buy or Sell Bitcoin?
</FormHelperText>
<RadioGroup row defaultValue="0" onChange={this.handleTypeChange}>
<FormControlLabel
value="0"
@ -156,36 +163,35 @@ export default class MakerPage extends Component {
</RadioGroup>
</FormControl>
</Grid>
<Grid item xs={12} align="center">
<FormControl >
<TextField
label="Amount of Fiat to Trade"
type="number"
required="true"
defaultValue={this.defaultAmount}
inputProps={{
min:0 ,
style: {textAlign:"center"}
}}
onChange={this.handleAmountChange}
/>
<Select
label="Select Payment Currency"
required="true"
defaultValue={this.defaultCurrency}
inputProps={{
style: {textAlign:"center"}
}}
onChange={this.handleCurrencyChange}
>
{
Object.entries(this.state.currencies_dict)
.map( ([key, value]) => <MenuItem value={parseInt(key)}>{value}</MenuItem> )
}
</Select>
</FormControl>
</Grid>
<Grid container xs={11} align="center">
<TextField
label="Amount"
type="number"
required="true"
defaultValue={this.defaultAmount}
inputProps={{
min:0 ,
style: {textAlign:"center"}
}}
onChange={this.handleAmountChange}
/>
<Select
label="Select Payment Currency"
required="true"
defaultValue={this.defaultCurrency}
inputProps={{
style: {textAlign:"center"}
}}
onChange={this.handleCurrencyChange}
>
{
Object.entries(this.state.currencies_dict)
.map( ([key, value]) => <MenuItem value={parseInt(key)}>{value}</MenuItem> )
}
</Select>
</Grid>
<br/>
<Grid item xs={12} align="center">
<FormControl >
<TextField
@ -200,8 +206,14 @@ export default class MakerPage extends Component {
/>
</FormControl>
</Grid>
<Grid item xs={12} align="center">
<FormControl component="fieldset">
<FormHelperText >
<div align='center'>
Choose a Pricing Method
</div>
</FormHelperText>
<RadioGroup row defaultValue="relative">
<FormControlLabel
value="relative"
@ -218,24 +230,20 @@ export default class MakerPage extends Component {
onClick={this.handleClickExplicit}
/>
</RadioGroup>
<FormHelperText >
<div align='center'>
Choose a Pricing Method
</div>
</FormHelperText>
</FormControl>
</Grid>
{/* conditional shows either Premium % field or Satoshis field based on pricing method */}
{ this.state.isExplicit
? <Grid item xs={12} align="center">
<TextField
label="Explicit Amount in Satoshis"
label="Satoshis"
error={this.state.badSatoshis}
type="number"
required="true"
inputProps={{
// TODO read these from .env file
min:10000 ,
max:500000 ,
min:this.minTradeSats ,
max:this.maxTradeSats ,
style: {textAlign:"center"}
}}
onChange={this.handleSatoshisChange}
@ -255,7 +263,7 @@ export default class MakerPage extends Component {
</Grid>
}
</Paper>
</Grid>
</Grid>
<Grid item xs={12} align="center">
<Button color="primary" variant="contained" onClick={this.handleCreateOfferButtonPressed} >
Create Order

View File

@ -24,7 +24,7 @@ export default class UserGenPage extends Component {
constructor(props) {
super(props);
this.state = {
token: this.genBase62Token(32),
token: this.genBase62Token(34),
};
this.getGeneratedUser(this.state.token);
}
@ -72,7 +72,7 @@ export default class UserGenPage extends Component {
handleAnotherButtonPressed=(e)=>{
this.delGeneratedUser()
this.setState({
token: this.genBase62Token(32),
token: this.genBase62Token(34),
})
this.reload_for_csrf_to_work();
}

View File

@ -7,6 +7,7 @@
```
pip install virtualenvwrapper
pip install python-decouple
pip install ring
```
### Add to .bashrc