mirror of
https://github.com/RoboSats/robosats.git
synced 2024-12-13 10:56:24 +00:00
Add user profile bottom left icon with active order reminder
This commit is contained in:
parent
292addc081
commit
5ab97453f0
@ -40,12 +40,12 @@ class Logics():
|
||||
'''Checks if the user is already partipant of an active order'''
|
||||
queryset = Order.objects.filter(maker=user, status__in=active_order_status)
|
||||
if queryset.exists():
|
||||
return False, {'bad_request':'You are already maker of an active order'}
|
||||
return False, {'bad_request':'You are already maker of an active order'}, queryset[0]
|
||||
|
||||
queryset = Order.objects.filter(taker=user, status__in=active_order_status)
|
||||
if queryset.exists():
|
||||
return False, {'bad_request':'You are already taker of an active order'}
|
||||
return True, None
|
||||
return False, {'bad_request':'You are already taker of an active order'}, queryset[0]
|
||||
return True, None, None
|
||||
|
||||
def validate_order_size(order):
|
||||
'''Validates if order is withing limits in satoshis at t0'''
|
||||
@ -769,13 +769,12 @@ class Logics():
|
||||
|
||||
# Double check the escrow is settled.
|
||||
if LNNode.double_check_htlc_is_settled(order.trade_escrow.payment_hash):
|
||||
# RETURN THE BONDS // Probably best also do it even if payment failed
|
||||
cls.return_bond(order.taker_bond)
|
||||
cls.return_bond(order.maker_bond)
|
||||
is_payed, context = follow_send_payment(order.payout) ##### !!! KEY LINE - PAYS THE BUYER INVOICE !!!
|
||||
if is_payed:
|
||||
# RETURN THE BONDS // Probably best also do it even if payment failed
|
||||
cls.return_bond(order.taker_bond)
|
||||
cls.return_bond(order.maker_bond)
|
||||
order.save()
|
||||
|
||||
return True, context
|
||||
else:
|
||||
# error handling here
|
||||
|
12
api/views.py
12
api/views.py
@ -49,7 +49,7 @@ class MakerView(CreateAPIView):
|
||||
satoshis = serializer.data.get('satoshis')
|
||||
is_explicit = serializer.data.get('is_explicit')
|
||||
|
||||
valid, context = Logics.validate_already_maker_or_taker(request.user)
|
||||
valid, context, _ = Logics.validate_already_maker_or_taker(request.user)
|
||||
if not valid: return Response(context, status.HTTP_409_CONFLICT)
|
||||
|
||||
# Creates a new order
|
||||
@ -270,7 +270,7 @@ class OrderView(viewsets.ViewSet):
|
||||
# 1) If action is take, it is a taker request!
|
||||
if action == 'take':
|
||||
if order.status == Order.Status.PUB:
|
||||
valid, context = Logics.validate_already_maker_or_taker(request.user)
|
||||
valid, context, _ = Logics.validate_already_maker_or_taker(request.user)
|
||||
if not valid: return Response(context, status=status.HTTP_409_CONFLICT)
|
||||
valid, context = Logics.take(order, request.user)
|
||||
if not valid: return Response(context, status=status.HTTP_403_FORBIDDEN)
|
||||
@ -345,7 +345,7 @@ 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 request.user.is_authenticated:
|
||||
context = {'nickname': request.user.username}
|
||||
not_participant, _ = Logics.validate_already_maker_or_taker(request.user)
|
||||
not_participant, _, _ = Logics.validate_already_maker_or_taker(request.user)
|
||||
|
||||
# Does not allow this 'mistake' if an active order
|
||||
if not not_participant:
|
||||
@ -449,7 +449,6 @@ class BookView(ListAPIView):
|
||||
if len(queryset)== 0:
|
||||
return Response({'not_found':'No orders found, be the first to make one'}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
# queryset = queryset.order_by('created_at')
|
||||
book_data = []
|
||||
for order in queryset:
|
||||
data = ListOrderSerializer(order).data
|
||||
@ -509,6 +508,11 @@ class InfoView(ListAPIView):
|
||||
context['robosats_running_commit_hash'] = get_commit_robosats()
|
||||
context['fee'] = FEE
|
||||
context['bond_size'] = float(config('BOND_SIZE'))
|
||||
if request.user.is_authenticated:
|
||||
context['nickname'] = request.user.username
|
||||
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
|
||||
|
||||
return Response(context, status.HTTP_200_OK)
|
||||
|
||||
|
@ -7,16 +7,23 @@ import BottomBar from "./BottomBar";
|
||||
export default class App extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
nickname: null,
|
||||
}
|
||||
}
|
||||
|
||||
setAppState=(newState)=>{
|
||||
this.setState(newState)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<div className='appCenter'>
|
||||
<HomePage />
|
||||
<HomePage setAppState={this.setAppState}/>
|
||||
</div>
|
||||
<div className='bottomBar'>
|
||||
<BottomBar />
|
||||
<BottomBar nickname={this.state.nickname} setAppState={this.setAppState} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { Component } from 'react'
|
||||
import {Badge, Paper, Grid, IconButton, Typography, Select, MenuItem, List, ListItemText, ListItem, ListItemIcon, ListItemButton, Divider, Dialog, DialogContent} from "@mui/material";
|
||||
import {Badge, ListItemAvatar, Avatar,Paper, Grid, IconButton, Typography, Select, MenuItem, List, ListItemText, ListItem, ListItemIcon, ListItemButton, Divider, Dialog, DialogContent} from "@mui/material";
|
||||
import MediaQuery from 'react-responsive'
|
||||
|
||||
// Icons
|
||||
@ -15,6 +15,7 @@ import GitHubIcon from '@mui/icons-material/GitHub';
|
||||
import EqualizerIcon from '@mui/icons-material/Equalizer';
|
||||
import SendIcon from '@mui/icons-material/Send';
|
||||
import PublicIcon from '@mui/icons-material/Public';
|
||||
import NumbersIcon from '@mui/icons-material/Numbers';
|
||||
|
||||
// pretty numbers
|
||||
function pn(x) {
|
||||
@ -36,10 +37,12 @@ export default class BottomBar extends Component {
|
||||
today_total_volume: 0,
|
||||
lifetime_satoshis_settled: 0,
|
||||
robosats_running_commit_hash: '000000000000000',
|
||||
openProfile: false,
|
||||
profileShown: false,
|
||||
};
|
||||
this.getInfo();
|
||||
}
|
||||
|
||||
|
||||
handleClickSuppport = () => {
|
||||
window.open("https://t.me/robosats");
|
||||
};
|
||||
@ -48,7 +51,8 @@ export default class BottomBar extends Component {
|
||||
this.setState(null)
|
||||
fetch('/api/info/')
|
||||
.then((response) => response.json())
|
||||
.then((data) => this.setState(data));
|
||||
.then((data) => this.setState(data) &
|
||||
this.props.setAppState({nickname:data.nickname}));
|
||||
}
|
||||
|
||||
handleClickOpenStatsForNerds = () => {
|
||||
@ -166,20 +170,82 @@ export default class BottomBar extends Component {
|
||||
)
|
||||
}
|
||||
|
||||
handleClickOpenProfile = () => {
|
||||
this.getInfo();
|
||||
this.setState({openProfile: true, profileShown: true});
|
||||
};
|
||||
handleClickCloseProfile = () => {
|
||||
this.setState({openProfile: false});
|
||||
};
|
||||
|
||||
dialogProfile =() =>{
|
||||
return(
|
||||
<Dialog
|
||||
open={this.state.openProfile}
|
||||
onClose={this.handleClickCloseProfile}
|
||||
aria-labelledby="profile-title"
|
||||
aria-describedby="profile-description"
|
||||
>
|
||||
<DialogContent>
|
||||
<Typography component="h5" variant="h5">Your Profile</Typography>
|
||||
<List>
|
||||
<Divider/>
|
||||
<ListItem className="profileNickname">
|
||||
<ListItemText secondary="Your robot pseudonymous">
|
||||
<Typography component="h6" variant="h6">
|
||||
{this.props.nickname ? "⚡"+this.props.nickname+"⚡" : ""}
|
||||
</Typography>
|
||||
</ListItemText>
|
||||
<ListItemAvatar>
|
||||
<Avatar className='avatar'
|
||||
sx={{ width: 65, height:65 }}
|
||||
alt={this.props.nickname}
|
||||
src={this.props.nickname ? window.location.origin +'/static/assets/avatars/' + this.props.nickname + '.png' : null}
|
||||
/>
|
||||
</ListItemAvatar>
|
||||
</ListItem>
|
||||
<Divider/>
|
||||
{this.state.active_order_id ?
|
||||
// TODO Link to router and do this.props.history.push
|
||||
<ListItemButton component="a" href={window.location.origin +'/order/'+this.state.active_order_id}>
|
||||
<ListItemIcon>
|
||||
<NumbersIcon color="primary"/>
|
||||
</ListItemIcon>
|
||||
<ListItemText color="primary" primary={'One active order #'+this.state.active_order_id} secondary="Your current order"/>
|
||||
</ListItemButton>
|
||||
:
|
||||
<ListItem>
|
||||
<ListItemIcon><NumbersIcon/></ListItemIcon>
|
||||
<ListItemText primary="No active orders" secondary="Your current order"/>
|
||||
</ListItem>
|
||||
}
|
||||
</List>
|
||||
</DialogContent>
|
||||
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
bottomBarDesktop =()=>{
|
||||
return(
|
||||
<Paper elevation={6} style={{height:40}}>
|
||||
<this.StatsDialog/>
|
||||
<this.CommunityDialog/>
|
||||
<this.dialogProfile/>
|
||||
<Grid container xs={12}>
|
||||
|
||||
<Grid item xs={1}>
|
||||
<IconButton color="primary"
|
||||
aria-label="Stats for Nerds"
|
||||
onClick={this.handleClickOpenStatsForNerds} >
|
||||
<SettingsIcon />
|
||||
</IconButton>
|
||||
<Grid item xs={2}>
|
||||
<ListItemButton onClick={this.handleClickOpenProfile} >
|
||||
<ListItemAvatar sx={{ width: 30, height: 30 }} >
|
||||
<Badge badgeContent={(this.state.active_order_id > 0 & !this.state.profileShown) ? "1": null} color="primary">
|
||||
<Avatar className='rotatedAvatar' sx={{margin: 0, top: -13}}
|
||||
alt={this.props.nickname}
|
||||
src={this.props.nickname ? window.location.origin +'/static/assets/avatars/' + this.props.nickname + '.png' : null}
|
||||
/>
|
||||
</Badge>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={this.props.nickname}/>
|
||||
</ListItemButton>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={2}>
|
||||
@ -234,7 +300,7 @@ bottomBarDesktop =()=>{
|
||||
</ListItem>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={2}>
|
||||
<Grid item xs={1}>
|
||||
<ListItem className="bottomItem">
|
||||
<ListItemIcon size="small">
|
||||
<PercentIcon/>
|
||||
@ -243,12 +309,12 @@ bottomBarDesktop =()=>{
|
||||
primaryTypographyProps={{fontSize: '14px'}}
|
||||
secondaryTypographyProps={{fontSize: '12px'}}
|
||||
primary={this.state.fee*100}
|
||||
secondary="Trading Fee" />
|
||||
secondary="Trade Fee" />
|
||||
</ListItem>
|
||||
</Grid>
|
||||
|
||||
<Grid container item xs={1}>
|
||||
<Grid item xs={4}>
|
||||
<Grid item xs={6}>
|
||||
<Select
|
||||
size = 'small'
|
||||
defaultValue={1}
|
||||
@ -258,15 +324,21 @@ bottomBarDesktop =()=>{
|
||||
<MenuItem value={1}>EN</MenuItem>
|
||||
</Select>
|
||||
</Grid>
|
||||
<Grid item xs={4}/>
|
||||
<Grid item xs={4}>
|
||||
<Grid item xs={3}>
|
||||
<IconButton
|
||||
color="primary"
|
||||
aria-label="Telegram"
|
||||
aria-label="Community"
|
||||
onClick={this.handleClickOpenCommunity} >
|
||||
<PeopleIcon />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<IconButton color="primary"
|
||||
aria-label="Stats for Nerds"
|
||||
onClick={this.handleClickOpenStatsForNerds} >
|
||||
<SettingsIcon />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
@ -275,13 +347,14 @@ bottomBarDesktop =()=>{
|
||||
}
|
||||
|
||||
handleClickOpenExchangeSummary = () => {
|
||||
this.getInfo();
|
||||
this.setState({openExchangeSummary: true});
|
||||
};
|
||||
handleClickCloseExchangeSummary = () => {
|
||||
this.setState({openExchangeSummary: false});
|
||||
};
|
||||
|
||||
phoneExchangeSummaryDialog =() =>{
|
||||
exchangeSummaryDialogPhone =() =>{
|
||||
return(
|
||||
<Dialog
|
||||
open={this.state.openExchangeSummary}
|
||||
@ -357,24 +430,27 @@ bottomBarDesktop =()=>{
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
bottomBarPhone =()=>{
|
||||
return(
|
||||
<Paper elevation={6} style={{height:40}}>
|
||||
<this.StatsDialog/>
|
||||
<this.CommunityDialog/>
|
||||
<this.phoneExchangeSummaryDialog/>
|
||||
<this.exchangeSummaryDialogPhone/>
|
||||
<this.dialogProfile/>
|
||||
<Grid container xs={12}>
|
||||
|
||||
<Grid item xs={1}>
|
||||
<IconButton color="primary"
|
||||
aria-label="Stats for Nerds"
|
||||
onClick={this.handleClickOpenStatsForNerds} >
|
||||
<SettingsIcon />
|
||||
</IconButton>
|
||||
<Grid item xs={1.6}>
|
||||
<IconButton onClick={this.handleClickOpenProfile} sx={{margin: 0, top: -13, }} >
|
||||
<Badge badgeContent={(this.state.active_order_id >0 & !this.state.profileShown) ? "1": null} color="primary">
|
||||
<Avatar className='rotatedAvatar'
|
||||
alt={this.props.nickname}
|
||||
src={this.props.nickname ? window.location.origin +'/static/assets/avatars/' + this.props.nickname + '.png' : null}
|
||||
/>
|
||||
</Badge>
|
||||
</IconButton>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={2} align="center">
|
||||
<Grid item xs={1.6} align="center">
|
||||
<IconButton onClick={this.handleClickOpenExchangeSummary} >
|
||||
<Badge badgeContent={this.state.num_public_buy_orders} color="action">
|
||||
<InventoryIcon />
|
||||
@ -382,7 +458,7 @@ bottomBarPhone =()=>{
|
||||
</IconButton>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={2} align="center">
|
||||
<Grid item xs={1.6} align="center">
|
||||
<IconButton onClick={this.handleClickOpenExchangeSummary} >
|
||||
<Badge badgeContent={this.state.num_public_sell_orders} color="action">
|
||||
<SellIcon />
|
||||
@ -390,7 +466,7 @@ bottomBarPhone =()=>{
|
||||
</IconButton>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={2} align="center">
|
||||
<Grid item xs={1.6} align="center">
|
||||
<IconButton onClick={this.handleClickOpenExchangeSummary} >
|
||||
<Badge badgeContent={this.state.active_robots_today} color="action">
|
||||
<SmartToyIcon />
|
||||
@ -398,7 +474,7 @@ bottomBarPhone =()=>{
|
||||
</IconButton>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={2} align="center">
|
||||
<Grid item xs={1.8} align="center">
|
||||
<IconButton onClick={this.handleClickOpenExchangeSummary} >
|
||||
<Badge badgeContent={this.state.today_avg_nonkyc_btc_premium+"%"} color="action">
|
||||
<PriceChangeIcon />
|
||||
@ -406,8 +482,8 @@ bottomBarPhone =()=>{
|
||||
</IconButton>
|
||||
</Grid>
|
||||
|
||||
<Grid container item xs={3}>
|
||||
<Grid item xs={4}>
|
||||
<Grid container item xs={3.8}>
|
||||
<Grid item xs={6}>
|
||||
<Select
|
||||
size = 'small'
|
||||
defaultValue={1}
|
||||
@ -417,11 +493,17 @@ bottomBarPhone =()=>{
|
||||
<MenuItem value={1}>EN</MenuItem>
|
||||
</Select>
|
||||
</Grid>
|
||||
<Grid item xs={4}/>
|
||||
<Grid item xs={4}>
|
||||
<Grid item xs={3}>
|
||||
<IconButton color="primary"
|
||||
aria-label="Stats for Nerds"
|
||||
onClick={this.handleClickOpenStatsForNerds} >
|
||||
<SettingsIcon />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<IconButton
|
||||
color="primary"
|
||||
aria-label="Telegram"
|
||||
aria-label="Community"
|
||||
onClick={this.handleClickOpenCommunity} >
|
||||
<PeopleIcon />
|
||||
</IconButton>
|
||||
|
@ -14,8 +14,7 @@ export default class HomePage extends Component {
|
||||
return (
|
||||
<Router >
|
||||
<Switch>
|
||||
<Route exact path='/' component={UserGenPage}/>
|
||||
<Route path='/home'><p>You are at the start page</p></Route>
|
||||
<Route exact path='/' render={(props) => <UserGenPage setAppState={this.props.setAppState}/>}/>
|
||||
<Route path='/make' component={MakerPage}/>
|
||||
<Route path='/book' component={BookPage}/>
|
||||
<Route path="/order/:orderId" component={OrderPage}/>
|
||||
|
@ -27,10 +27,10 @@ export default class InfoDialog extends Component {
|
||||
received the fiat, then the satoshis are released to Bob. Enjoy your satoshis,
|
||||
Bob!</p>
|
||||
|
||||
<p>At no point, AdequateAlice01 and BafflingBob02 have to trust the
|
||||
bitcoin to each other. In case they have a conflict, <i>RoboSats</i> staff
|
||||
<p>At no point, AnonymousAlice01 and BafflingBob02 have to trust the
|
||||
bitcoin funds to each other. In case they have a conflict, <i>RoboSats</i> staff
|
||||
will help resolving the dispute. You can find a step-by-step
|
||||
description of the trade pipeline in <a href='https://github.com/Reckless-Satoshi/robosats/blob/main/README.md#how-it-works'>'How it works'</a></p>
|
||||
description of the trade pipeline in <a href='https://github.com/Reckless-Satoshi/robosats/blob/main/README.md#how-it-works'>How it works</a></p>
|
||||
</Typography>
|
||||
|
||||
<Typography component="h5" variant="h5">What payment methods are accepted?</Typography>
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Paper, Button , Grid, Typography, TextField, Select, FormHelperText, MenuItem, FormControl, Radio, FormControlLabel, RadioGroup} from "@mui/material"
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
import getFlags from './getFlags'
|
||||
|
||||
function getCookie(name) {
|
||||
|
@ -45,7 +45,7 @@ export default class UserGenPage extends Component {
|
||||
.substring(0, length);
|
||||
}
|
||||
|
||||
getGeneratedUser(token) {
|
||||
getGeneratedUser=(token)=>{
|
||||
fetch('/api/user' + '?token=' + token)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
@ -57,7 +57,11 @@ export default class UserGenPage extends Component {
|
||||
bad_request: data.bad_request,
|
||||
found: data.found,
|
||||
showRobosat:true,
|
||||
});
|
||||
})
|
||||
&
|
||||
this.props.setAppState({
|
||||
nickname: data.nickname,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -112,11 +116,10 @@ export default class UserGenPage extends Component {
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12} align="center" sx={{width:380}}>
|
||||
<Grid item xs={12} align="center" sx={{width:370}}>
|
||||
{this.state.showRobosat ?
|
||||
<div>
|
||||
<Grid item xs={12} align="center">
|
||||
|
@ -37,6 +37,11 @@ body {
|
||||
top: -14px;
|
||||
}
|
||||
|
||||
.profileNickname {
|
||||
margin: 0;
|
||||
left: -22px;
|
||||
}
|
||||
|
||||
.newAvatar {
|
||||
background-color:white;
|
||||
border-radius: 50%;
|
||||
@ -44,4 +49,16 @@ body {
|
||||
filter: drop-shadow(1px 1px 1px #000000);
|
||||
height: 200px;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
border: 0.5px solid #555;
|
||||
filter: drop-shadow(0.5px 0.5px 0.5px #000000);
|
||||
left: 35px;
|
||||
}
|
||||
|
||||
.rotatedAvatar {
|
||||
transform: scaleX(-1);
|
||||
border: 0.5px solid #555;
|
||||
filter: drop-shadow(0.5px 0.5px 0.5px #000000);
|
||||
}
|
Loading…
Reference in New Issue
Block a user