mirror of
https://github.com/RoboSats/robosats.git
synced 2025-01-18 12:11:35 +00:00
Add dynamic countdown. Attach countdown to expiry progress bar.
This commit is contained in:
parent
18a8038466
commit
abb1bdd0be
@ -56,8 +56,16 @@ class ChatRoomConsumer(AsyncWebsocketConsumer):
|
||||
message = event['message']
|
||||
nick = event['nick']
|
||||
|
||||
# Insert a white space in words longer than 22 characters.
|
||||
# Helps when messages overflow in a single line.
|
||||
words = message.split(' ')
|
||||
fix_message = ''
|
||||
for word in words:
|
||||
word = ' '.join(word[i:i+22] for i in range(0, len(word), 22))
|
||||
fix_message = fix_message +' '+ word
|
||||
|
||||
await self.send(text_data=json.dumps({
|
||||
'message': message,
|
||||
'message': fix_message,
|
||||
'user_nick': nick,
|
||||
}))
|
||||
|
||||
|
@ -2,7 +2,7 @@ from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.index, name='index'),
|
||||
path('<str:order_id>/', views.room, name='order_chat'),
|
||||
]
|
||||
# urlpatterns = [
|
||||
# path('', views.index, name='index'),
|
||||
# path('<str:order_id>/', views.room, name='order_chat'),
|
||||
# ]
|
@ -1,10 +1,7 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
|
||||
def index(request):
|
||||
return render(request, 'index.html', {})
|
||||
|
||||
def room(request, order_id):
|
||||
return render(request, 'chatroom.html', {
|
||||
'order_id': order_id
|
||||
})
|
||||
# def room(request, order_id):
|
||||
# return render(request, 'chatroom.html', {
|
||||
# 'order_id': order_id
|
||||
# })
|
8
frontend/package-lock.json
generated
8
frontend/package-lock.json
generated
@ -6503,6 +6503,14 @@
|
||||
"object-assign": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"react-countdown": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/react-countdown/-/react-countdown-2.3.2.tgz",
|
||||
"integrity": "sha512-Q4SADotHtgOxNWhDdvgupmKVL0pMB9DvoFcxv5AzjsxVhzOVxnttMbAywgqeOdruwEAmnPhOhNv/awAgkwru2w==",
|
||||
"requires": {
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"react-devtools-core": {
|
||||
"version": "4.22.1",
|
||||
"resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-4.22.1.tgz",
|
||||
|
@ -29,6 +29,7 @@
|
||||
"@mui/material": "^5.2.7",
|
||||
"@mui/system": "^5.2.6",
|
||||
"material-ui-image": "^3.3.2",
|
||||
"react-countdown": "^2.3.2",
|
||||
"react-markdown": "^7.1.2",
|
||||
"react-native": "^0.66.4",
|
||||
"react-native-svg": "^12.1.1",
|
||||
|
@ -13,15 +13,13 @@ export default class Chat extends Component {
|
||||
state = {
|
||||
messages: [],
|
||||
value:'',
|
||||
orderId: 2,
|
||||
};
|
||||
|
||||
client = new W3CWebSocket('ws://' + window.location.host + '/ws/chat/' + this.props.data.orderId + '/');
|
||||
client = new W3CWebSocket('ws://' + window.location.host + '/ws/chat/' + this.props.orderId + '/');
|
||||
|
||||
componentDidMount() {
|
||||
this.client.onopen = () => {
|
||||
console.log('WebSocket Client Connected')
|
||||
console.log(this.props.data)
|
||||
}
|
||||
this.client.onmessage = (message) => {
|
||||
const dataFromServer = JSON.parse(message.data);
|
||||
@ -43,11 +41,19 @@ export default class Chat extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.scrollToBottom();
|
||||
}
|
||||
|
||||
scrollToBottom = () => {
|
||||
this.messagesEnd.scrollIntoView({ behavior: "smooth" });
|
||||
}
|
||||
|
||||
onButtonClicked = (e) => {
|
||||
this.client.send(JSON.stringify({
|
||||
type: "message",
|
||||
message: this.state.value,
|
||||
nick: this.props.data.urNick,
|
||||
nick: this.props.urNick,
|
||||
}));
|
||||
this.state.value = ''
|
||||
e.preventDefault();
|
||||
@ -60,7 +66,7 @@ export default class Chat extends Component {
|
||||
{this.state.messages.map(message => <>
|
||||
<Card elevation={5} align="left" >
|
||||
{/* If message sender is not our nick, gray color, if it is our nick, green color */}
|
||||
{message.userNick == this.props.data.urNick ?
|
||||
{message.userNick == this.props.urNick ?
|
||||
<CardHeader
|
||||
avatar={
|
||||
<Avatar
|
||||
@ -86,6 +92,7 @@ export default class Chat extends Component {
|
||||
/>}
|
||||
</Card>
|
||||
</>)}
|
||||
<div style={{ float:"left", clear: "both" }} ref={(el) => { this.messagesEnd = el; }}></div>
|
||||
</Paper>
|
||||
<form noValidate onSubmit={this.onButtonClicked}>
|
||||
<Grid containter alignItems="stretch" style={{ display: "flex" }}>
|
||||
|
@ -1,44 +1,8 @@
|
||||
import React, { Component } from "react";
|
||||
import { Alert, Paper, CircularProgress, Button , Grid, Typography, List, ListItem, ListItemText, ListItemAvatar, Avatar, Divider, Box, LinearProgress} from "@mui/material"
|
||||
import Countdown, { zeroPad, calcTimeDelta } from 'react-countdown';
|
||||
import TradeBox from "./TradeBox";
|
||||
|
||||
function msToTime(duration) {
|
||||
var seconds = Math.floor((duration / 1000) % 60),
|
||||
minutes = Math.floor((duration / (1000 * 60)) % 60),
|
||||
hours = Math.floor((duration / (1000 * 60 * 60)) % 24);
|
||||
|
||||
minutes = (minutes < 10) ? "0" + minutes : minutes;
|
||||
seconds = (seconds < 10) ? "0" + seconds : seconds;
|
||||
|
||||
return hours + "h " + minutes + "m " + seconds + "s";
|
||||
}
|
||||
|
||||
// TO DO fix Progress bar to go from 100 to 0, from total_expiration time, showing time_left
|
||||
function LinearDeterminate() {
|
||||
const [progress, setProgress] = React.useState(0);
|
||||
|
||||
React.useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
setProgress((oldProgress) => {
|
||||
if (oldProgress === 0) {
|
||||
return 100;
|
||||
}
|
||||
const diff = 1;
|
||||
return Math.max(oldProgress - diff, 0);
|
||||
});
|
||||
}, 500);
|
||||
|
||||
return () => {
|
||||
clearInterval(timer);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box sx={{ width: '100%' }}>
|
||||
<LinearProgress variant="determinate" value={progress} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function getCookie(name) {
|
||||
let cookieValue = null;
|
||||
@ -67,8 +31,9 @@ export default class OrderPage extends Component {
|
||||
super(props);
|
||||
this.state = {
|
||||
isExplicit: false,
|
||||
delay: 2000, // Refresh every 2 seconds by default
|
||||
currencies_dict: {"1":"USD"}
|
||||
delay: 60000, // Refresh every 60 seconds by default
|
||||
currencies_dict: {"1":"USD"},
|
||||
total_secs_expiry: 300,
|
||||
};
|
||||
this.orderId = this.props.match.params.orderId;
|
||||
this.getCurrencyDict();
|
||||
@ -136,6 +101,50 @@ export default class OrderPage extends Component {
|
||||
window.history.back();
|
||||
}
|
||||
|
||||
// Countdown Renderer callback with condition
|
||||
countdownRenderer = ({ total, hours, minutes, seconds, completed }) => {
|
||||
if (completed) {
|
||||
// Render a completed state
|
||||
this.getOrderDetails();
|
||||
} else {
|
||||
var col = 'black'
|
||||
var fraction_left = (total/1000) / this.state.total_secs_expiry
|
||||
console.log(fraction_left)
|
||||
// Make orange at -25% of time left
|
||||
if (fraction_left < 0.25){col = 'orange'}
|
||||
// Make red at 10% of time left
|
||||
if (fraction_left < 0.1){col = 'red'}
|
||||
// Render a countdown
|
||||
return (
|
||||
fraction_left < 0.25 ? <b><span style={{color:col}}>{hours}h {zeroPad(minutes)}m {zeroPad(seconds)}s </span></b>
|
||||
:<span style={{color:col}}>{hours}h {zeroPad(minutes)}m {zeroPad(seconds)}s </span>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
LinearDeterminate =()=> {
|
||||
const [progress, setProgress] = React.useState(0);
|
||||
|
||||
React.useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
setProgress((oldProgress) => {
|
||||
var left = calcTimeDelta( new Date(this.state.expiresAt)).total /1000;
|
||||
return (left / this.state.total_secs_expiry) * 100;
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
return () => {
|
||||
clearInterval(timer);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box sx={{ width: '100%' }}>
|
||||
<LinearProgress variant="determinate" value={progress} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
handleClickTakeOrderButton=()=>{
|
||||
console.log(this.state)
|
||||
const requestOptions = {
|
||||
@ -246,9 +255,11 @@ export default class OrderPage extends Component {
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem>
|
||||
<ListItemText primary={msToTime( new Date(this.state.expiresAt) - Date.now())} secondary="Expires"/>
|
||||
<ListItemText secondary="Expires">
|
||||
<Countdown onTick={console.log(this.seconds)} date={new Date(this.state.expiresAt)} renderer={this.countdownRenderer} />
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<LinearDeterminate />
|
||||
<this.LinearDeterminate />
|
||||
</List>
|
||||
|
||||
{/* If the user has a penalty/limit */}
|
||||
|
@ -377,7 +377,7 @@ handleRatingChange=(e)=>{
|
||||
<Divider/>
|
||||
</Grid>
|
||||
|
||||
<Chat data={this.props.data}/>
|
||||
<Chat orderId={this.props.data.id} urNick={this.props.data.urNick}/>
|
||||
|
||||
<Grid item xs={12} align="center">
|
||||
{openDisputeButton ? this.showOpenDisputeButton() : ""}
|
||||
|
@ -19,6 +19,6 @@ from django.urls import path, include
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('api/', include('api.urls')),
|
||||
path('chat/', include('chat.urls')),
|
||||
# path('chat/', include('chat.urls')),
|
||||
path('', include('frontend.urls')),
|
||||
]
|
||||
|
1
setup.md
1
setup.md
@ -114,6 +114,7 @@ npm install react-qr-code
|
||||
npm install @mui/material
|
||||
npm install react-markdown
|
||||
npm install websocket
|
||||
npm install react-countdown
|
||||
```
|
||||
Note we are using mostly MaterialUI V5 (@mui/material) but Image loading from V4 (@material-ui/core) extentions (so both V4 and V5 are needed)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user