diff --git a/frontend/src/basic/NavBar/NavBar.tsx b/frontend/src/basic/NavBar/NavBar.tsx index bf207cd7..79b2ae1f 100644 --- a/frontend/src/basic/NavBar/NavBar.tsx +++ b/frontend/src/basic/NavBar/NavBar.tsx @@ -92,7 +92,7 @@ const NavBar = ({ return ( { -// this.setState({ openStatsForNerds: true }); -// }; - -// handleClickCloseStatsForNerds = () => { -// this.setState({ openStatsForNerds: false }); -// }; - -// handleClickOpenCommunity = () => { -// this.setState({ openCommunity: true }); -// }; - -// handleClickCloseCommunity = () => { -// this.setState({ openCommunity: false }); -// }; - -// handleClickOpenProfile = () => { -// this.setState({ openProfile: true, profileShown: true }); -// }; - -// handleClickCloseProfile = () => { -// this.setState({ openProfile: false }); -// }; - -// handleClickOpenExchangeSummary = () => { -// this.setState({ openExchangeSummary: true }); -// }; - -// handleClickCloseExchangeSummary = () => { -// this.setState({ openExchangeSummary: false }); -// }; - -// handleSubmitInvoiceClicked = (e, rewardInvoice) => { -// this.setState({ badInvoice: false, showRewardsSpinner: true }); - -// apiClient -// .post('/api/reward/', { -// invoice: rewardInvoice, -// }) -// .then((data) => { -// this.setState({ badInvoice: data.bad_invoice, showRewardsSpinner: false }); -// this.props.setInfo({ -// ...this.props.info, -// badInvoice: data.bad_invoice, -// openClaimRewards: !data.successful_withdrawal, -// withdrawn: !!data.successful_withdrawal, -// showRewardsSpinner: false, -// }); -// this.props.setRobot({ -// ...this.props.robot, -// earnedRewards: data.successful_withdrawal ? 0 : this.props.robot.earnedRewards, -// }); -// }); -// e.preventDefault(); -// }; - -// handleSetStealthInvoice = (wantsStealth) => { -// apiClient -// .put('/api/stealth/', { wantsStealth }) -// .then((data) => -// this.props.setRobot({ ...this.props.robot, stealthInvoices: data?.wantsStealth }), -// ); -// }; - -// showProfileButton = () => { -// return ( -// this.props.robot.avatarLoaded && -// (this.props.robot.token -// ? systemClient.getCookie('robot_token') === this.props.robot.token -// : true) && -// systemClient.getCookie('sessionid') -// ); -// }; - -// bottomBarDesktop = () => { -// const { t } = this.props; -// const hasRewards = this.props.robot.earnedRewards > 0; -// const hasOrder = !!( -// (this.props.robot.activeOrderId > 0) & -// !this.state.profileShown & -// this.props.robot.avatarLoaded -// ); -// const fontSize = this.props.theme.typography.fontSize; -// const fontSizeFactor = fontSize / 14; // default fontSize is 14 -// const typographyProps = { -// primaryTypographyProps: { fontSize }, -// secondaryTypographyProps: { fontSize: (fontSize * 12) / 14 }, -// }; -// return ( -// -// -// -//
-// -// -// -// 0) & !this.state.profileShown -// ? 'primary' -// : undefined -// } -// nickname={this.props.robot.nickname} -// onLoad={() => -// this.props.setRobot({ ...this.props.robot, avatarLoaded: true }) -// } -// /> -// -// -// -// -//
-//
- -// -// -// -// -// -// -// -// -// -// - -// -// -// -// -// -// -// -// -// -// - -// -// -// -// -// -// -// -// -// -// - -// -// -// -// -// -// -// -// -// -// - -// -// -// -// -// -// -// -// -// -// - -// -// -// {this.LangSelect()} -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -//
-//
-// ); -// }; - -// bottomBarPhone = () => { -// const { t } = this.props; -// const hasRewards = this.props.robot.earnedRewards > 0; -// const hasOrder = !!( -// (this.props.info.active_order_id > 0) & -// !this.state.profileShown & -// this.props.robot.avatarLoaded -// ); -// return ( -// -// -// -//
-// -// -// 0) & !this.state.profileShown -// ? 'primary' -// : undefined -// } -// nickname={this.props.robot.nickname} -// onLoad={() => this.props.setRobot({ ...this.props.robot, avatarLoaded: true })} -// /> -// -// -//
-//
- -// -// -// -// -// -// -// -// -// - -// -// -// -// -// -// -// -// -// - -// -// -// -// -// -// -// -// -// - -// -// -// -// -// -// -// -// -// - -// -// -// {this.LangSelect()} -// -// -// -// -// -// -// -// -// -// -// this.props.fetchInfo()} -// onClick={this.handleClickOpenStatsForNerds} -// > -// -// -// -// -// -//
-//
-// ); -// }; - -// render() { -// return ( -//
- -// -// this.props.setInfo({ ...this.props.info, openUpdateClient: false }) -// } -// /> - -// - -// this.props.setRobot({ ...robot, ...newParam })} -// stealthInvoices={this.props.robot.stealthInvoices} -// handleSetStealthInvoice={this.handleSetStealthInvoice} -// /> - -// - -// {this.bottomBarDesktop()} - -// {this.bottomBarPhone()} -//
-// ); -// } -// } - -// export default NavBar; diff --git a/frontend/src/components/TradeBox/Dialogs/ConfirmDispute.tsx b/frontend/src/components/TradeBox/Dialogs/ConfirmDispute.tsx new file mode 100644 index 00000000..c4964ee8 --- /dev/null +++ b/frontend/src/components/TradeBox/Dialogs/ConfirmDispute.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { + Dialog, + DialogTitle, + DialogActions, + DialogContent, + DialogContentText, + Button, +} from '@mui/material'; + +interface ConfirmDisputeDialogProps { + open: boolean; + onClose: () => void; + onAgreeClick: () => void; +} + +export const ConfirmDisputeDialog = ({ + open, + onClose, + onAgreeClick, +}: ConfirmDisputeDialogProps): JSX.Element => { + const { t } = useTranslation(); + + return ( + + {t('Do you want to open a dispute?')} + + + {t( + 'The RoboSats staff will examine the statements and evidence provided. You need to build a complete case, as the staff cannot read the chat. It is best to provide a burner contact method with your statement. The satoshis in the trade escrow will be sent to the dispute winner, while the dispute loser will lose the bond.', + )} + +
+ + {t( + 'Make sure to EXPORT the chat log. The staff might request your exported chat log JSON in order to solve discrepancies. It is your responsibility to store it.', + )} + +
+ + + + +
+ ); +}; + +export default ConfirmDisputeDialog; diff --git a/frontend/src/components/TradeBox/Dialogs/ConfirmFiatReceived.tsx b/frontend/src/components/TradeBox/Dialogs/ConfirmFiatReceived.tsx new file mode 100644 index 00000000..d44b7745 --- /dev/null +++ b/frontend/src/components/TradeBox/Dialogs/ConfirmFiatReceived.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { + Dialog, + DialogTitle, + DialogActions, + DialogContent, + DialogContentText, + Button, +} from '@mui/material'; +import { Order } from '../../../models'; +import currencyDict from '../../../../static/assets/currencies.json'; +import { pn } from '../../../utils'; +import { LoadingButton } from '@mui/lab'; + +interface ConfirmFiatReceivedDialogProps { + open: boolean; + loadingButton: boolean; + order: Order; + onClose: () => void; + onConfirmClick: () => void; +} + +export const ConfirmFiatReceivedDialog = ({ + open, + loadingButton, + onClose, + order, + onConfirmClick, +}: ConfirmFiatReceivedDialogProps): JSX.Element => { + const { t } = useTranslation(); + const currencyCode = currencyDict[order.currency.toString()]; + const amount = pn(parseFloat(parseFloat(order.amount).toFixed(order.currency == 1000 ? 8 : 4))); + + return ( + + + {t('Confirm you received {{amount}} {{currencyCode}}?', { currencyCode, amount })} + + + + {t( + 'Confirming that you received the fiat will finalize the trade. The satoshis in the escrow will be released to the buyer. Only confirm after the {{amount}} {{currencyCode}} have arrived to your account. In addition, if you have received the payment and do not confirm it, you risk losing your bond.', + { currencyCode, amount }, + )} + + + + + + {t('Confirm')} + + + + ); +}; + +export default ConfirmFiatReceivedDialog; diff --git a/frontend/src/components/TradeBox/Dialogs/index.ts b/frontend/src/components/TradeBox/Dialogs/index.ts new file mode 100644 index 00000000..1effc88f --- /dev/null +++ b/frontend/src/components/TradeBox/Dialogs/index.ts @@ -0,0 +1,2 @@ +export { ConfirmDisputeDialog } from './ConfirmDispute'; +export { ConfirmFiatReceivedDialog } from './ConfirmFiatReceived'; diff --git a/frontend/src/components/TradeBox/index.tsx b/frontend/src/components/TradeBox/index.tsx index c962bd65..bf909cc0 100644 --- a/frontend/src/components/TradeBox/index.tsx +++ b/frontend/src/components/TradeBox/index.tsx @@ -1,6 +1,7 @@ -import React, { Component } from 'react'; -import { withTranslation, Trans } from 'react-i18next'; +import React, { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; import { + Box, Alert, AlertTitle, ToggleButtonGroup, @@ -31,1854 +32,1834 @@ import QRCode from 'react-qr-code'; import Countdown, { zeroPad } from 'react-countdown'; import EncryptedChat from './EncryptedChat'; import TradeSummary from './TradeSummary'; -import MediaQuery from 'react-responsive'; import { systemClient } from '../../services/System'; import { apiClient } from '../../services/api'; +import { ConfirmDisputeDialog, ConfirmFiatReceivedDialog } from './Dialogs'; // Icons -import PercentIcon from '@mui/icons-material/Percent'; -import BookIcon from '@mui/icons-material/Book'; -import LockIcon from '@mui/icons-material/Lock'; -import LockOpenIcon from '@mui/icons-material/LockOpen'; -import BalanceIcon from '@mui/icons-material/Balance'; -import ContentCopy from '@mui/icons-material/ContentCopy'; -import PauseCircleIcon from '@mui/icons-material/PauseCircle'; -import PlayCircleIcon from '@mui/icons-material/PlayCircle'; -import BoltIcon from '@mui/icons-material/Bolt'; -import LinkIcon from '@mui/icons-material/Link'; -import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet'; -import FavoriteIcon from '@mui/icons-material/Favorite'; -import RocketLaunchIcon from '@mui/icons-material/RocketLaunch'; -import RefreshIcon from '@mui/icons-material/Refresh'; +import { + Percent, + Book, + Lock, + LockOpen, + Balance, + ContentCopy, + PauseCircle, + PlayCircle, + Bolt, + Link, + AccountBalanceWallet, + Favorite, + RocketLaunch, + Refresh, +} from '@mui/icons-material'; import { NewTabIcon } from '../Icons'; -import { pn } from '../../utils/prettyNumbers'; - -class TradeBox extends Component { - invoice_escrow_duration = 3; - - constructor(props) { - super(props); - this.state = { - openConfirmFiatReceived: false, - loadingButtonFiatSent: false, - loadingButtonFiatReceived: false, - loadingSubmitInvoice: false, - loadingSubmitAddress: false, - openConfirmDispute: false, - receiveTab: 0, - address: '', - miningFee: 1.05, - badInvoice: false, - badAddress: false, - badStatement: false, - }; - } - - Sound = (soundFileName) => ( - // Four filenames: "locked-invoice", "taker-found", "open-chat", "successful" -