From 64df2c7ce6c1f272881604730e318dd9652ecaa7 Mon Sep 17 00:00:00 2001 From: Reckless_Satoshi Date: Thu, 13 Jan 2022 11:22:54 -0800 Subject: [PATCH] Add provisional frontend chat. The chat is totally unsafe as of now. Does not store messages either. --- api/logics.py | 3 +- api/models.py | 8 +- api/views.py | 1 + chat/consumers.py | 30 +++++-- frontend/package-lock.json | 130 +++++++++++++++++++++++++++ frontend/package.json | 3 +- frontend/src/components/Chat.js | 98 ++++++++++++++++++++ frontend/src/components/MakerPage.js | 2 +- frontend/src/components/OrderPage.js | 1 + frontend/src/components/TradeBox.js | 18 ++-- setup.md | 1 + 11 files changed, 273 insertions(+), 22 deletions(-) create mode 100644 frontend/src/components/Chat.js diff --git a/api/logics.py b/api/logics.py index ab4063c3..c8da042a 100644 --- a/api/logics.py +++ b/api/logics.py @@ -375,7 +375,8 @@ class Logics(): # If there was no taker_bond object yet, generates one order.last_satoshis = cls.satoshis_now(order) bond_satoshis = int(order.last_satoshis * BOND_SIZE) - description = f"RoboSats - Taking '{str(order)}' - This is a taker bond, it will freeze in your wallet temporarily and automatically return. It will be charged if you cheat or cancel." + pos_text = 'Buying' if cls.is_buyer(order, user) else 'Selling' + description = f"RoboSats - Taking 'Order {order.id}' {pos_text} BTC for {order.amount} - This is a taker bond, it will freeze in your wallet temporarily and automatically return. It will be charged if you cheat or cancel." # Gen hold Invoice hold_payment = LNNode.gen_hold_invoice(bond_satoshis, description, BOND_EXPIRY*3600) diff --git a/api/models.py b/api/models.py index ace4cdf4..bc7edd9f 100644 --- a/api/models.py +++ b/api/models.py @@ -4,6 +4,7 @@ from django.core.validators import MaxValueValidator, MinValueValidator, validat from django.db.models.signals import post_save, pre_delete from django.dispatch import receiver from django.utils.html import mark_safe +import uuid from decouple import config from pathlib import Path @@ -40,6 +41,7 @@ class LNPayment(models.Model): # payment use details + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) type = models.PositiveSmallIntegerField(choices=Types.choices, null=False, default=Types.HOLD) concept = models.PositiveSmallIntegerField(choices=Concepts.choices, null=False, default=Concepts.MAKEBOND) status = models.PositiveSmallIntegerField(choices=Status.choices, null=False, default=Status.INVGEN) @@ -59,7 +61,7 @@ class LNPayment(models.Model): receiver = models.ForeignKey(User, related_name='receiver', on_delete=models.CASCADE, null=True, default=None) def __str__(self): - return (f'HTLC {self.id}: {self.Concepts(self.concept).label} - {self.Status(self.status).label}') + return (f'LN-{str(self.id)[:8]}: {self.Concepts(self.concept).label} - {self.Status(self.status).label}') class Order(models.Model): @@ -203,7 +205,7 @@ class MarketTick(models.Model): maker and taker are commited with bonds (contract is finished and cancellation has a cost) ''' - + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) price = models.DecimalField(max_digits=10, decimal_places=2, default=None, null=True, validators=[MinValueValidator(0)]) volume = models.DecimalField(max_digits=8, decimal_places=8, default=None, null=True, validators=[MinValueValidator(0)]) premium = models.DecimalField(max_digits=5, decimal_places=2, default=None, null=True, validators=[MinValueValidator(-100), MaxValueValidator(999)], blank=True) @@ -235,6 +237,6 @@ class MarketTick(models.Model): tick.save() def __str__(self): - return f'Tick: {self.id}' + return f'Tick: {str(self.id)[:8]}' diff --git a/api/views.py b/api/views.py index 0b9d9721..46dbd2d3 100644 --- a/api/views.py +++ b/api/views.py @@ -116,6 +116,7 @@ class OrderView(viewsets.ViewSet): data['is_maker'] = order.maker == request.user data['is_taker'] = order.taker == request.user data['is_participant'] = data['is_maker'] or data['is_taker'] + data['ur_nick'] = request.user.username # 3) If not a participant and order is not public, forbid. if not data['is_participant'] and order.status != Order.Status.PUB: diff --git a/chat/consumers.py b/chat/consumers.py index 34724987..2013b34a 100644 --- a/chat/consumers.py +++ b/chat/consumers.py @@ -1,12 +1,29 @@ from channels.generic.websocket import AsyncWebsocketConsumer +from api.logics import Logics +from api.models import Order + import json class ChatRoomConsumer(AsyncWebsocketConsumer): + 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) + + # # 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 + + print(self.user_nick) + print(self.order_id) await self.channel_layer.group_add( self.room_group_name, @@ -24,27 +41,24 @@ class ChatRoomConsumer(AsyncWebsocketConsumer): async def receive(self, text_data): text_data_json = json.loads(text_data) message = text_data_json['message'] - username = text_data_json['username'] + nick = text_data_json['nick'] await self.channel_layer.group_send( self.room_group_name, { 'type': 'chatroom_message', 'message': message, - 'username': username, + 'nick': nick, } ) async def chatroom_message(self, event): message = event['message'] - username = event['username'] - try: - message = int(message)*42 - except: - pass + nick = event['nick'] + await self.send(text_data=json.dumps({ 'message': message, - 'username': username, + 'user_nick': nick, })) pass \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 1b34eb74..69c66f59 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -2889,6 +2889,14 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "bufferutil": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.6.tgz", + "integrity": "sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw==", + "requires": { + "node-gyp-build": "^4.3.0" + } + }, "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -3319,6 +3327,15 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.19.tgz", "integrity": "sha512-ZVxXaNy28/k3kJg0Fou5MiYpp88j7H9hLZp8PDC3jV0WFjfH5E9xHb56L0W59cPbKbcHXeP4qyT8PrHp8t6LcQ==" }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, "dayjs": { "version": "1.10.7", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz", @@ -3564,6 +3581,35 @@ "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", "dev": true }, + "es5-ext": { + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -3706,6 +3752,21 @@ } } }, + "ext": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", + "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", + "requires": { + "type": "^2.5.0" + }, + "dependencies": { + "type": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz", + "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==" + } + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -4329,6 +4390,11 @@ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -5927,6 +5993,11 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -5953,6 +6024,11 @@ "whatwg-url": "^5.0.0" } }, + "node-gyp-build": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", + "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==" + }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -7902,11 +7978,24 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, "type-fest": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==" }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, "uglify-es": { "version": "3.3.9", "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", @@ -8123,6 +8212,14 @@ "object-assign": "^4.1.1" } }, + "utf-8-validate": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.8.tgz", + "integrity": "sha512-k4dW/Qja1BYDl2qD4tOMB9PFVha/UJtxTc1cXYOe3WwA/2m0Yn4qB7wLMpJyLJ/7DR0XnTut3HsCSzDT4ZvKgA==", + "requires": { + "node-gyp-build": "^4.3.0" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -8305,6 +8402,34 @@ "integrity": "sha512-cp5qdmHnu5T8wRg2G3vZZHoJPN14aqQ89SyQ11NpGH5zEMDCclt49rzo+MaRazk7/UeILhAI+/sEtcM+7Fr0nw==", "dev": true }, + "websocket": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz", + "integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==", + "requires": { + "bufferutil": "^4.0.1", + "debug": "^2.2.0", + "es5-ext": "^0.10.50", + "typedarray-to-buffer": "^3.1.5", + "utf-8-validate": "^5.0.2", + "yaeti": "^0.0.6" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "whatwg-fetch": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", @@ -8440,6 +8565,11 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" }, + "yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" + }, "yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 33d9d3f8..28aa9147 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -33,6 +33,7 @@ "react-native": "^0.66.4", "react-native-svg": "^12.1.1", "react-qr-code": "^2.0.3", - "react-router-dom": "^5.2.0" + "react-router-dom": "^5.2.0", + "websocket": "^1.0.34" } } diff --git a/frontend/src/components/Chat.js b/frontend/src/components/Chat.js new file mode 100644 index 00000000..606f25df --- /dev/null +++ b/frontend/src/components/Chat.js @@ -0,0 +1,98 @@ +import React, { Component } from 'react'; +import { w3cwebsocket as W3CWebSocket } from "websocket"; +import {Button, TextField, Link, Grid, Typography, Container, Card, CardHeader, Paper, Avatar} from "@mui/material"; +import { withStyles } from "@mui/material"; + + + +export default class Chat extends Component { + constructor(props) { + super(props); + } + + state = { + messages: [], + value:'', + orderId: 2, + }; + + client = new W3CWebSocket('ws://' + window.location.host + '/ws/chat/' + this.props.data.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); + console.log('Got reply!', dataFromServer.type); + if (dataFromServer){ + this.setState((state) => + ({ + messages: [...state.messages, + { + msg: dataFromServer.message, + userNick: dataFromServer.user_nick, + }], + + }) + + ) + } + + } + } + + onButtonClicked = (e) => { + this.client.send(JSON.stringify({ + type: "message", + message: this.state.value, + nick: this.props.data.urNick, + })); + this.state.value = '' + e.preventDefault(); + } + + render() { + return ( + + + {this.state.messages.map(message => <> + {/* {message.userNick == this.props.data.urNick ? align='right' : align='left'} */} + + + } + title={message.userNick} + subheader={message.msg} + /> + + )} + +
+ + + { + this.setState({ value: e.target.value }); + this.value = this.state.value; + }} + /> + + + + + +
+
+ ) + } +} diff --git a/frontend/src/components/MakerPage.js b/frontend/src/components/MakerPage.js index 2abc6e36..7d2da795 100644 --- a/frontend/src/components/MakerPage.js +++ b/frontend/src/components/MakerPage.js @@ -167,7 +167,7 @@ export default class MakerPage extends Component { - + { return( - + ) @@ -335,7 +337,7 @@ handleRatingChange=(e)=>{ // Ask for double confirmation. return( - + ) } @@ -368,13 +370,13 @@ handleRatingChange=(e)=>{ } - - CHAT PLACEHOLDER - + + + - {sendFiatButton ? this.showFiatSentButton() : ""} - {receivedFiatButton ? this.showFiatReceivedButton() : ""} - {openDisputeButton ? this.showOpenDisputeButton() : ""} + {openDisputeButton ? this.showOpenDisputeButton() : ""} + {sendFiatButton ? this.showFiatSentButton() : ""} + {receivedFiatButton ? this.showFiatReceivedButton() : ""} {this.showBondIsLocked()} diff --git a/setup.md b/setup.md index 803fa170..f530c3d8 100644 --- a/setup.md +++ b/setup.md @@ -113,6 +113,7 @@ npm install react-native-svg npm install react-qr-code npm install @mui/material npm install react-markdown +npm install websocket ``` 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)