From 7665a2bb22773f646a9a4b14d95daf9bf87a066d Mon Sep 17 00:00:00 2001 From: Reckless_Satoshi Date: Thu, 20 Apr 2023 08:19:47 -0700 Subject: [PATCH] Refactor tradebox states as switch, fix websocket first connect, show PGP erros. --- .../EncryptedSocketChat/index.tsx | 47 +- .../EncryptedTurtleChat/index.tsx | 39 +- frontend/src/components/TradeBox/index.tsx | 558 +++++++++--------- 3 files changed, 335 insertions(+), 309 deletions(-) diff --git a/frontend/src/components/TradeBox/EncryptedChat/EncryptedSocketChat/index.tsx b/frontend/src/components/TradeBox/EncryptedChat/EncryptedSocketChat/index.tsx index 92d4df2c..551f6c12 100644 --- a/frontend/src/components/TradeBox/EncryptedChat/EncryptedSocketChat/index.tsx +++ b/frontend/src/components/TradeBox/EncryptedChat/EncryptedSocketChat/index.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useLayoutEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { Button, Tooltip, TextField, Grid, Paper } from '@mui/material'; +import { Button, Tooltip, TextField, Grid, Paper, Typography } from '@mui/material'; import { encryptMessage, decryptMessage } from '../../../../pgp'; import { AuditPGPDialog } from '../../../Dialogs'; import { websocketClient, WebsocketConnection } from '../../../../services/Websocket'; @@ -46,10 +46,7 @@ const EncryptedSocketChat: React.FC = ({ const audio = new Audio(`/static/assets/sounds/chat-open.mp3`); const [connected, setConnected] = useState(false); const [peerConnected, setPeerConnected] = useState(false); - const [ownPubKey] = useState(robot.pubKey); - const [ownEncPrivKey] = useState(robot.encPrivKey); const [peerPubKey, setPeerPubKey] = useState(); - const [token] = useState(robot.token); const [serverMessages, setServerMessages] = useState([]); const [value, setValue] = useState(''); const [connection, setConnection] = useState(); @@ -58,12 +55,13 @@ const EncryptedSocketChat: React.FC = ({ const [lastSent, setLastSent] = useState('---BLANK---'); const [messageCount, setMessageCount] = useState(0); const [receivedIndexes, setReceivedIndexes] = useState([]); + const [error, setError] = useState(''); useEffect(() => { - if (!connected) { + if (!connected && robot.avatarLoaded) { connectWebsocket(); } - }, [connected]); + }, [connected, robot]); // Make sure to not keep reconnecting once status is not Chat useEffect(() => { @@ -99,7 +97,7 @@ const EncryptedSocketChat: React.FC = ({ setConnected(true); connection.send({ - message: ownPubKey, + message: robot.pubKey, nick: userNick, }); @@ -112,10 +110,10 @@ const EncryptedSocketChat: React.FC = ({ const createJsonFile: () => object = () => { return { credentials: { - own_public_key: ownPubKey, + own_public_key: robot.pubKey, peer_public_key: peerPubKey, - encrypted_private_key: ownEncPrivKey, - passphrase: token, + encrypted_private_key: robot.encPrivKey, + passphrase: robot.token, }, messages, }; @@ -131,7 +129,7 @@ const EncryptedSocketChat: React.FC = ({ if ( connection != null && dataFromServer.message.substring(0, 36) == `-----BEGIN PGP PUBLIC KEY BLOCK-----` && - dataFromServer.message != ownPubKey + dataFromServer.message != robot.pubKey ) { setPeerPubKey(dataFromServer.message); connection.send({ @@ -143,9 +141,9 @@ const EncryptedSocketChat: React.FC = ({ else if (dataFromServer.message.substring(0, 27) == `-----BEGIN PGP MESSAGE-----`) { decryptMessage( dataFromServer.message.split('\\').join('\n'), - dataFromServer.user_nick == userNick ? ownPubKey : peerPubKey, - ownEncPrivKey, - token, + dataFromServer.user_nick == userNick ? robot.pubKey : peerPubKey, + robot.encPrivKey, + robot.token, ).then((decryptedData) => { setWaitingEcho(waitingEcho ? decryptedData.decryptedMessage !== lastSent : false); setLastSent(decryptedData.decryptedMessage === lastSent ? '----BLANK----' : lastSent); @@ -197,9 +195,9 @@ const EncryptedSocketChat: React.FC = ({ }; const onButtonClicked = (e: any) => { - if (token && value.includes(token)) { + if (robot.token && value.includes(robot.token)) { alert( - `Aye! You just sent your own robot token to your peer in chat, that's a catastrophic idea! So bad your message was blocked.`, + `Aye! You just sent your own robot robot.token to your peer in chat, that's a catastrophic idea! So bad your message was blocked.`, ); setValue(''); } @@ -217,16 +215,16 @@ const EncryptedSocketChat: React.FC = ({ setValue(''); setWaitingEcho(true); setLastSent(value); - encryptMessage(value, ownPubKey, peerPubKey, ownEncPrivKey, token).then( - (encryptedMessage) => { + encryptMessage(value, robot.pubKey, peerPubKey, robot.encPrivKey, robot.token) + .then((encryptedMessage) => { if (connection != null) { connection.send({ message: encryptedMessage.toString().split('\n').join('\\'), nick: userNick, }); } - }, - ); + }) + .catch((error) => setError(error.toString())); } e.preventDefault(); }; @@ -244,10 +242,10 @@ const EncryptedSocketChat: React.FC = ({ onClose={() => setAudit(false)} orderId={Number(orderId)} messages={messages} - own_pub_key={ownPubKey || ''} - own_enc_priv_key={ownEncPrivKey || ''} + own_pub_key={robot.pubKey || ''} + own_enc_priv_key={robot.encPrivKey || ''} peer_pub_key={peerPubKey || 'Not received yet'} - passphrase={token || ''} + passphrase={robot.token || ''} onClickBack={() => setAudit(false)} /> @@ -343,6 +341,9 @@ const EncryptedSocketChat: React.FC = ({ + + {error} + diff --git a/frontend/src/components/TradeBox/EncryptedChat/EncryptedTurtleChat/index.tsx b/frontend/src/components/TradeBox/EncryptedChat/EncryptedTurtleChat/index.tsx index 4badad61..9478d30d 100644 --- a/frontend/src/components/TradeBox/EncryptedChat/EncryptedTurtleChat/index.tsx +++ b/frontend/src/components/TradeBox/EncryptedChat/EncryptedTurtleChat/index.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { Button, TextField, Grid, Paper } from '@mui/material'; +import { Button, TextField, Grid, Paper, Typography } from '@mui/material'; import { encryptMessage, decryptMessage } from '../../../../pgp'; import { AuditPGPDialog } from '../../../Dialogs'; import { Robot } from '../../../../models'; @@ -45,10 +45,7 @@ const EncryptedTurtleChat: React.FC = ({ const audio = new Audio(`/static/assets/sounds/chat-open.mp3`); const [peerConnected, setPeerConnected] = useState(false); - const [ownPubKey] = useState(robot.pubKey || ''); - const [ownEncPrivKey] = useState(robot.encPrivKey || ''); const [peerPubKey, setPeerPubKey] = useState(); - const [token] = useState(robot.token || ''); const [value, setValue] = useState(''); const [audit, setAudit] = useState(false); const [waitingEcho, setWaitingEcho] = useState(false); @@ -56,6 +53,7 @@ const EncryptedTurtleChat: React.FC = ({ const [messageCount, setMessageCount] = useState(0); const [serverMessages, setServerMessages] = useState([]); const [lastIndex, setLastIndex] = useState(0); + const [error, setError] = useState(''); useEffect(() => { if (messages.length > messageCount) { @@ -91,10 +89,10 @@ const EncryptedTurtleChat: React.FC = ({ const createJsonFile: () => object = () => { return { credentials: { - own_public_key: ownPubKey, + own_public_key: robot.pubKey, peer_public_key: peerPubKey, - encrypted_private_key: ownEncPrivKey, - passphrase: token, + encrypted_private_key: robot.encPrivKey, + passphrase: robot.token, }, messages, }; @@ -106,9 +104,9 @@ const EncryptedTurtleChat: React.FC = ({ if (dataFromServer.message.substring(0, 27) == `-----BEGIN PGP MESSAGE-----`) { decryptMessage( dataFromServer.message.split('\\').join('\n'), - dataFromServer.nick == userNick ? ownPubKey : peerPubKey, - ownEncPrivKey, - token, + dataFromServer.nick == userNick ? robot.pubKey : peerPubKey, + robot.encPrivKey, + robot.token, ).then((decryptedData) => { setLastSent(decryptedData.decryptedMessage === lastSent ? '----BLANK----' : lastSent); setLastIndex(lastIndex < dataFromServer.index ? dataFromServer.index : lastIndex); @@ -160,9 +158,9 @@ const EncryptedTurtleChat: React.FC = ({ }; const onButtonClicked = (e: any) => { - if (token && value.includes(token)) { + if (robot.token && value.includes(robot.token)) { alert( - `Aye! You just sent your own robot token to your peer in chat, that's a catastrophic idea! So bad your message was blocked.`, + `Aye! You just sent your own robot robot.token to your peer in chat, that's a catastrophic idea! So bad your message was blocked.`, ); setValue(''); } @@ -191,8 +189,8 @@ const EncryptedTurtleChat: React.FC = ({ else if (value != '') { setWaitingEcho(true); setLastSent(value); - encryptMessage(value, ownPubKey, peerPubKey, ownEncPrivKey, token).then( - (encryptedMessage) => { + encryptMessage(value, robot.pubKey, peerPubKey, robot.encPrivKey, robot.token) + .then((encryptedMessage) => { apiClient .post(baseUrl, `/api/chat/`, { PGP_message: encryptedMessage.toString().split('\n').join('\\'), @@ -211,8 +209,8 @@ const EncryptedTurtleChat: React.FC = ({ setWaitingEcho(false); setValue(''); }); - }, - ); + }) + .catch((error) => setError(error.toString())); } e.preventDefault(); }; @@ -230,10 +228,10 @@ const EncryptedTurtleChat: React.FC = ({ onClose={() => setAudit(false)} orderId={Number(orderId)} messages={messages} - own_pub_key={ownPubKey || ''} - own_enc_priv_key={ownEncPrivKey || ''} + own_pub_key={robot.pubKey || ''} + own_enc_priv_key={robot.encPrivKey || ''} peer_pub_key={peerPubKey || 'Not received yet'} - passphrase={token || ''} + passphrase={robot.token || ''} onClickBack={() => setAudit(false)} /> @@ -323,6 +321,9 @@ const EncryptedTurtleChat: React.FC = ({ + + {error} + diff --git a/frontend/src/components/TradeBox/index.tsx b/frontend/src/components/TradeBox/index.tsx index d8054b4c..b3a78a30 100644 --- a/frontend/src/components/TradeBox/index.tsx +++ b/frontend/src/components/TradeBox/index.tsx @@ -49,6 +49,7 @@ import { Order, Robot, Settings } from '../../models'; import { EncryptedChatMessage } from './EncryptedChat'; import CollabCancelAlert from './CollabCancelAlert'; import { Bolt } from '@mui/icons-material'; +import es from 'date-fns/esm/locale/es/index.js'; interface loadingButtonsProps { cancel: boolean; @@ -304,276 +305,250 @@ const TradeBox = ({ let prompt = () => Wops!; let bondStatus: 'hide' | 'locked' | 'unlocked' | 'settled' = 'hide'; - // 0: 'Waiting for maker bond' - if (status == 0) { - if (isMaker) { - title = 'Lock {{amountSats}} Sats to PUBLISH order'; - titleVariables = { amountSats: pn(order.bond_satoshis) }; - prompt = () => { - return ; - }; - bondStatus = 'hide'; - } - + switch (status) { + // 0: 'Waiting for maker bond' + case 0: + if (isMaker) { + title = 'Lock {{amountSats}} Sats to PUBLISH order'; + titleVariables = { amountSats: pn(order.bond_satoshis) }; + prompt = () => { + return ; + }; + bondStatus = 'hide'; + } + break; // 1: 'Public' - } else if (status == 1) { - if (isMaker) { - title = 'Your order is public'; - prompt = () => { - return ( - - ); - }; - bondStatus = 'locked'; - } - + case 1: + if (isMaker) { + title = 'Your order is public'; + prompt = () => { + return ( + + ); + }; + bondStatus = 'locked'; + } + break; // 2: 'Paused' - } else if (status == 2) { - if (isMaker) { - title = 'Your order is paused'; - prompt = () => { - return ( - - ); - }; - bondStatus = 'locked'; - } + case 2: + if (isMaker) { + title = 'Your order is paused'; + prompt = () => { + return ( + + ); + }; + bondStatus = 'locked'; + } + break; // 3: 'Waiting for taker bond' - } else if (status == 3) { - if (isMaker) { - title = 'A taker has been found!'; - prompt = () => { - return ; - }; - bondStatus = 'locked'; - } else { - title = 'Lock {{amountSats}} Sats to TAKE order'; - titleVariables = { amountSats: pn(order.bond_satoshis) }; - prompt = () => { - return ; - }; - bondStatus = 'hide'; - } + case 3: + if (isMaker) { + title = 'A taker has been found!'; + prompt = () => { + return ; + }; + bondStatus = 'locked'; + } else { + title = 'Lock {{amountSats}} Sats to TAKE order'; + titleVariables = { amountSats: pn(order.bond_satoshis) }; + prompt = () => { + return ; + }; + bondStatus = 'hide'; + } + break; // 5: 'Expired' - } else if (status == 5) { - title = 'The order has expired'; - prompt = () => { - return ( - { - onRenewOrder(); - setLoadingButtons({ ...noLoadingButtons, renewOrder: true }); - }} - /> - ); - }; - bondStatus = 'hide'; // To do: show bond status according to expiry message. + case 5: + title = 'The order has expired'; + prompt = () => { + return ( + { + onRenewOrder(); + setLoadingButtons({ ...noLoadingButtons, renewOrder: true }); + }} + /> + ); + }; + bondStatus = 'hide'; // To do: show bond status according to expiry message. // 6: 'Waiting for trade collateral and buyer invoice' - } else if (status == 6) { - bondStatus = 'locked'; - if (isBuyer) { - title = 'Submit payout info'; - titleVariables = { amountSats: pn(order.invoice_amount) }; - prompt = function () { - return ( - - ); - }; - } else { - title = 'Lock {{amountSats}} Sats as collateral'; - titleVariables = { amountSats: pn(order.escrow_satoshis) }; - titleColor = 'warning'; - prompt = () => { - return ; - }; - } + case 6: + bondStatus = 'locked'; + if (isBuyer) { + title = 'Submit payout info'; + titleVariables = { amountSats: pn(order.invoice_amount) }; + prompt = function () { + return ( + + ); + }; + } else { + title = 'Lock {{amountSats}} Sats as collateral'; + titleVariables = { amountSats: pn(order.escrow_satoshis) }; + titleColor = 'warning'; + prompt = () => { + return ; + }; + } + break; // 7: 'Waiting only for seller trade collateral' - } else if (status == 7) { - bondStatus = 'locked'; - if (isBuyer) { - title = 'Your info looks good!'; - prompt = () => { - return ; - }; - } else { - title = 'Lock {{amountSats}} Sats as collateral'; - titleVariables = { amountSats: pn(order.escrow_satoshis) }; - titleColor = 'warning'; - prompt = () => { - return ; - }; - } + case 7: + bondStatus = 'locked'; + if (isBuyer) { + title = 'Your info looks good!'; + prompt = () => { + return ; + }; + } else { + title = 'Lock {{amountSats}} Sats as collateral'; + titleVariables = { amountSats: pn(order.escrow_satoshis) }; + titleColor = 'warning'; + prompt = () => { + return ; + }; + } + break; // 8: 'Waiting only for buyer invoice' - } else if (status == 8) { - bondStatus = 'locked'; - if (isBuyer) { - title = 'Submit payout info'; - titleVariables = { amountSats: pn(order.invoice_amount) }; - prompt = () => { - return ( - - ); - }; - } else { - title = 'The trade collateral is locked!'; - prompt = () => { - return ; - }; - } + case 8: + bondStatus = 'locked'; + if (isBuyer) { + title = 'Submit payout info'; + titleVariables = { amountSats: pn(order.invoice_amount) }; + prompt = () => { + return ( + + ); + }; + } else { + title = 'The trade collateral is locked!'; + prompt = () => { + return ; + }; + } + break; // 9: 'Sending fiat - In chatroom' // 10: 'Fiat sent - In chatroom' - } else if (status == 9 || status == 10) { - title = isBuyer ? 'Chat with the seller' : 'Chat with the buyer'; - prompt = function () { - return ( - setOpen({ ...open, confirmFiatSent: true })} - onClickConfirmReceived={() => setOpen({ ...open, confirmFiatReceived: true })} - loadingSent={loadingButtons.fiatSent} - loadingReceived={loadingButtons.fiatReceived} - onClickDispute={() => setOpen({ ...open, confirmDispute: true })} - loadingDispute={loadingButtons.openDispute} - baseUrl={baseUrl} - messages={messages} - setMessages={setMessages} - /> - ); - }; - bondStatus = 'locked'; + case 9: + case 10: + title = isBuyer ? 'Chat with the seller' : 'Chat with the buyer'; + prompt = function () { + return ( + setOpen({ ...open, confirmFiatSent: true })} + onClickConfirmReceived={() => setOpen({ ...open, confirmFiatReceived: true })} + loadingSent={loadingButtons.fiatSent} + loadingReceived={loadingButtons.fiatReceived} + onClickDispute={() => setOpen({ ...open, confirmDispute: true })} + loadingDispute={loadingButtons.openDispute} + baseUrl={baseUrl} + messages={messages} + setMessages={setMessages} + /> + ); + }; + bondStatus = 'locked'; + break; // 11: 'In dispute' - } else if (status == 11) { - bondStatus = 'settled'; - if (order.statement_submitted) { - title = 'We have received your statement'; - prompt = function () { - return ; - }; - } else { - title = 'A dispute has been opened'; - prompt = function () { - return ( - - ); - }; - } + case 11: + bondStatus = 'settled'; + if (order.statement_submitted) { + title = 'We have received your statement'; + prompt = function () { + return ; + }; + } else { + title = 'A dispute has been opened'; + prompt = function () { + return ( + + ); + }; + } + break; + // 12: 'Collaboratively cancelled' - } else if (status == 12) { + case 12: + break; // 13: 'Sending satoshis to buyer' - } else if (status == 13) { - if (isBuyer) { - bondStatus = 'unlocked'; - title = 'Attempting Lightning Payment'; - prompt = function () { - return ; - }; - } else { - title = 'Trade finished!'; - titleColor = 'success'; - titleIcon = function () { - return ; - }; - prompt = function () { - return ( - { - onRenewOrder(); - setLoadingButtons({ ...noLoadingButtons, renewOrder: true }); - }} - /> - ); - }; - } + case 13: + if (isBuyer) { + bondStatus = 'unlocked'; + title = 'Attempting Lightning Payment'; + prompt = function () { + return ; + }; + } else { + title = 'Trade finished!'; + titleColor = 'success'; + titleIcon = function () { + return ; + }; + prompt = function () { + return ( + { + onRenewOrder(); + setLoadingButtons({ ...noLoadingButtons, renewOrder: true }); + }} + /> + ); + }; + } + break; // 14: 'Sucessful trade' - } else if (status == 14) { - title = 'Trade finished!'; - titleColor = 'success'; - titleIcon = function () { - return ; - }; - prompt = function () { - return ( - { - onRenewOrder(); - setLoadingButtons({ ...noLoadingButtons, renewOrder: true }); - }} - /> - ); - }; - // 15: 'Failed lightning network routing' - } else if (status == 15) { - if (isBuyer) { - bondStatus = 'unlocked'; - title = 'Lightning Routing Failed'; - prompt = function () { - return ( - - ); - }; - } else { + case 14: title = 'Trade finished!'; titleColor = 'success'; titleIcon = function () { @@ -594,29 +569,78 @@ const TradeBox = ({ /> ); }; - } + break; + + // 15: 'Failed lightning network routing' + case 15: + if (isBuyer) { + bondStatus = 'unlocked'; + title = 'Lightning Routing Failed'; + prompt = function () { + return ( + + ); + }; + } else { + title = 'Trade finished!'; + titleColor = 'success'; + titleIcon = function () { + return ; + }; + prompt = function () { + return ( + { + onRenewOrder(); + setLoadingButtons({ ...noLoadingButtons, renewOrder: true }); + }} + /> + ); + }; + } + break; // 16: 'Wait for dispute resolution' - } else if (status == 16) { - bondStatus = 'settled'; - title = 'We have the statements'; - prompt = function () { - return ; - }; + case 16: + bondStatus = 'settled'; + title = 'We have the statements'; + prompt = function () { + return ; + }; + break; // 17: 'Maker lost dispute' // 18: 'Taker lost dispute' - } else if ((status == 17 && isMaker) || (status == 18 && !isMaker)) { - title = 'You have lost the dispute'; - prompt = function () { - return ; - }; - bondStatus = 'settled'; - } else if ((status == 17 && !isMaker) || (status == 18 && isMaker)) { - title = 'You have won the dispute'; - prompt = function () { - return ; - }; + case 17: + case 18: + if ((status === 17 && isMaker) || (status === 18 && !isMaker)) { + title = 'You have lost the dispute'; + prompt = function () { + return ; + }; + bondStatus = 'settled'; + } else if ((status === 17 && !isMaker) || (status === 18 && isMaker)) { + title = 'You have won the dispute'; + prompt = function () { + return ; + }; + } + break; + + default: + break; } return { title, titleVariables, titleColor, prompt, bondStatus, titleIcon };