Add dynamic countdown. Attach countdown to expiry progress bar.

This commit is contained in:
Reckless_Satoshi 2022-01-13 16:43:26 -08:00
parent 18a8038466
commit abb1bdd0be
No known key found for this signature in database
GPG Key ID: 9C4585B561315571
10 changed files with 93 additions and 60 deletions

View File

@ -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,
}))

View File

@ -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'),
# ]

View File

@ -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
# })

View File

@ -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",

View File

@ -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",

View File

@ -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" }}>

View File

@ -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 */}

View File

@ -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() : ""}

View File

@ -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')),
]

View File

@ -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)