mirror of
https://github.com/RoboSats/robosats.git
synced 2024-09-11 22:07:08 +03:00
Improve chat status communication
This commit is contained in:
parent
3d130129f1
commit
69f6735f86
17
README.md
17
README.md
@ -10,17 +10,16 @@ RoboSats is a simple and private way to exchange bitcoin for national currencies
|
||||
<img width="75%" src="https://raw.githubusercontent.com/Reckless-Satoshi/robosats/main/frontend/static/assets/images/robosats_0.1.0_banner.png">
|
||||
</div>
|
||||
|
||||
**Bitcoin mainnet:**
|
||||
- Tor: robosats6tkf3eva7x2voqso3a5wcorsnw34jveyxfqi2fu7oyheasid.onion
|
||||
- Url: robosats.com (Coming soon)
|
||||
- Version: v0.1.0-mvp
|
||||
### 🔗 **Bitcoin Mainnet**
|
||||
- 🧅 **TOR URL:** [**RoboSats**6tkf3eva7x2voqso3a5wcorsnw34jveyxfqi2fu7oyheasid.onion](http://robosats6tkf3eva7x2voqso3a5wcorsnw34jveyxfqi2fu7oyheasid.onion) ( Open with [Tor Browser](https://www.torproject.org/download/))
|
||||
- Clearnet URL: [unsafe.robosats.com](https://unsafe.robosats.com) (not recommended!)
|
||||
- Version: v0.1.0 MVP (+++)
|
||||
|
||||
**Bitcoin testnet:**
|
||||
- Tor: robotestagw3dcxmd66r4rgksb4nmmr43fh77bzn2ia2eucduyeafnyd.onion
|
||||
- Url: testnet.robosats.com (Coming soon)
|
||||
- Latest commit.
|
||||
*⚠️ Always use [Tor Browser](https://www.torproject.org/download/) and .onion for best anonymity. The Clearnet URL redirects to a third party Tor2web service. Your privacy cannot be guaranteed to be respected. Use only to check around the app, never use for trading!⚠️*
|
||||
|
||||
*Always use [Tor Browser](https://www.torproject.org/download/) and .onion for best anonymity.*
|
||||
You can also use RoboSats in Testnet:
|
||||
- TOR URL: [RoboTestagw3dcxmd66r4rgksb4nmmr43fh77bzn2ia2eucduyeafnyd.onion](http://robotestagw3dcxmd66r4rgksb4nmmr43fh77bzn2ia2eucduyeafnyd.onion)
|
||||
- Clearnet URL: [unsafe.testnet.robosats.com](https://unsafe.testnet.robosats.com)
|
||||
|
||||
## How to use it
|
||||
|
||||
|
@ -2,7 +2,7 @@ from django.contrib import admin
|
||||
from django_admin_relation_links import AdminChangeLinksMixin
|
||||
from django.contrib.auth.models import Group, User
|
||||
from django.contrib.auth.admin import UserAdmin
|
||||
from .models import Order, LNPayment, Profile, MarketTick, Currency
|
||||
from api.models import Order, LNPayment, Profile, MarketTick, Currency
|
||||
|
||||
admin.site.unregister(Group)
|
||||
admin.site.unregister(User)
|
||||
|
@ -1,3 +1,20 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from django_admin_relation_links import AdminChangeLinksMixin
|
||||
from chat.models import ChatRoom
|
||||
# Register your models here.
|
||||
|
||||
|
||||
@admin.register(ChatRoom)
|
||||
class UserProfileAdmin(AdminChangeLinksMixin, admin.ModelAdmin):
|
||||
list_display = (
|
||||
"id",
|
||||
"order_link",
|
||||
"maker_link",
|
||||
"taker_link",
|
||||
"maker_connected",
|
||||
"taker_connected",
|
||||
"maker_connect_date",
|
||||
"taker_connect_date",
|
||||
"room_group_name",
|
||||
)
|
||||
change_links = ["order","maker","taker"]
|
@ -1,57 +1,136 @@
|
||||
from channels.generic.websocket import AsyncWebsocketConsumer
|
||||
from api.logics import Logics
|
||||
from channels.db import database_sync_to_async
|
||||
from api.models import Order
|
||||
from chat.models import ChatRoom
|
||||
|
||||
import json
|
||||
|
||||
|
||||
class ChatRoomConsumer(AsyncWebsocketConsumer):
|
||||
|
||||
@database_sync_to_async
|
||||
def allow_in_chatroom(self):
|
||||
order = Order.objects.get(id=self.order_id)
|
||||
if not (order.maker == self.user or order.taker == self.user):
|
||||
print("Not allowed in this chat")
|
||||
return False
|
||||
return True
|
||||
|
||||
@database_sync_to_async
|
||||
def save_connect_user(self):
|
||||
'''Creates or updates the ChatRoom object'''
|
||||
|
||||
order = Order.objects.get(id=self.order_id)
|
||||
|
||||
if order.maker == self.user:
|
||||
ChatRoom.objects.update_or_create(
|
||||
id=self.order_id,
|
||||
order=order,
|
||||
room_group_name=self.room_group_name,
|
||||
defaults={
|
||||
"maker": self.user,
|
||||
"maker_connected": True,
|
||||
}
|
||||
)
|
||||
|
||||
elif order.taker == self.user:
|
||||
ChatRoom.objects.update_or_create(
|
||||
id=self.order_id,
|
||||
order=order,
|
||||
room_group_name=self.room_group_name,
|
||||
defaults={
|
||||
"taker": self.user,
|
||||
"taker_connected": True,
|
||||
}
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
@database_sync_to_async
|
||||
def save_disconnect_user(self):
|
||||
'''Creates or updates the ChatRoom object'''
|
||||
|
||||
order = Order.objects.get(id=self.order_id)
|
||||
if order.maker == self.user:
|
||||
ChatRoom.objects.update_or_create(
|
||||
id=self.order_id,
|
||||
defaults={
|
||||
"maker_connected": False
|
||||
}
|
||||
)
|
||||
elif order.taker == self.user:
|
||||
ChatRoom.objects.update_or_create(
|
||||
id=self.order_id,
|
||||
defaults={
|
||||
"taker_connected": False
|
||||
}
|
||||
)
|
||||
return None
|
||||
|
||||
@database_sync_to_async
|
||||
def is_peer_connected(self):
|
||||
'''Creates or updates the ChatRoom object'''
|
||||
|
||||
chatroom = ChatRoom.objects.get(id=self.order_id)
|
||||
|
||||
if chatroom.maker == self.user:
|
||||
return chatroom.taker_connected
|
||||
|
||||
if chatroom.taker == self.user:
|
||||
return chatroom.maker_connected
|
||||
|
||||
async def connect(self):
|
||||
self.order_id = self.scope["url_route"]["kwargs"]["order_id"]
|
||||
self.room_group_name = f"chat_order_{self.order_id}"
|
||||
self.user = self.scope["user"]
|
||||
self.user_nick = str(self.user)
|
||||
|
||||
# Forbit if user is not part of the order
|
||||
# Does not work Async
|
||||
# order = Order.objects.get(id=self.order_id)
|
||||
allowed = await self.allow_in_chatroom()
|
||||
|
||||
# # Check if user is participant on the order.
|
||||
# if not (Logics.is_buyer(order[0], self.user) or Logics.is_seller(order[0], self.user)):
|
||||
# print ("Outta this chat")
|
||||
# return False
|
||||
if allowed:
|
||||
await self.save_connect_user()
|
||||
await self.channel_layer.group_add(self.room_group_name,
|
||||
self.channel_name)
|
||||
|
||||
await self.channel_layer.group_add(self.room_group_name,
|
||||
self.channel_name)
|
||||
|
||||
await self.accept()
|
||||
await self.accept()
|
||||
|
||||
async def disconnect(self, close_code):
|
||||
await self.save_disconnect_user()
|
||||
await self.channel_layer.group_discard(self.room_group_name,
|
||||
self.channel_name)
|
||||
await self.channel_layer.group_send(
|
||||
self.room_group_name,
|
||||
{
|
||||
"type": "chatroom_message",
|
||||
"message": 'peer-disconnected',
|
||||
"nick": self.scope["user"].username,
|
||||
"peer_connected": False,
|
||||
},
|
||||
)
|
||||
|
||||
async def receive(self, text_data):
|
||||
text_data_json = json.loads(text_data)
|
||||
message = text_data_json["message"]
|
||||
nick = text_data_json["nick"]
|
||||
|
||||
|
||||
peer_connected = await self.is_peer_connected()
|
||||
await self.channel_layer.group_send(
|
||||
self.room_group_name,
|
||||
{
|
||||
"type": "chatroom_message",
|
||||
"message": message,
|
||||
"nick": nick,
|
||||
"nick": self.scope["user"].username,
|
||||
"peer_connected": peer_connected,
|
||||
},
|
||||
)
|
||||
|
||||
async def chatroom_message(self, event):
|
||||
message = event["message"]
|
||||
nick = event["nick"]
|
||||
peer_connected = event["peer_connected"]
|
||||
|
||||
await self.send(text_data=json.dumps({
|
||||
"message": message,
|
||||
"user_nick": nick,
|
||||
"peer_connected": peer_connected,
|
||||
}))
|
||||
|
||||
pass
|
||||
|
@ -1,3 +1,43 @@
|
||||
from django.db import models
|
||||
from api.models import User, Order
|
||||
|
||||
# Create your models here.
|
||||
|
||||
class ChatRoom(models.Model):
|
||||
'''
|
||||
Simple ChatRoom model. Needed to facilitate communication: Is my counterpart in the room?
|
||||
'''
|
||||
|
||||
id = models.PositiveBigIntegerField(primary_key=True, null=False,default=None, blank=True)
|
||||
order = models.ForeignKey(
|
||||
Order,
|
||||
related_name="order",
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
default=None)
|
||||
maker = models.ForeignKey(
|
||||
User,
|
||||
related_name="chat_maker",
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
default=None)
|
||||
taker = models.ForeignKey(
|
||||
User,
|
||||
related_name="chat_taker",
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
default=None,
|
||||
blank=True,
|
||||
)
|
||||
|
||||
maker_connected = models.BooleanField(default=False, null=False)
|
||||
taker_connected = models.BooleanField(default=False, null=False)
|
||||
|
||||
maker_connect_date = models.DateTimeField(auto_now_add=True)
|
||||
taker_connect_date = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
room_group_name = models.CharField(
|
||||
max_length=50,
|
||||
null=True,
|
||||
default=None,
|
||||
blank=True,
|
||||
)
|
5
frontend/package-lock.json
generated
5
frontend/package-lock.json
generated
@ -6583,6 +6583,11 @@
|
||||
"resolve": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"reconnecting-websocket": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz",
|
||||
"integrity": "sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng=="
|
||||
},
|
||||
"regenerate": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
||||
|
@ -38,6 +38,7 @@
|
||||
"react-qr-reader": "^2.2.1",
|
||||
"react-responsive": "^9.0.0-beta.6",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"reconnecting-websocket": "^4.4.0",
|
||||
"websocket": "^1.0.34"
|
||||
}
|
||||
}
|
||||
|
@ -315,11 +315,11 @@ export default class BottomBar extends Component {
|
||||
<ListItemIcon>
|
||||
<PasswordIcon/>
|
||||
</ListItemIcon>
|
||||
<ListItemText secondary="Your token">
|
||||
<ListItemText secondary="Back it up now. It will not remain here.">
|
||||
{this.props.token ?
|
||||
<TextField
|
||||
disabled
|
||||
label='Store Safely'
|
||||
label='Your Token'
|
||||
value={this.props.token }
|
||||
variant='filled'
|
||||
size='small'
|
||||
@ -337,7 +337,7 @@ export default class BottomBar extends Component {
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
|
||||
<Divider><Chip label='Earn Sats'/></Divider>
|
||||
<Divider><Chip label='Rewards & Compensations'/></Divider>
|
||||
<ListItem>
|
||||
<ListItemIcon>
|
||||
<PersonAddAltIcon/>
|
||||
@ -430,6 +430,7 @@ bottomBarDesktop =()=>{
|
||||
<Grid item xs={1.9}>
|
||||
<div style={{display: this.props.avatarLoaded ? '':'none'}}>
|
||||
<ListItemButton onClick={this.handleClickOpenProfile} >
|
||||
<Tooltip open={this.state.earned_rewards > 0 ? true: false} title="You can claim satoshis!">
|
||||
<Tooltip open={(this.state.active_order_id > 0 & !this.state.profileShown & this.props.avatarLoaded) ? true: false}
|
||||
title="You have an active order">
|
||||
<ListItemAvatar sx={{ width: 30, height: 30 }} >
|
||||
@ -444,6 +445,7 @@ bottomBarDesktop =()=>{
|
||||
</Badge>
|
||||
</ListItemAvatar>
|
||||
</Tooltip>
|
||||
</Tooltip>
|
||||
<ListItemText primary={this.props.nickname}/>
|
||||
</ListItemButton>
|
||||
</div>
|
||||
@ -671,8 +673,9 @@ bottomBarPhone =()=>{
|
||||
|
||||
<Grid item xs={1.6}>
|
||||
<div style={{display: this.props.avatarLoaded ? '':'none'}}>
|
||||
<Tooltip open={(this.state.active_order_id > 0 & !this.state.profileShown & this.props.avatarLoaded) ? true: false}
|
||||
title="You have an active order">
|
||||
<Tooltip open={this.state.earned_rewards > 0 ? true: false} title="You can claim satoshis!">
|
||||
<Tooltip open={(this.state.active_order_id > 0 & !this.state.profileShown & this.props.avatarLoaded) ? true: false}
|
||||
title="You have an active order">
|
||||
<IconButton onClick={this.handleClickOpenProfile} sx={{margin: 0, bottom: 17, right: 8}} >
|
||||
<Badge badgeContent={(this.state.active_order_id >0 & !this.state.profileShown) ? "": null} color="primary">
|
||||
<Avatar className='phoneFlippedSmallAvatar'
|
||||
@ -686,6 +689,7 @@ bottomBarPhone =()=>{
|
||||
</Badge>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Grid>
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React, { Component } from 'react';
|
||||
import { w3cwebsocket as W3CWebSocket } from "websocket";
|
||||
import {Button, TextField, Grid, Container, Card, CardHeader, Paper, Avatar, FormHelperText} from "@mui/material";
|
||||
|
||||
import {Button, Badge, TextField, Grid, Container, Card, CardHeader, Paper, Avatar, FormHelperText, Typography} from "@mui/material";
|
||||
import ReconnectingWebSocket from 'reconnecting-websocket';
|
||||
|
||||
export default class Chat extends Component {
|
||||
constructor(props) {
|
||||
@ -11,32 +10,52 @@ export default class Chat extends Component {
|
||||
state = {
|
||||
messages: [],
|
||||
value:'',
|
||||
connected: false,
|
||||
peer_connected: false,
|
||||
};
|
||||
|
||||
client = new W3CWebSocket('ws://' + window.location.host + '/ws/chat/' + this.props.orderId + '/');
|
||||
rws = new ReconnectingWebSocket('ws://' + window.location.host + '/ws/chat/' + this.props.orderId + '/');
|
||||
|
||||
componentDidMount() {
|
||||
this.client.onopen = () => {
|
||||
console.log('WebSocket Client Connected')
|
||||
}
|
||||
this.client.onmessage = (message) => {
|
||||
this.rws.addEventListener('open', () => {
|
||||
console.log('Connected!');
|
||||
this.setState({connected: true});
|
||||
this.rws.send(JSON.stringify({
|
||||
type: "message",
|
||||
message: 'just-connected',
|
||||
nick: this.props.ur_nick,
|
||||
}));
|
||||
});
|
||||
|
||||
this.rws.addEventListener('message', (message) => {
|
||||
|
||||
const dataFromServer = JSON.parse(message.data);
|
||||
console.log('Got reply!', dataFromServer.type);
|
||||
|
||||
if (dataFromServer){
|
||||
this.setState((state) =>
|
||||
({
|
||||
messages: [...state.messages,
|
||||
{
|
||||
msg: dataFromServer.message,
|
||||
userNick: dataFromServer.user_nick,
|
||||
}],
|
||||
|
||||
})
|
||||
|
||||
)
|
||||
if (dataFromServer.message != 'just-connected' & dataFromServer.message != 'peer-disconnected'){
|
||||
this.setState((state) =>
|
||||
({
|
||||
messages: [...state.messages,
|
||||
{
|
||||
msg: dataFromServer.message,
|
||||
userNick: dataFromServer.user_nick,
|
||||
}],
|
||||
})
|
||||
)
|
||||
}
|
||||
this.setState({peer_connected: dataFromServer.peer_connected})
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
this.rws.addEventListener('close', () => {
|
||||
console.log('Socket is closed. Reconnect will be attempted');
|
||||
this.setState({connected: false});
|
||||
});
|
||||
|
||||
this.rws.addEventListener('error', () => {
|
||||
console.error('Socket encountered error: Closing socket');
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
@ -49,7 +68,7 @@ export default class Chat extends Component {
|
||||
|
||||
onButtonClicked = (e) => {
|
||||
if(this.state.value!=''){
|
||||
this.client.send(JSON.stringify({
|
||||
this.rws.send(JSON.stringify({
|
||||
type: "message",
|
||||
message: this.state.value,
|
||||
nick: this.props.ur_nick,
|
||||
@ -62,17 +81,38 @@ export default class Chat extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Container component="main" maxWidth="xs">
|
||||
<Paper style={{ height: 300, maxHeight: 300, overflow: 'auto', boxShadow: 'none', }}>
|
||||
<Paper elevation={1} style={{ height: 300, maxHeight: 300, overflow: 'auto', backgroundColor: '#F7F7F7' }}>
|
||||
<Grid container xs={12} spacing={0.5}>
|
||||
<Grid item xs={0.5}/>
|
||||
<Grid item xs={5}>
|
||||
<Paper elevation={1} style={this.state.connected ? {backgroundColor: '#e8ffe6'}: {backgroundColor: '#FFF1C5'}}>
|
||||
<Typography variant='caption' >
|
||||
You: {this.state.connected ? 'connected': 'disconnected'}
|
||||
</Typography>
|
||||
</Paper>
|
||||
</Grid>
|
||||
<Grid item xs={1}/>
|
||||
<Grid item xs={5}>
|
||||
<Paper elevation={1} style={this.state.peer_connected ? {backgroundColor: '#e8ffe6'}: {backgroundColor: '#FFF1C5'}}>
|
||||
<Typography variant='caption'>
|
||||
Peer: {this.state.peer_connected ? 'connected': 'disconnected'}
|
||||
</Typography>
|
||||
</Paper>
|
||||
</Grid>
|
||||
<Grid item xs={0.5}/>
|
||||
</Grid>
|
||||
{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.ur_nick ?
|
||||
<CardHeader
|
||||
avatar={
|
||||
<Avatar
|
||||
alt={message.userNick}
|
||||
src={window.location.origin +'/static/assets/avatars/' + message.userNick + '.png'}
|
||||
/>
|
||||
<Badge variant="dot" overlap="circular" badgeContent="" color={this.state.connected ? "success" : "error"}>
|
||||
<Avatar className="flippedSmallAvatar"
|
||||
alt={message.userNick}
|
||||
src={window.location.origin +'/static/assets/avatars/' + message.userNick + '.png'}
|
||||
/>
|
||||
</Badge>
|
||||
}
|
||||
style={{backgroundColor: '#e8ffe6'}}
|
||||
title={message.userNick}
|
||||
@ -82,10 +122,12 @@ export default class Chat extends Component {
|
||||
:
|
||||
<CardHeader
|
||||
avatar={
|
||||
<Avatar
|
||||
alt={message.userNick}
|
||||
src={window.location.origin +'/static/assets/avatars/' + message.userNick + '.png'}
|
||||
/>
|
||||
<Badge variant="dot" overlap="circular" badgeContent="" color={this.state.peer_connected ? "success" : "error"}>
|
||||
<Avatar className="flippedSmallAvatar"
|
||||
alt={message.userNick}
|
||||
src={window.location.origin +'/static/assets/avatars/' + message.userNick + '.png'}
|
||||
/>
|
||||
</Badge>
|
||||
}
|
||||
style={{backgroundColor: '#fcfcfc'}}
|
||||
title={message.userNick}
|
||||
@ -98,20 +140,22 @@ export default class Chat extends Component {
|
||||
</Paper>
|
||||
<form noValidate onSubmit={this.onButtonClicked}>
|
||||
<Grid containter alignItems="stretch" style={{ display: "flex" }}>
|
||||
<Grid item alignItems="stretch" style={{ display: "flex" }}>
|
||||
<Grid item alignItems="stretch" style={{ display: "flex"}}>
|
||||
<TextField
|
||||
label="Type a message"
|
||||
variant="outlined"
|
||||
variant="standard"
|
||||
size="small"
|
||||
helperText={this.state.connected ? null : "Connecting..."}
|
||||
value={this.state.value}
|
||||
onChange={e => {
|
||||
this.setState({ value: e.target.value });
|
||||
this.value = this.state.value;
|
||||
}}
|
||||
sx={{width: 214}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item alignItems="stretch" style={{ display: "flex" }}>
|
||||
<Button type="submit" variant="contained" color="primary" > Send </Button>
|
||||
<Button disabled={!this.state.connected} type="submit" variant="contained" color="primary" > Send </Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
|
@ -790,7 +790,6 @@ handleRatingRobosatsChange=(e)=>{
|
||||
Say hi! Ask for payment details and click "Confirm Sent" as soon as the payment is sent.
|
||||
</Typography>
|
||||
}
|
||||
<Divider/>
|
||||
</Grid>
|
||||
|
||||
<Chat orderId={this.props.data.id} ur_nick={this.props.data.ur_nick}/>
|
||||
|
File diff suppressed because one or more lines are too long
@ -58,10 +58,6 @@
|
||||
!*** ./src/components/UserGenPage.js ***!
|
||||
\***************************************/
|
||||
|
||||
/*!****************************************!*\
|
||||
!*** ./node_modules/es5-ext/global.js ***!
|
||||
\****************************************/
|
||||
|
||||
/*!****************************************!*\
|
||||
!*** ./node_modules/qr.js/lib/math.js ***!
|
||||
\****************************************/
|
||||
@ -146,10 +142,6 @@
|
||||
!*** ./node_modules/stylis/src/Prefixer.js ***!
|
||||
\*********************************************/
|
||||
|
||||
/*!*********************************************!*\
|
||||
!*** ./node_modules/websocket/package.json ***!
|
||||
\*********************************************/
|
||||
|
||||
/*!**********************************************!*\
|
||||
!*** ./node_modules/@mui/system/esm/grid.js ***!
|
||||
\**********************************************/
|
||||
@ -202,14 +194,6 @@
|
||||
!*** ./node_modules/stylis/src/Serializer.js ***!
|
||||
\***********************************************/
|
||||
|
||||
/*!***********************************************!*\
|
||||
!*** ./node_modules/websocket/lib/browser.js ***!
|
||||
\***********************************************/
|
||||
|
||||
/*!***********************************************!*\
|
||||
!*** ./node_modules/websocket/lib/version.js ***!
|
||||
\***********************************************/
|
||||
|
||||
/*!************************************************!*\
|
||||
!*** ./node_modules/@mui/system/esm/sizing.js ***!
|
||||
\************************************************/
|
||||
@ -2226,6 +2210,10 @@
|
||||
!*** ./node_modules/@popperjs/core/lib/utils/getOppositeVariationPlacement.js ***!
|
||||
\********************************************************************************/
|
||||
|
||||
/*!********************************************************************************!*\
|
||||
!*** ./node_modules/reconnecting-websocket/dist/reconnecting-websocket-mjs.js ***!
|
||||
\********************************************************************************/
|
||||
|
||||
/*!*********************************************************************************!*\
|
||||
!*** ./node_modules/@babel/runtime/helpers/esm/objectWithoutPropertiesLoose.js ***!
|
||||
\*********************************************************************************/
|
||||
|
Loading…
Reference in New Issue
Block a user