Merge branch 'add-typescript-support' into main (#103)

This commit is contained in:
Reckless_Satoshi 2022-05-10 11:57:51 -07:00
commit ce0f5f8f74
No known key found for this signature in database
GPG Key ID: 9C4585B561315571
33 changed files with 2971 additions and 1443 deletions

26
frontend/.eslintrc.json Normal file
View File

@ -0,0 +1,26 @@
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},
"plugins": [
"@typescript-eslint",
"react-hooks"
],
"extends": [
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended"
],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"react/prop-types": "off"
},
"settings": {
"react": {
"pragma": "React",
"version": "detect"
}
}
}

View File

@ -1,14 +1,15 @@
{ {
"presets": [ "presets": [
[ "@babel/preset-env",
"@babel/preset-env", "@babel/preset-react",
{ "@babel/preset-typescript"
"targets": { ],
"node": "10" "plugins": [
} [
} "@babel/plugin-transform-runtime",
], {
"@babel/preset-react" "regenerator": true
], }
"plugins": ["@babel/plugin-proposal-class-properties"] ]
} ]
}

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"dev": "webpack --mode development --watch", "dev": "webpack --watch --progress --mode development",
"test": "jest", "test": "jest",
"build": "webpack --mode production" "build": "webpack --mode production"
}, },
@ -12,16 +12,27 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"devDependencies": { "devDependencies": {
"@babel/core": "^7.16.7", "@babel/core": "^7.17.9",
"@babel/preset-env": "^7.16.7", "@babel/plugin-transform-runtime": "^7.17.0",
"@babel/preset-env": "^7.16.11",
"@babel/preset-react": "^7.16.7", "@babel/preset-react": "^7.16.7",
"babel-loader": "^8.2.3", "@babel/preset-typescript": "^7.16.7",
"@babel/runtime": "^7.17.9",
"@types/jest": "^27.4.1",
"@types/react": "^17.0.44",
"@types/react-dom": "^18.0.1",
"@types/webpack": "^5.28.0",
"@typescript-eslint/eslint-plugin": "^5.20.0",
"@typescript-eslint/parser": "^5.20.0",
"babel-loader": "^8.2.5",
"eslint": "^8.13.0",
"eslint-plugin-react": "^7.29.4",
"eslint-plugin-react-hooks": "^4.4.0",
"jest": "^27.5.1", "jest": "^27.5.1",
"openpgp": "^5.2.1", "ts-node": "^10.7.0",
"react": "^17.0.2", "typescript": "^4.6.3",
"react-dom": "^17.0.2", "webpack": "^5.72.0",
"webpack": "^5.65.0", "webpack-cli": "^4.9.2"
"webpack-cli": "^4.9.1"
}, },
"dependencies": { "dependencies": {
"@babel/plugin-proposal-class-properties": "^7.16.7", "@babel/plugin-proposal-class-properties": "^7.16.7",
@ -29,6 +40,7 @@
"@emotion/styled": "^11.6.0", "@emotion/styled": "^11.6.0",
"@material-ui/core": "^4.12.3", "@material-ui/core": "^4.12.3",
"@material-ui/icons": "^4.11.2", "@material-ui/icons": "^4.11.2",
"@mui/base": "^5.0.0-alpha.77",
"@mui/icons-material": "^5.2.5", "@mui/icons-material": "^5.2.5",
"@mui/lab": "^5.0.0-alpha.73", "@mui/lab": "^5.0.0-alpha.73",
"@mui/material": "^5.2.7", "@mui/material": "^5.2.7",
@ -41,7 +53,10 @@
"i18next-http-backend": "^1.4.0", "i18next-http-backend": "^1.4.0",
"i18next-xhr-backend": "^3.2.2", "i18next-xhr-backend": "^3.2.2",
"material-ui-image": "^3.3.2", "material-ui-image": "^3.3.2",
"openpgp": "^5.2.1",
"react": "^18.0.0",
"react-countdown": "^2.3.2", "react-countdown": "^2.3.2",
"react-dom": "^18.1.0",
"react-i18next": "^11.16.2", "react-i18next": "^11.16.2",
"react-native": "^0.66.4", "react-native": "^0.66.4",
"react-native-svg": "^12.3.0", "react-native-svg": "^12.3.0",

View File

@ -1,5 +1,5 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { render } from "react-dom"; import ReactDOM from 'react-dom/client';
import HomePage from "./HomePage"; import HomePage from "./HomePage";
import { CssBaseline, IconButton} from "@mui/material"; import { CssBaseline, IconButton} from "@mui/material";
import { ThemeProvider, createTheme } from '@mui/material/styles'; import { ThemeProvider, createTheme } from '@mui/material/styles';
@ -46,5 +46,8 @@ export default class App extends Component {
} }
} }
const appDiv = document.getElementById("app"); const root = ReactDOM.createRoot(
render(<App />, appDiv); document.getElementById("app")
);
root.render(<App />);

View File

@ -2,15 +2,16 @@ import React, { useState } from "react";
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useAutocomplete } from '@mui/base/AutocompleteUnstyled'; import { useAutocomplete } from '@mui/base/AutocompleteUnstyled';
import CheckIcon from '@mui/icons-material/Check';
import CloseIcon from '@mui/icons-material/Close';
import { styled } from '@mui/material/styles'; import { styled } from '@mui/material/styles';
import PaymentIcon from './payment-methods/Icons' import { Button, Tooltip } from "@mui/material";
import {Button} from "@mui/material";
import { paymentMethods, swapDestinations } from "./payment-methods/Methods"; import { paymentMethods, swapDestinations } from "./payment-methods/Methods";
// Icons
import DashboardCustomizeIcon from '@mui/icons-material/DashboardCustomize'; import DashboardCustomizeIcon from '@mui/icons-material/DashboardCustomize';
import AddIcon from '@mui/icons-material/Add'; import AddIcon from '@mui/icons-material/Add';
import PaymentIcon from './payment-methods/Icons'
import CheckIcon from '@mui/icons-material/Check';
import CloseIcon from '@mui/icons-material/Close';
const Root = styled('div')( const Root = styled('div')(
({ theme }) => ` ({ theme }) => `
@ -191,7 +192,7 @@ const Listbox = styled('ul')(
); );
export default function AutocompletePayments(props) { export default function AutocompletePayments(props) {
const { t, i18n } = useTranslation(); const { t } = useTranslation();
const { const {
getRootProps, getRootProps,
getInputLabelProps, getInputLabelProps,
@ -237,22 +238,24 @@ export default function AutocompletePayments(props) {
return ( return (
<Root> <Root>
<div style={{height:'5px'}}></div> <div style={{height:'5px'}}></div>
<div {...getRootProps()} > <Tooltip placement="top" enterTouchDelay={300} enterDelay={700} enterNextDelay={2000} title={props.tooltipTitle}>
<Label {...getInputLabelProps()} error={props.error}>{props.label}</Label> <div {...getRootProps()} >
<InputWrapper ref={setAnchorEl} error={props.error} className={focused ? 'focused' : ''}> <Label {...getInputLabelProps()} error={props.error ? "error" : null}> {props.label}</Label>
{value.map((option, index) => ( <InputWrapper ref={setAnchorEl} error={props.error ? "error" : null} className={focused ? 'focused' : ''}>
<StyledTag label={t(option.name)} icon={option.icon} {...getTagProps({ index })} /> {value.map((option, index) => (
))} <StyledTag label={t(option.name)} icon={option.icon} {...getTagProps({ index })} />
<input {...getInputProps()} value={val}/> ))}
</InputWrapper> <input {...getInputProps()} value={val ? val :""}/>
</div> </InputWrapper>
</div>
</Tooltip>
{groupedOptions.length > 0 ? ( {groupedOptions.length > 0 ? (
<Listbox {...getListboxProps()}> <Listbox {...getListboxProps()}>
<div style={{position:'fixed', minHeight:'20px', marginLeft: 120-props.listHeaderText.length*3, marginTop: '-13px'}}> <div style={{position:'fixed', minHeight:'20px', marginLeft: 120-props.listHeaderText.length*3, marginTop: '-13px'}}>
<ListHeader ><i>{props.listHeaderText+""} </i> </ListHeader> <ListHeader ><i>{props.listHeaderText+""} </i> </ListHeader>
</div> </div>
{groupedOptions.map((option, index) => ( {groupedOptions.map((option, index) => (
<li {...getOptionProps({ option, index })}> <li key={option.name} {...getOptionProps({ option, index })}>
<Button fullWidth={true} color='inherit' size="small" sx={{textTransform: "none"}} style={{justifyContent: "flex-start"}}> <Button fullWidth={true} color='inherit' size="small" sx={{textTransform: "none"}} style={{justifyContent: "flex-start"}}>
<div style={{position:'relative', right: '4px', top:'4px'}}> <div style={{position:'relative', right: '4px', top:'4px'}}>
<AddIcon style={{color : '#1976d2'}} sx={{width:18,height:18}} /> <AddIcon style={{color : '#1976d2'}} sx={{width:18,height:18}} />

View File

@ -1,5 +1,5 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { withTranslation, Trans} from "react-i18next"; import { withTranslation } from "react-i18next";
import { Badge, Tooltip, Stack, Paper, Button, FormControlLabel, Checkbox, RadioGroup, ListItemButton, Typography, Grid, Select, MenuItem, FormControl, FormHelperText, ListItemText, ListItemAvatar, IconButton, CircularProgress} from "@mui/material"; import { Badge, Tooltip, Stack, Paper, Button, FormControlLabel, Checkbox, RadioGroup, ListItemButton, Typography, Grid, Select, MenuItem, FormControl, FormHelperText, ListItemText, ListItemAvatar, IconButton, CircularProgress} from "@mui/material";
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { DataGrid } from '@mui/x-data-grid'; import { DataGrid } from '@mui/x-data-grid';
@ -7,7 +7,7 @@ import currencyDict from '../../static/assets/currencies.json';
import MediaQuery from 'react-responsive' import MediaQuery from 'react-responsive'
import Image from 'material-ui-image' import Image from 'material-ui-image'
import getFlags from './getFlags' import FlagWithProps from './FlagWithProps'
import { pn } from "../utils/prettyNumbers"; import { pn } from "../utils/prettyNumbers";
import PaymentText from './PaymentText' import PaymentText from './PaymentText'
@ -110,13 +110,13 @@ class BookPage extends Component {
renderCell: (params) => {return ( renderCell: (params) => {return (
<ListItemButton style={{ cursor: "pointer" }}> <ListItemButton style={{ cursor: "pointer" }}>
<ListItemAvatar> <ListItemAvatar>
<Tooltip placement="right" enterTouchDelay="0" title={t(params.row.robot_status)}> <Tooltip placement="right" enterTouchDelay={0} title={t(params.row.robot_status)}>
<Badge variant="dot" overlap="circular" badgeContent="" color={this.statusBadgeColor(params.row.robot_status)}> <Badge variant="dot" overlap="circular" badgeContent="" color={this.statusBadgeColor(params.row.robot_status)}>
<Badge overlap="circular" anchorOrigin={{horizontal: 'right', vertical: 'bottom'}} badgeContent={<div style={{position:"relative", left:"11px", top:"2px"}}> {params.row.type == t("Buyer") ? <SendReceiveIcon sx={{transform: "scaleX(-1)",height:"20px",width:"20px"}} color="secondary"/> : <SendReceiveIcon sx={{height:"20px",width:"20px"}} color="primary"/>}</div>}> <Badge overlap="circular" anchorOrigin={{horizontal: 'right', vertical: 'bottom'}} badgeContent={<div style={{position:"relative", left:"11px", top:"2px"}}> {params.row.type == t("Buyer") ? <SendReceiveIcon sx={{transform: "scaleX(-1)",height:"20px",width:"20px"}} color="secondary"/> : <SendReceiveIcon sx={{height:"20px",width:"20px"}} color="primary"/>}</div>}>
<div style={{ width: 45, height: 45 }}> <div style={{ width: 45, height: 45 }}>
<Image className='bookAvatar' <Image className='bookAvatar'
disableError='true' disableError={true}
disableSpinner='true' disableSpinner={true}
color='null' color='null'
alt={params.row.robot} alt={params.row.robot}
src={params.row.avatar} src={params.row.avatar}
@ -137,7 +137,11 @@ class BookPage extends Component {
)}}, )}},
{ field: 'currency', headerName: t("Currency"), width: 100, { field: 'currency', headerName: t("Currency"), width: 100,
renderCell: (params) => {return ( renderCell: (params) => {return (
<div style={{ cursor: "pointer", display:'flex',alignItems:'center', flexWrap:'wrap'}}>{params.row.currency+" "}{getFlags(params.row.currency)}</div>) <div style={{ cursor: "pointer", display:'flex',alignItems:'center', flexWrap:'wrap'}}>
{params.row.currency+" "}
<FlagWithProps code={params.row.currency} />
</div>
)
}}, }},
{ field: 'payment_method', headerName: t("Payment Method"), width: 180 , { field: 'payment_method', headerName: t("Payment Method"), width: 180 ,
renderCell: (params) => {return ( renderCell: (params) => {return (
@ -157,7 +161,7 @@ class BookPage extends Component {
NoRowsOverlay: () => ( NoRowsOverlay: () => (
<Stack height="100%" alignItems="center" justifyContent="center"> <Stack height="100%" alignItems="center" justifyContent="center">
<div style={{ height:"220px"}}/> <div style={{ height:"220px"}}/>
<this.NoOrdersFound/> {this.NoOrdersFound()}
</Stack> </Stack>
), ),
NoResultsOverlay: () => ( NoResultsOverlay: () => (
@ -167,7 +171,7 @@ class BookPage extends Component {
) )
}} }}
pageSize={this.props.bookLoading ? 0 : this.state.pageSize} pageSize={this.props.bookLoading ? 0 : this.state.pageSize}
rowsPerPageOptions={[6,20,50]} rowsPerPageOptions={[0,6,20,50]}
onPageSizeChange={(newPageSize) => this.setState({pageSize:newPageSize})} onPageSizeChange={(newPageSize) => this.setState({pageSize:newPageSize})}
onRowClick={(params) => this.handleRowClick(params.row.id)} // Whole row is clickable, but the mouse only looks clickly in some places. onRowClick={(params) => this.handleRowClick(params.row.id)} // Whole row is clickable, but the mouse only looks clickly in some places.
/> />
@ -206,14 +210,14 @@ class BookPage extends Component {
{ field: 'robot', headerName: t("Robot"), width: 64, { field: 'robot', headerName: t("Robot"), width: 64,
renderCell: (params) => {return ( renderCell: (params) => {return (
<div style={{ position: "relative", left: "-5px" }}> <div style={{ position: "relative", left: "-5px" }}>
<Tooltip placement="right" enterTouchDelay="0" title={params.row.robot+" ("+t(params.row.robot_status)+")"}> <Tooltip placement="right" enterTouchDelay={0} title={params.row.robot+" ("+t(params.row.robot_status)+")"}>
<Badge variant="dot" overlap="circular" badgeContent="" color={this.statusBadgeColor(params.row.robot_status)}> <Badge variant="dot" overlap="circular" badgeContent="" color={this.statusBadgeColor(params.row.robot_status)}>
<Badge overlap="circular" anchorOrigin={{horizontal: 'right', vertical: 'bottom'}} badgeContent={<div style={{position:"relative", left:"11px", top:"2px"}}> {params.row.type == t("Buyer") ? <SendReceiveIcon sx={{transform: "scaleX(-1)",height:"20px",width:"20px"}} color="secondary"/> : <SendReceiveIcon sx={{height:"20px",width:"20px"}} color="primary"/>}</div>}> <Badge overlap="circular" anchorOrigin={{horizontal: 'right', vertical: 'bottom'}} badgeContent={<div style={{position:"relative", left:"11px", top:"2px"}}> {params.row.type == t("Buyer") ? <SendReceiveIcon sx={{transform: "scaleX(-1)",height:"20px",width:"20px"}} color="secondary"/> : <SendReceiveIcon sx={{height:"20px",width:"20px"}} color="primary"/>}</div>}>
<div style={{ width: 45, height: 45 }}> <div style={{ width: 45, height: 45 }}>
<Image className='bookAvatar' <Image className='bookAvatar'
disableError='true' disableError={true}
disableSpinner='true' disableSpinner={true}
color='null' color={null}
alt={params.row.robot} alt={params.row.robot}
src={params.row.avatar} src={params.row.avatar}
/> />
@ -227,14 +231,17 @@ class BookPage extends Component {
{ field: 'type', headerName: t("Is"), width: 60, hide:'true'}, { field: 'type', headerName: t("Is"), width: 60, hide:'true'},
{ field: 'amount', headerName: t("Amount"), type: 'number', width: 84, { field: 'amount', headerName: t("Amount"), type: 'number', width: 84,
renderCell: (params) => {return ( renderCell: (params) => {return (
<Tooltip placement="right" enterTouchDelay="0" title={t(params.row.type)}> <Tooltip placement="right" enterTouchDelay={0} title={t(params.row.type)}>
<div style={{ cursor: "pointer" }}>{this.amountToString(params.row.amount,params.row.has_range, params.row.min_amount, params.row.max_amount)}</div> <div style={{ cursor: "pointer" }}>{this.amountToString(params.row.amount,params.row.has_range, params.row.min_amount, params.row.max_amount)}</div>
</Tooltip> </Tooltip>
)} }, )} },
{ field: 'currency', headerName: t("Currency"), width: 85, { field: 'currency', headerName: t("Currency"), width: 85,
renderCell: (params) => {return ( renderCell: (params) => {return (
// <Tooltip placement="left" enterTouchDelay="0" title={params.row.payment_method}> // <Tooltip placement="left" enterTouchDelay={0} title={params.row.payment_method}>
<div style={{ cursor: "pointer", display:'flex',alignItems:'center', flexWrap:'wrap'}}>{params.row.currency+" "}{getFlags(params.row.currency)}</div> <div style={{ cursor: "pointer", display:'flex',alignItems:'center', flexWrap:'wrap'}}>
{params.row.currency+" "}
<FlagWithProps code={params.row.currency} />
</div>
// </Tooltip> // </Tooltip>
)} }, )} },
{ field: 'payment_method', headerName: t("Payment Method"), width: 180, hide:'true'}, { field: 'payment_method', headerName: t("Payment Method"), width: 180, hide:'true'},
@ -248,7 +255,7 @@ class BookPage extends Component {
)} }, )} },
{ field: 'premium', headerName: t("Premium"), type: 'number', width: 85, { field: 'premium', headerName: t("Premium"), type: 'number', width: 85,
renderCell: (params) => {return ( renderCell: (params) => {return (
<Tooltip placement="left" enterTouchDelay="0" title={pn(params.row.price) + " " +params.row.currency+ "/BTC" }> <Tooltip placement="left" enterTouchDelay={0} title={pn(params.row.price) + " " +params.row.currency+ "/BTC" }>
<div style={{ cursor: "pointer" }}>{parseFloat(parseFloat(params.row.premium).toFixed(4))+"%" }</div> <div style={{ cursor: "pointer" }}>{parseFloat(parseFloat(params.row.premium).toFixed(4))+"%" }</div>
</Tooltip> </Tooltip>
)} }, )} },
@ -258,7 +265,7 @@ class BookPage extends Component {
NoRowsOverlay: () => ( NoRowsOverlay: () => (
<Stack height="100%" alignItems="center" justifyContent="center"> <Stack height="100%" alignItems="center" justifyContent="center">
<div style={{ height:"220px"}}/> <div style={{ height:"220px"}}/>
<this.NoOrdersFound/> {this.NoOrdersFound()}
</Stack> </Stack>
), ),
NoResultsOverlay: () => ( NoResultsOverlay: () => (
@ -268,7 +275,7 @@ class BookPage extends Component {
) )
}} }}
pageSize={this.props.bookLoading ? 0 : this.state.pageSize} pageSize={this.props.bookLoading ? 0 : this.state.pageSize}
rowsPerPageOptions={[6,20,50]} rowsPerPageOptions={[0,6,20,50]}
onPageSizeChange={(newPageSize) => this.setState({pageSize:newPageSize})} onPageSizeChange={(newPageSize) => this.setState({pageSize:newPageSize})}
onRowClick={(params) => this.handleRowClick(params.row.id)} // Whole row is clickable, but the mouse only looks clickly in some places. onRowClick={(params) => this.handleRowClick(params.row.id)} // Whole row is clickable, but the mouse only looks clickly in some places.
@ -304,6 +311,7 @@ class BookPage extends Component {
NoOrdersFound=()=>{ NoOrdersFound=()=>{
const { t } = this.props; const { t } = this.props;
return( return(
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
@ -319,7 +327,7 @@ class BookPage extends Component {
<Grid item> <Grid item>
<Button size="large" variant="contained" color='primary' to='/make/' component={Link}>{t("Make Order")}</Button> <Button size="large" variant="contained" color='primary' to='/make/' component={Link}>{t("Make Order")}</Button>
</Grid> </Grid>
<Typography color="primary" component="body1" variant="body1"> <Typography color="primary" variant="body1">
{t("Be the first one to create an order")} {t("Be the first one to create an order")}
<br/> <br/>
<br/> <br/>
@ -338,13 +346,13 @@ class BookPage extends Component {
<Grid item xs={6} align="right"> <Grid item xs={6} align="right">
<FormControl align="center"> <FormControl align="center">
<FormHelperText align="center"> <FormHelperText align="center" sx={{position:"relative", left:"10px", textAlign:"center"}}>
<div style={{position:"relative", left:"10px", textAlign:"center"}}>{t("I want to")} </div> {t("I want to")}
</FormHelperText> </FormHelperText>
<RadioGroup row> <RadioGroup row>
<div style={{position:"relative", left:"20px"}}> <div style={{position:"relative", left:"20px"}}>
<FormControlLabel <FormControlLabel
control={<Checkbox defaultChecked={true} icon={<BuySatsIcon sx={{width:"30px",height:"30px"}} color="inherit"/>} checkedIcon={<BuySatsCheckedIcon sx={{width:"30px",height:"30px"}} color="primary"/>}/>} control={<Checkbox icon={<BuySatsIcon sx={{width:"30px",height:"30px"}} color="inherit"/>} checkedIcon={<BuySatsCheckedIcon sx={{width:"30px",height:"30px"}} color="primary"/>}/>}
label={ label={
<div style={{position:"relative",top:"-13px"}}> <div style={{position:"relative",top:"-13px"}}>
{this.props.buyChecked ? {this.props.buyChecked ?
@ -360,7 +368,7 @@ class BookPage extends Component {
/> />
</div> </div>
<FormControlLabel <FormControlLabel
control={<Checkbox defaultChecked={true} icon={<SellSatsIcon sx={{width:"30px",height:"30px"}} color="inherit"/>} checkedIcon={<SellSatsCheckedIcon sx={{width:"30px",height:"30px"}} color="secondary"/>}/>} control={<Checkbox icon={<SellSatsIcon sx={{width:"30px",height:"30px"}} color="inherit"/>} checkedIcon={<SellSatsCheckedIcon sx={{width:"30px",height:"30px"}} color="secondary"/>}/>}
label={ label={
<div style={{position:"relative",top:"-13px"}}> <div style={{position:"relative",top:"-13px"}}>
{this.props.sellChecked ? {this.props.sellChecked ?
@ -380,25 +388,23 @@ class BookPage extends Component {
<Grid item xs={6} align="left"> <Grid item xs={6} align="left">
<FormControl align="center"> <FormControl align="center">
<FormHelperText align="center"> <FormHelperText align="center" sx={{textAlign:"center", position:"relative", left:"-5px"}}>
<div style={{textAlign:"center", position:"relative", left:"-5px"}}>
{this.props.bookType == 0 ? t("and receive") : (this.props.bookType == 1 ? t("and pay with") : t("and use") )} {this.props.bookType == 0 ? t("and receive") : (this.props.bookType == 1 ? t("and pay with") : t("and use") )}
</div>
</FormHelperText> </FormHelperText>
<Select <Select
//autoWidth={true} //autoWidth={true}
sx={{width:120}} sx={{width:120}}
label={t("Select Payment Currency")} label={t("Select Payment Currency")}
required="true" required={true}
value={this.props.bookCurrency} value={this.props.bookCurrency}
inputProps={{ inputProps={{
style: {textAlign:"center"} style: {textAlign:"center"}
}} }}
onChange={this.handleCurrencyChange} onChange={this.handleCurrencyChange}
> <MenuItem value={0}><div style={{display:'flex',alignItems:'center', flexWrap:'wrap'}}>{getFlags('ANY')}{" "+t("ANY_currency")}</div></MenuItem> > <MenuItem value={0}><div style={{display:'flex',alignItems:'center', flexWrap:'wrap'}}><FlagWithProps code="ANY" />{" "+t("ANY_currency")}</div></MenuItem>
{ {
Object.entries(currencyDict) Object.entries(currencyDict)
.map( ([key, value]) => <MenuItem value={parseInt(key)}><div style={{display:'flex',alignItems:'center', flexWrap:'wrap'}}>{getFlags(value)}{" "+value}</div></MenuItem> ) .map( ([key, value]) => <MenuItem key={key} value={parseInt(key)}><div style={{display:'flex',alignItems:'center', flexWrap:'wrap'}}><FlagWithProps code={value} />{" "+value}</div></MenuItem> )
} }
</Select> </Select>
</FormControl> </FormControl>
@ -426,14 +432,14 @@ class BookPage extends Component {
{/* Desktop Book */} {/* Desktop Book */}
<MediaQuery minWidth={930}> <MediaQuery minWidth={930}>
<Paper elevation={0} style={{width: 925, maxHeight: 500, overflow: 'auto'}}> <Paper elevation={0} style={{width: 925, maxHeight: 500, overflow: 'auto'}}>
<this.bookListTableDesktop/> {this.bookListTableDesktop()}
</Paper> </Paper>
</MediaQuery> </MediaQuery>
{/* Smartphone Book */} {/* Smartphone Book */}
<MediaQuery maxWidth={929}> <MediaQuery maxWidth={929}>
<Paper elevation={0} style={{width: 395, maxHeight: 450, overflow: 'auto'}}> <Paper elevation={0} style={{width: 395, maxHeight: 450, overflow: 'auto'}}>
<this.bookListTablePhone/> {this.bookListTablePhone()}
</Paper> </Paper>
</MediaQuery> </MediaQuery>
</Grid> </Grid>
@ -445,7 +451,7 @@ class BookPage extends Component {
</Grid> </Grid>
</Grid> </Grid>
); );
}; }
} }
export default withTranslation()(BookPage); export default withTranslation()(BookPage);

View File

@ -1,9 +1,8 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { withTranslation, Trans} from "react-i18next"; import { withTranslation } from "react-i18next";
import {FormControlLabel, Link, Switch, CircularProgress, Badge, Tooltip, TextField, ListItemAvatar, Button, Avatar,Paper, Grid, IconButton, Typography, Select, MenuItem, List, ListItemText, ListItem, ListItemIcon, ListItemButton, Divider, Dialog, DialogContent} from "@mui/material"; import {FormControlLabel, Link, Switch, CircularProgress, Badge, Tooltip, TextField, ListItemAvatar, Button, Avatar,Paper, Grid, IconButton, Typography, Select, MenuItem, List, ListItemText, ListItem, ListItemIcon, ListItemButton, Divider, Dialog, DialogContent} from "@mui/material";
import MediaQuery from 'react-responsive' import MediaQuery from 'react-responsive'
import { Link as LinkRouter } from 'react-router-dom' import { Link as LinkRouter } from 'react-router-dom'
import Flags from 'country-flag-icons/react/3x2'
// Icons // Icons
import SettingsIcon from '@mui/icons-material/Settings'; import SettingsIcon from '@mui/icons-material/Settings';
@ -16,7 +15,7 @@ import PriceChangeIcon from '@mui/icons-material/PriceChange';
import BoltIcon from '@mui/icons-material/Bolt'; import BoltIcon from '@mui/icons-material/Bolt';
import GitHubIcon from '@mui/icons-material/GitHub'; import GitHubIcon from '@mui/icons-material/GitHub';
import EqualizerIcon from '@mui/icons-material/Equalizer'; import EqualizerIcon from '@mui/icons-material/Equalizer';
import SendIcon from '@mui/icons-material/Send';
import PublicIcon from '@mui/icons-material/Public'; import PublicIcon from '@mui/icons-material/Public';
import NumbersIcon from '@mui/icons-material/Numbers'; import NumbersIcon from '@mui/icons-material/Numbers';
import PasswordIcon from '@mui/icons-material/Password'; import PasswordIcon from '@mui/icons-material/Password';
@ -29,6 +28,8 @@ import EmojiEventsIcon from '@mui/icons-material/EmojiEvents';
import AmbossIcon from "./icons/AmbossIcon"; import AmbossIcon from "./icons/AmbossIcon";
import FavoriteIcon from '@mui/icons-material/Favorite'; import FavoriteIcon from '@mui/icons-material/Favorite';
import { CommunityDialog } from './Dialogs';
import { getCookie } from "../utils/cookies"; import { getCookie } from "../utils/cookies";
import { pn } from "../utils/prettyNumbers"; import { pn } from "../utils/prettyNumbers";
@ -62,8 +63,11 @@ class BottomBar extends Component {
showRewardsSpinner: false, showRewardsSpinner: false,
withdrawn: false, withdrawn: false,
}; };
}
componentDidMount() {
this.getInfo(); this.getInfo();
} }
getInfo() { getInfo() {
this.setState(null) this.setState(null)
@ -179,62 +183,6 @@ class BottomBar extends Component {
this.setState({openCommuniy: false}); this.setState({openCommuniy: false});
}; };
CommunityDialog =() =>{
const { t } = this.props;
return(
<Dialog
open={this.state.openCommuniy}
onClose={this.handleClickCloseCommunity}
aria-labelledby="community-dialog-title"
aria-describedby="community-description"
>
<DialogContent>
<Typography component="h5" variant="h5">{t("Community")}</Typography>
<Typography component="body2" variant="body2">
<p>{t("Support is only offered via public channels. Join our Telegram community if you have questions or want to hang out with other cool robots. Please, use our Github Issues if you find a bug or want to see new features!")}</p>
</Typography>
<List>
<Divider/>
<ListItemButton component="a" target="_blank" href="https://t.me/robosats">
<ListItemIcon><SendIcon/></ListItemIcon>
<ListItemText primary={t("Join the RoboSats group")}
secondary={t("Telegram (English / Main)")}/>
</ListItemButton>
<Divider/>
<ListItem>
<ListItemIcon><SendIcon/></ListItemIcon>
<ListItemText secondary={t("RoboSats Telegram Communities")}>
<Tooltip title={t("Join RoboSats Spanish speaking community!")}>
<IconButton component="a" target="_blank" href="https://t.me/robosats_es"><Flags.ES width={30} height={30} style={{filter: 'drop-shadow(2px 2px 2px #444444)'}}/></IconButton>
</Tooltip>
<Tooltip title={t("Join RoboSats Russian speaking community!")}>
<IconButton component="a" target="_blank" href="https://t.me/robosats_ru"><Flags.RU width={30} height={30} style={{filter: 'drop-shadow(2px 2px 2px #444444)'}}/></IconButton>
</Tooltip>
<Tooltip title={t("Join RoboSats Chinese speaking community!")}>
<IconButton component="a" target="_blank" href="https://t.me/robosats_cn"><Flags.CN width={30} height={30} style={{filter: 'drop-shadow(2px 2px 2px #444444)'}}/></IconButton>
</Tooltip>
<Tooltip title={t("Join RoboSats English speaking community!")}>
<IconButton component="a" target="_blank" href="https://t.me/robosats"><Flags.US width={30} height={30} style={{filter: 'drop-shadow(2px 2px 2px #444444)'}}/></IconButton>
</Tooltip>
</ListItemText>
</ListItem>
<Divider/>
<ListItemButton component="a" target="_blank" href="https://github.com/Reckless-Satoshi/robosats/issues">
<ListItemIcon><GitHubIcon/></ListItemIcon>
<ListItemText primary={t("Tell us about a new feature or a bug")}
secondary={t("Github Issues - The Robotic Satoshis Open Source Project")}/>
</ListItemButton>
</List>
</DialogContent>
</Dialog>
)
}
handleClickOpenProfile = () => { handleClickOpenProfile = () => {
this.getInfo(); this.getInfo();
this.setState({openProfile: true, profileShown: true}); this.setState({openProfile: true, profileShown: true});
@ -349,7 +297,7 @@ class BottomBar extends Component {
size='small' size='small'
InputProps={{ InputProps={{
endAdornment: endAdornment:
<Tooltip disableHoverListener enterTouchDelay="0" title={t("Copied!")}> <Tooltip disableHoverListener enterTouchDelay={0} title={t("Copied!")}>
<IconButton onClick= {()=> (navigator.clipboard.writeText(getCookie("robot_token")) & this.props.setAppState({copiedToken:true}))}> <IconButton onClick= {()=> (navigator.clipboard.writeText(getCookie("robot_token")) & this.props.setAppState({copiedToken:true}))}>
<ContentCopy color={this.props.copiedToken ? "inherit" : "primary"}/> <ContentCopy color={this.props.copiedToken ? "inherit" : "primary"}/>
</IconButton> </IconButton>
@ -363,7 +311,7 @@ class BottomBar extends Component {
<Divider/> <Divider/>
<Grid spacing={1} align="center"> <Grid item align="center">
<FormControlLabel labelPlacement="start"control={ <FormControlLabel labelPlacement="start"control={
<Switch <Switch
checked={this.state.showRewards} checked={this.state.showRewards}
@ -384,7 +332,7 @@ class BottomBar extends Component {
size='small' size='small'
InputProps={{ InputProps={{
endAdornment: endAdornment:
<Tooltip disableHoverListener enterTouchDelay="0" title={t("Copied!")}> <Tooltip disableHoverListener enterTouchDelay={0} title={t("Copied!")}>
<IconButton onClick= {()=>navigator.clipboard.writeText('http://'+this.getHost()+'/ref/'+this.state.referral_code)}> <IconButton onClick= {()=>navigator.clipboard.writeText('http://'+this.getHost()+'/ref/'+this.state.referral_code)}>
<ContentCopy /> <ContentCopy />
</IconButton> </IconButton>
@ -400,7 +348,7 @@ class BottomBar extends Component {
</ListItemIcon> </ListItemIcon>
{!this.state.openClaimRewards ? {!this.state.openClaimRewards ?
<ListItemText secondary={t("Your earned rewards")}> <ListItemText secondary={t("Your earned rewards")}>
<Grid container xs={12}> <Grid container>
<Grid item xs={9}> <Grid item xs={9}>
<Typography>{this.state.earned_rewards+" Sats"}</Typography> <Typography>{this.state.earned_rewards+" Sats"}</Typography>
</Grid> </Grid>
@ -454,33 +402,36 @@ class BottomBar extends Component {
bottomBarDesktop =()=>{ bottomBarDesktop =()=>{
const { t } = this.props; const { t } = this.props;
var hasRewards = this.state.earned_rewards > 0 ? true: false;
var hasOrder = this.state.active_order_id > 0 & !this.state.profileShown & this.props.avatarLoaded ? true : false;
return( return(
<Paper elevation={6} style={{height:40}}> <Paper elevation={6} style={{height:40}}>
<this.StatsDialog/> {this.StatsDialog()}
<this.CommunityDialog/> {this.dialogProfile()}
<this.dialogProfile/> {this.exchangeSummaryDialog()}
<this.exchangeSummaryDialog/> <Grid container>
<Grid container xs={12}>
<Grid item xs={1.9}> <Grid item xs={1.9}>
<div style={{display: this.props.avatarLoaded ? '':'none'}}> <div style={{display: this.props.avatarLoaded ? '':'none'}}>
<ListItemButton onClick={this.handleClickOpenProfile} > <ListItemButton onClick={this.handleClickOpenProfile} >
<Tooltip open={this.state.earned_rewards > 0 ? true: false} title={t("You can claim satoshis!")}> <Tooltip
<Tooltip open={(this.state.active_order_id > 0 & !this.state.profileShown & this.props.avatarLoaded) ? true: false} open={hasRewards || hasOrder}
title={t("You have an active order")}> title={(hasRewards ? t("You can claim satoshis!")+" ": "" )+
(hasOrder ? t("You have an active order"):"")}
>
<ListItemAvatar sx={{ width: 30, height: 30 }} > <ListItemAvatar sx={{ width: 30, height: 30 }} >
<Badge badgeContent={(this.state.active_order_id > 0 & !this.state.profileShown) ? "": null} color="primary"> <Badge badgeContent={(this.state.active_order_id > 0 & !this.state.profileShown) ? "": null} color="primary">
<Avatar className='flippedSmallAvatar' sx={{margin: 0, top: -13}} <Avatar className='flippedSmallAvatar' sx={{margin: 0, top: -13}}
alt={this.props.nickname} alt={this.props.nickname}
imgProps={{ imgProps={{
onLoad:() => this.props.setAppState({avatarLoaded: true}), onLoad:() => this.props.setAppState({avatarLoaded: true}),
}} }}
src={this.props.nickname ? window.location.origin +'/static/assets/avatars/' + this.props.nickname + '.png' : null} src={this.props.nickname ? window.location.origin +'/static/assets/avatars/' + this.props.nickname + '.png' : null}
/> />
</Badge> </Badge>
</ListItemAvatar> </ListItemAvatar>
</Tooltip> </Tooltip>
</Tooltip>
<ListItemText primary={this.props.nickname}/> <ListItemText primary={this.props.nickname}/>
</ListItemButton> </ListItemButton>
</div> </div>
@ -553,10 +504,10 @@ bottomBarDesktop =()=>{
<Grid container item xs={1}> <Grid container item xs={1}>
<Grid item xs={6}> <Grid item xs={6}>
<this.LangSelect/> {this.LangSelect()}
</Grid> </Grid>
<Grid item xs={3}> <Grid item xs={3}>
<Tooltip enterTouchDelay="250" title={t("Show community and support links")}> <Tooltip enterTouchDelay={250} title={t("Show community and support links")}>
<IconButton <IconButton
color="primary" color="primary"
aria-label="Community" aria-label="Community"
@ -566,7 +517,7 @@ bottomBarDesktop =()=>{
</Tooltip> </Tooltip>
</Grid> </Grid>
<Grid item xs={3}> <Grid item xs={3}>
<Tooltip enterTouchDelay="250" title={t("Show stats for nerds")}> <Tooltip enterTouchDelay={250} title={t("Show stats for nerds")}>
<IconButton color="primary" <IconButton color="primary"
aria-label="Stats for Nerds" aria-label="Stats for Nerds"
onClick={this.handleClickOpenStatsForNerds} > onClick={this.handleClickOpenStatsForNerds} >
@ -690,7 +641,7 @@ bottomBarDesktop =()=>{
<ListItemIcon size="small"> <ListItemIcon size="small">
<PercentIcon/> <PercentIcon/>
</ListItemIcon> </ListItemIcon>
<Grid container xs={12}> <Grid container >
<Grid item xs={6}> <Grid item xs={6}>
<ListItemText <ListItemText
primaryTypographyProps={{fontSize: '14px'}} primaryTypographyProps={{fontSize: '14px'}}
@ -718,19 +669,20 @@ bottomBarDesktop =()=>{
bottomBarPhone =()=>{ bottomBarPhone =()=>{
const { t } = this.props; const { t } = this.props;
var hasRewards = this.state.earned_rewards > 0 ? true: false;
var hasOrder = this.state.active_order_id > 0 & !this.state.profileShown & this.props.avatarLoaded ? true : false;
return( return(
<Paper elevation={6} style={{height:40}}> <Paper elevation={6} style={{height:40}}>
<this.StatsDialog/> {this.StatsDialog()}
<this.CommunityDialog/> {this.exchangeSummaryDialog()}
<this.exchangeSummaryDialog/> {this.dialogProfile()}
<this.dialogProfile/> <Grid container>
<Grid container xs={12}>
<Grid item xs={1.6}> <Grid item xs={1.6}>
<div style={{display: this.props.avatarLoaded ? '':'none'}}> <div style={{display: this.props.avatarLoaded ? '':'none'}}>
<Tooltip open={this.state.earned_rewards > 0 ? true: false} title={t("You can claim satoshis!")}> <Tooltip open={hasRewards || hasOrder}
<Tooltip open={(this.state.active_order_id > 0 & !this.state.profileShown & this.props.avatarLoaded) ? true: false} title={(hasRewards ? t("You can claim satoshis!")+" ": "" )+
title={t("You have an active order")}> (hasOrder ? t("You have an active order"):"")}>
<IconButton onClick={this.handleClickOpenProfile} sx={{margin: 0, bottom: 17, right: 8}} > <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"> <Badge badgeContent={(this.state.active_order_id >0 & !this.state.profileShown) ? "": null} color="primary">
<Avatar className='phoneFlippedSmallAvatar' <Avatar className='phoneFlippedSmallAvatar'
@ -744,12 +696,11 @@ bottomBarPhone =()=>{
</Badge> </Badge>
</IconButton> </IconButton>
</Tooltip> </Tooltip>
</Tooltip>
</div> </div>
</Grid> </Grid>
<Grid item xs={1.6} align="center"> <Grid item xs={1.6} align="center">
<Tooltip enterTouchDelay="300" title={t("Number of public BUY orders")}> <Tooltip enterTouchDelay={300} title={t("Number of public BUY orders")}>
<IconButton onClick={this.handleClickOpenExchangeSummary} > <IconButton onClick={this.handleClickOpenExchangeSummary} >
<Badge badgeContent={this.state.num_public_buy_orders} color="action"> <Badge badgeContent={this.state.num_public_buy_orders} color="action">
<InventoryIcon /> <InventoryIcon />
@ -759,7 +710,7 @@ bottomBarPhone =()=>{
</Grid> </Grid>
<Grid item xs={1.6} align="center"> <Grid item xs={1.6} align="center">
<Tooltip enterTouchDelay="300" title={t("Number of public SELL orders")}> <Tooltip enterTouchDelay={300} title={t("Number of public SELL orders")}>
<IconButton onClick={this.handleClickOpenExchangeSummary} > <IconButton onClick={this.handleClickOpenExchangeSummary} >
<Badge badgeContent={this.state.num_public_sell_orders} color="action"> <Badge badgeContent={this.state.num_public_sell_orders} color="action">
<SellIcon /> <SellIcon />
@ -769,7 +720,7 @@ bottomBarPhone =()=>{
</Grid> </Grid>
<Grid item xs={1.6} align="center"> <Grid item xs={1.6} align="center">
<Tooltip enterTouchDelay="300" title={t("Today active robots")}> <Tooltip enterTouchDelay={300} title={t("Today active robots")}>
<IconButton onClick={this.handleClickOpenExchangeSummary} > <IconButton onClick={this.handleClickOpenExchangeSummary} >
<Badge badgeContent={this.state.active_robots_today} color="action"> <Badge badgeContent={this.state.active_robots_today} color="action">
<SmartToyIcon /> <SmartToyIcon />
@ -779,7 +730,7 @@ bottomBarPhone =()=>{
</Grid> </Grid>
<Grid item xs={1.8} align="center"> <Grid item xs={1.8} align="center">
<Tooltip enterTouchDelay="300" title={t("24h non-KYC bitcoin premium")}> <Tooltip enterTouchDelay={300} title={t("24h non-KYC bitcoin premium")}>
<IconButton onClick={this.handleClickOpenExchangeSummary} > <IconButton onClick={this.handleClickOpenExchangeSummary} >
<Badge badgeContent={this.state.last_day_nonkyc_btc_premium+"%"} color="action"> <Badge badgeContent={this.state.last_day_nonkyc_btc_premium+"%"} color="action">
<PriceChangeIcon /> <PriceChangeIcon />
@ -790,10 +741,10 @@ bottomBarPhone =()=>{
<Grid container item xs={3.8}> <Grid container item xs={3.8}>
<Grid item xs={6}> <Grid item xs={6}>
<this.LangSelect/> {this.LangSelect()}
</Grid> </Grid>
<Grid item xs={3}> <Grid item xs={3}>
<Tooltip enterTouchDelay="250" title={t("Show community and support links")}> <Tooltip enterTouchDelay={250} title={t("Show community and support links")}>
<IconButton <IconButton
color="primary" color="primary"
aria-label="Community" aria-label="Community"
@ -803,7 +754,7 @@ bottomBarPhone =()=>{
</Tooltip> </Tooltip>
</Grid> </Grid>
<Grid item xs={3}> <Grid item xs={3}>
<Tooltip enterTouchDelay="250" title={t("Show stats for nerds")}> <Tooltip enterTouchDelay={250} title={t("Show stats for nerds")}>
<IconButton color="primary" <IconButton color="primary"
aria-label="Stats for Nerds" aria-label="Stats for Nerds"
onClick={this.handleClickOpenStatsForNerds} > onClick={this.handleClickOpenStatsForNerds} >
@ -821,15 +772,20 @@ bottomBarPhone =()=>{
render() { render() {
return ( return (
<div> <div>
<CommunityDialog
isOpen={this.state.openCommuniy}
handleClickCloseCommunity={this.handleClickCloseCommunity}
/>
<MediaQuery minWidth={1200}> <MediaQuery minWidth={1200}>
<this.bottomBarDesktop/> {this.bottomBarDesktop()}
</MediaQuery> </MediaQuery>
<MediaQuery maxWidth={1199}> <MediaQuery maxWidth={1199}>
<this.bottomBarPhone/> {this.bottomBarPhone()}
</MediaQuery> </MediaQuery>
</div> </div>
) )
} }
} }
export default withTranslation()(BottomBar); export default withTranslation()(BottomBar);

View File

@ -75,7 +75,7 @@ class Chat extends Component {
message: this.state.value, message: this.state.value,
nick: this.props.ur_nick, nick: this.props.ur_nick,
})); }));
this.state.value = '' this.setState({value: ""});
} }
e.preventDefault(); e.preventDefault();
} }
@ -84,87 +84,88 @@ class Chat extends Component {
const { t } = this.props; const { t } = this.props;
return ( return (
<Container component="main" maxWidth="xs" > <Container component="main" maxWidth="xs" >
<Grid container xs={12} spacing={0.5}> <Grid container spacing={0.5}>
<Grid item xs={0.3}/> <Grid item xs={0.3}/>
<Grid item xs={5.5}> <Grid item xs={5.5}>
<Paper elevation={1} style={this.state.connected ? {backgroundColor: '#e8ffe6'}: {backgroundColor: '#FFF1C5'}}> <Paper elevation={1} style={this.state.connected ? {backgroundColor: '#e8ffe6'}: {backgroundColor: '#FFF1C5'}}>
<Typography variant='caption' sx={{color: '#111111'}}> <Typography variant='caption' sx={{color: '#111111'}}>
{t("You")+": "}{this.state.connected ? t("connected"): t("disconnected")} {t("You")+": "}{this.state.connected ? t("connected"): t("disconnected")}
</Typography> </Typography>
</Paper>
</Grid>
<Grid item xs={0.4}/>
<Grid item xs={5.5}>
<Paper elevation={1} style={this.state.peer_connected ? {backgroundColor: '#e8ffe6'}: {backgroundColor: '#FFF1C5'}}>
<Typography variant='caption' sx={{color: '#111111'}}>
{t("Peer")+": "}{this.state.peer_connected ? t("connected"): t("disconnected")}
</Typography>
</Paper>
</Grid>
<Grid item xs={0.3}/>
</Grid>
<Paper elevation={1} style={{ height: '300px', maxHeight: '300px' , width: '280px' ,overflow: 'auto', backgroundColor: '#F7F7F7' }}>
{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 sx={{color: '#111111'}}
avatar={
<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: '#eeeeee'}}
title={message.userNick}
subheader={message.msg}
subheaderTypographyProps={{sx: {wordWrap: "break-word", width: '200px', color: '#444444'}}}
/>
:
<CardHeader sx={{color: '#111111'}}
avatar={
<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: '#fafafa'}}
title={message.userNick}
subheader={message.msg}
subheaderTypographyProps={{sx: {wordWrap: "break-word", width: '200px', color: '#444444'}}}
/>}
</Card>
</>)}
<div style={{ float:"left", clear: "both" }} ref={(el) => { this.messagesEnd = el; }}></div>
</Paper> </Paper>
<form noValidate onSubmit={this.onButtonClicked}> </Grid>
<Grid containter alignItems="stretch" style={{ display: "flex" }}> <Grid item xs={0.4}/>
<Grid item alignItems="stretch" style={{ display: "flex"}}> <Grid item xs={5.5}>
<TextField <Paper elevation={1} style={this.state.peer_connected ? {backgroundColor: '#e8ffe6'}: {backgroundColor: '#FFF1C5'}}>
label={t("Type a message")} <Typography variant='caption' sx={{color: '#111111'}}>
variant="standard" {t("Peer")+": "}{this.state.peer_connected ? t("connected"): t("disconnected")}
size="small" </Typography>
helperText={this.state.connected ? null : t("Connecting...")} </Paper>
value={this.state.value} </Grid>
onChange={e => { <Grid item xs={0.3}/>
this.setState({ value: e.target.value }); </Grid>
this.value = this.state.value; <Paper elevation={1} style={{ height: '300px', maxHeight: '300px' , width: '280px' ,overflow: 'auto', backgroundColor: '#F7F7F7' }}>
}} {this.state.messages.map((message, index) =>
sx={{width: 214}} <li style={{listStyleType:"none"}} key={index}>
/> <Card elevation={5} align="left" >
</Grid> {/* If message sender is not our nick, gray color, if it is our nick, green color */}
<Grid item alignItems="stretch" style={{ display: "flex" }}> {message.userNick == this.props.ur_nick ?
<Button sx={{'width':68}} disabled={!this.state.connected} type="submit" variant="contained" color="primary">{t("Send")} </Button> <CardHeader sx={{color: '#111111'}}
</Grid> avatar={
</Grid> <Badge variant="dot" overlap="circular" badgeContent="" color={this.state.connected ? "success" : "error"}>
</form> <Avatar className="flippedSmallAvatar"
<FormHelperText> alt={message.userNick}
{t("The chat has no memory: if you leave, messages are lost.")} <Link target="_blank" href={t("PGP_guide_url")}> {t("Learn easy PGP encryption.")}</Link> src={window.location.origin +'/static/assets/avatars/' + message.userNick + '.png'}
</FormHelperText> />
</Badge>
}
style={{backgroundColor: '#eeeeee'}}
title={message.userNick}
subheader={message.msg}
subheaderTypographyProps={{sx: {wordWrap: "break-word", width: '200px', color: '#444444'}}}
/>
:
<CardHeader sx={{color: '#111111'}}
avatar={
<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: '#fafafa'}}
title={message.userNick}
subheader={message.msg}
subheaderTypographyProps={{sx: {wordWrap: "break-word", width: '200px', color: '#444444'}}}
/>}
</Card>
</li>)}
<div style={{ float:"left", clear: "both" }} ref={(el) => { this.messagesEnd = el; }}></div>
</Paper>
<form noValidate onSubmit={this.onButtonClicked}>
<Grid alignItems="stretch" style={{ display: "flex" }}>
<Grid item alignItems="stretch" style={{ display: "flex"}}>
<TextField
label={t("Type a message")}
variant="standard"
size="small"
helperText={this.state.connected ? null : t("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 sx={{'width':68}} disabled={!this.state.connected} type="submit" variant="contained" color="primary">{t("Send")} </Button>
</Grid>
</Grid>
</form>
<FormHelperText>
{t("The chat has no memory: if you leave, messages are lost.")} <Link target="_blank" href={t("PGP_guide_url")}> {t("Learn easy PGP encryption.")}</Link>
</FormHelperText>
</Container> </Container>
) )
} }

View File

@ -0,0 +1,151 @@
import React from "react";
import { useTranslation } from "react-i18next";
import {
Dialog,
DialogContent,
Divider,
IconButton,
List,
ListItemText,
ListItem,
ListItemIcon,
ListItemButton,
Tooltip,
Typography,
} from "@mui/material";
import SendIcon from '@mui/icons-material/Send';
import GitHubIcon from '@mui/icons-material/GitHub';
import Flags from 'country-flag-icons/react/3x2'
type Props = {
isOpen: boolean;
handleClickCloseCommunity: () => void;
}
const CommunityDialog = ({
isOpen,
handleClickCloseCommunity,
}: Props): JSX.Element => {
const { t } = useTranslation();
const flagProps = {
width: 30,
height: 30,
style: {
filter: "drop-shadow(2px 2px 2px #444444)",
},
};
return (
<Dialog
open={isOpen}
onClose={handleClickCloseCommunity}
aria-labelledby="community-dialog-title"
aria-describedby="community-description"
>
<DialogContent>
<Typography component="h5" variant="h5">
{t("Community")}
</Typography>
<Typography component="div" variant="body2">
<p>{t("Support is only offered via public channels. Join our Telegram community if you have questions or want to hang out with other cool robots. Please, use our Github Issues if you find a bug or want to see new features!")}</p>
</Typography>
<List>
<Divider/>
<ListItemButton
component="a"
target="_blank"
href="https://t.me/robosats"
rel="noreferrer"
>
<ListItemIcon>
<SendIcon/>
</ListItemIcon>
<ListItemText
primary={t("Join the RoboSats group")}
secondary={t("Telegram (English / Main)")}
/>
</ListItemButton>
<Divider/>
<ListItem>
<ListItemIcon>
<SendIcon/>
</ListItemIcon>
<ListItemText secondary={t("RoboSats Telegram Communities")}>
<Tooltip title={t("Join RoboSats Spanish speaking community!") || ""}>
<IconButton
component="a"
target="_blank"
href="https://t.me/robosats_es"
rel="noreferrer"
>
<Flags.ES {...flagProps} />
</IconButton>
</Tooltip>
<Tooltip title={t("Join RoboSats Russian speaking community!") || ""}>
<IconButton
component="a"
target="_blank"
href="https://t.me/robosats_ru"
rel="noreferrer"
>
<Flags.RU {...flagProps} />
</IconButton>
</Tooltip>
<Tooltip title={t("Join RoboSats Chinese speaking community!") || ""}>
<IconButton
component="a"
target="_blank"
href="https://t.me/robosats_cn"
rel="noreferrer"
>
<Flags.CN {...flagProps} />
</IconButton>
</Tooltip>
<Tooltip title={t("Join RoboSats English speaking community!") || ""}>
<IconButton
component="a"
target="_blank"
href="https://t.me/robosats"
rel="noreferrer"
>
<Flags.US {...flagProps} />
</IconButton>
</Tooltip>
</ListItemText>
</ListItem>
<Divider/>
<ListItemButton
component="a"
target="_blank"
href="https://github.com/Reckless-Satoshi/robosats/issues"
rel="noreferrer"
>
<ListItemIcon>
<GitHubIcon/>
</ListItemIcon>
<ListItemText
primary={t("Tell us about a new feature or a bug")}
secondary={t("Github Issues - The Robotic Satoshis Open Source Project")}
/>
</ListItemButton>
</List>
</DialogContent>
</Dialog>
);
};
export default CommunityDialog;

View File

@ -0,0 +1,137 @@
import React from "react";
import { useTranslation } from "react-i18next";
import {
Dialog,
Typography,
Link,
DialogActions,
DialogContent,
Button,
Grid,
} from "@mui/material"
import Image from 'material-ui-image'
import MediaQuery from 'react-responsive'
type Props = {
open: boolean;
onClose: () => void;
}
const InfoDialog = ({
open,
onClose,
}: Props): JSX.Element => {
const { t } = useTranslation();
return (
<Dialog
open={open}
onClose={onClose}
aria-labelledby="info-dialog-title"
aria-describedby="info-dialog-description"
scroll="paper">
<DialogContent>
<MediaQuery minWidth={475}>
<Grid container>
<Grid item xs={8}>
<Typography component="h4" variant="h4">{t("What is RoboSats?")}</Typography>
<Typography component="div" variant="body2">
<p>{t("It is a BTC/FIAT peer-to-peer exchange over lightning.")} <br/>
{t("It simplifies matchmaking and minimizes the need of trust. RoboSats focuses in privacy and speed.")}</p>
<p>{t("RoboSats is an open source project ")} <Link
href='https://github.com/reckless-satoshi/robosats'>{t("(GitHub).")}</Link>
</p>
</Typography>
</Grid>
<Grid item xs={4} align="center">
<Image className='newAvatar'
disableError={true}
cover={true}
color='null'
src={window.location.origin +'/static/assets/images/v0.1.2-04.png'}
/>
</Grid>
</Grid>
</MediaQuery>
<MediaQuery maxWidth={474}>
<Typography component="h4" variant="h4">{t("What is RoboSats?")}</Typography>
<Typography component="div" variant="body2">
<p>{t("It is a BTC/FIAT peer-to-peer exchange over lightning.")+" "} {t("It simplifies matchmaking and minimizes the need of trust. RoboSats focuses in privacy and speed.")}</p>
<img
width='100%'
src={window.location.origin +'/static/assets/images/v0.1.2-03.png'}
/>
<p>{t("RoboSats is an open source project ")} <Link
href='https://github.com/reckless-satoshi/robosats'>{t("(GitHub).")}</Link>
</p>
</Typography>
</MediaQuery>
<Typography component="h5" variant="h5">{t("How does it work?")}</Typography>
<Typography component="div" variant="body2">
<p> {t("AnonymousAlice01 wants to sell bitcoin. She posts a sell order. BafflingBob02 wants to buy bitcoin and he takes Alice's order. Both have to post a small bond using lightning to prove they are real robots. Then, Alice posts the trade collateral also using a lightning hold invoice. RoboSats locks the invoice until Alice confirms she received the fiat, then the satoshis are released to Bob. Enjoy your satoshis, Bob!")}</p>
<p>{t("At no point, AnonymousAlice01 and BafflingBob02 have to entrust the bitcoin funds to each other. In case they have a conflict, RoboSats staff will help resolving the dispute.")}
{t("You can find a step-by-step description of the trade pipeline in ")}
<Link href='https://github.com/Reckless-Satoshi/robosats/blob/main/README.md#how-it-works'>{t("How it works")}</Link>.
{" "+t("You can also check the full guide in ")}
<Link href='https://github.com/Reckless-Satoshi/robosats/blob/main/docs/how-to-use.md'>{t("How to use")}</Link>.</p>
</Typography>
<Typography component="h5" variant="h5">{t("What payment methods are accepted?")}</Typography>
<Typography component="div" variant="body2">
<p>{t("All of them as long as they are fast. You can write down your preferred payment method(s). You will have to match with a peer who also accepts that method. The step to exchange fiat has a expiry time of 24 hours before a dispute is automatically open. We highly recommend using instant fiat payment rails.")} </p>
</Typography>
<Typography component="h5" variant="h5">{t("Are there trade limits?")}</Typography>
<Typography component="div" variant="body2">
<p>{t("Maximum single trade size is {{maxAmount}} Satoshis to minimize lightning routing failure. There is no limits to the number of trades per day. A robot can only have one order at a time. However, you can use multiple robots simultaneously in different browsers (remember to back up your robot tokens!).", {maxAmount: '1,200,000'})} </p>
</Typography>
<Typography component="h5" variant="h5">{t("Is RoboSats private?")}</Typography>
<Typography component="div" variant="body2">
<p> {t("RoboSats will never ask you for your name, country or ID. RoboSats does not custody your funds and does not care who you are. RoboSats does not collect or custody any personal data. For best anonymity use Tor Browser and access the .onion hidden service.")} </p>
<p>{t("Your trading peer is the only one who can potentially guess anything about you. Keep your chat short and concise. Avoid providing non-essential information other than strictly necessary for the fiat payment.")} </p>
</Typography>
<Typography component="h5" variant="h5">{t("What are the risks?")}</Typography>
<Typography component="div" variant="body2">
<p> {t("This is an experimental application, things could go wrong. Trade small amounts!")}</p>
<p> {t("The seller faces the same charge-back risk as with any other peer-to-peer service. Paypal or credit cards are not recommended.")}</p>
</Typography>
<Typography component="h5" variant="h5">{t("What is the trust model?")}</Typography>
<Typography component="div" variant="body2">
<p> {t("The buyer and the seller never have to trust each other. Some trust on RoboSats is needed since linking the seller's hold invoice and buyer payment is not atomic (yet). In addition, disputes are solved by the RoboSats staff.")}</p>
<p> {t("To be totally clear. Trust requirements are minimized. However, there is still one way RoboSats could run away with your satoshis: by not releasing the satoshis to the buyer. It could be argued that such move is not in RoboSats' interest as it would damage the reputation for a small payout. However, you should hesitate and only trade small quantities at a time. For large amounts use an onchain escrow service such as Bisq")}</p>
<p> {t("You can build more trust on RoboSats by inspecting the source code.")} <Link href='https://github.com/reckless-satoshi/robosats'> {t("Project source code")}</Link>. </p>
</Typography>
<Typography component="h5" variant="h5">{t("What happens if RoboSats suddenly disappears?")}</Typography>
<Typography component="div" variant="body2">
<p> {t("Your sats will return to you. Any hold invoice that is not settled would be automatically returned even if RoboSats goes down forever. This is true for both, locked bonds and trading escrows. However, there is a small window between the seller confirms FIAT RECEIVED and the moment the buyer receives the satoshis when the funds could be permanently lost if RoboSats disappears. This window is about 1 second long. Make sure to have enough inbound liquidity to avoid routing failures. If you have any problem, reach out trough the RoboSats public channels.")}</p>
</Typography>
<Typography component="h5" variant="h5">{t("Is RoboSats legal in my country?")}</Typography>
<Typography component="div" variant="body2">
<p> {t("In many countries using RoboSats is no different than using Ebay or Craiglist. Your regulation may vary. It is your responsibility to comply.")}</p>
</Typography>
<Typography component="h5" variant="h5">{t("Disclaimer")}</Typography>
<Typography component="div" variant="body2">
<p> {t("This lightning application is provided as is. It is in active development: trade with the utmost caution. There is no private support. Support is only offered via public channels ")}<Link href='https://t.me/robosats'>{t("(Telegram)")}</Link>{t(". RoboSats will never contact you. RoboSats will definitely never ask for your robot token.")}</p>
</Typography>
<DialogActions>
<Button onClick={onClose}>{t("Close")}</Button>
</DialogActions>
</DialogContent>
</Dialog>
)
}
export default InfoDialog;

View File

@ -0,0 +1,48 @@
import React from "react";
import { useTranslation } from "react-i18next";
import {
Dialog,
DialogTitle,
DialogActions,
DialogContent,
DialogContentText,
Button,
} from "@mui/material"
import { Link } from 'react-router-dom'
type Props = {
open: boolean;
onClose: () => void;
}
const NoRobotDialog = ({
open,
onClose,
}: Props): JSX.Element => {
const { t } = useTranslation();
return (
<Dialog
open={open}
onClose={onClose}
>
<DialogTitle>
{t("You do not have a robot avatar")}
</DialogTitle>
<DialogContent>
<DialogContentText>
{t("You need to generate a robot avatar in order to become an order maker")}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={onClose} autoFocus>{t("Go back")}</Button>
<Button onClick={onClose} to="/" component={Link}>{t("Generate Robot")}</Button>
</DialogActions>
</Dialog>
)
}
export default NoRobotDialog;

View File

@ -0,0 +1,80 @@
import React from "react";
import { useTranslation } from "react-i18next";
import {
Dialog,
DialogTitle,
Tooltip,
IconButton,
TextField,
DialogActions,
DialogContent,
DialogContentText,
Button,
Grid,
} from "@mui/material"
import { getCookie } from "../../utils/cookies";
import ContentCopy from "@mui/icons-material/ContentCopy";
type Props = {
open: boolean;
onClose: () => void;
copyIconColor: string;
onClickCopy: () => void;
onClickBack: () => void;
onClickDone: () => void;
}
const StoreTokenDialog = ({
open,
onClose,
copyIconColor,
onClickCopy,
onClickBack,
onClickDone,
}: Props): JSX.Element => {
const { t } = useTranslation();
return (
<Dialog
open={open}
onClose={onClose}
>
<DialogTitle >
{t("Store your robot token")}
</DialogTitle>
<DialogContent>
<DialogContentText>
{t("You might need to recover your robot avatar in the future: store it safely. You can simply copy it into another application.")}
</DialogContentText>
<br/>
<Grid align="center">
<TextField
sx={{width:"100%", maxWidth:"550px"}}
disabled
label={t("Back it up!")}
value={getCookie("robot_token")}
variant='filled'
size='small'
InputProps={{
endAdornment:
<Tooltip disableHoverListener enterTouchDelay={0} title={t("Copied!")}>
<IconButton onClick={onClickCopy}>
<ContentCopy color={copyIconColor}/>
</IconButton>
</Tooltip>,
}}
/>
</Grid>
</DialogContent>
<DialogActions>
<Button onClick={onClickBack} autoFocus>{t("Go back")}</Button>
<Button onClick={onClickDone}>{t("Done")}</Button>
</DialogActions>
</Dialog>
)
}
export default StoreTokenDialog;

View File

@ -0,0 +1,4 @@
export { default as CommunityDialog } from "./CommunityDialog";
export { default as InfoDialog } from "./InfoDialog";
export { default as StoreTokenDialog } from "./StoreTokenDialog";
export { default as NoRobotDialog } from "./NoRobotDialog";

View File

@ -0,0 +1,91 @@
import React from "react";
import Flags from 'country-flag-icons/react/3x2'
import SwapCallsIcon from '@mui/icons-material/SwapCalls';
import GoldIcon from '../icons/GoldIcon';
import EarthIcon from '../icons/EarthIcon'
type Props = {
code: string;
}
const FlagWithProps = ({ code }: Props): JSX.Element => {
const defaultProps = {
width: 20,
height: 20,
};
let flag: JSX.Element | null = null;
if(code === 'AUD') flag = <Flags.AU {...defaultProps}/>;
if(code === 'ARS') flag = <Flags.AR {...defaultProps}/>;
if(code === 'BRL') flag = <Flags.BR {...defaultProps}/>;
if(code === 'CAD') flag = <Flags.CA {...defaultProps}/>;
if(code === 'CHF') flag = <Flags.CH {...defaultProps}/>;
if(code === 'CLP') flag = <Flags.CL {...defaultProps}/>;
if(code === 'CNY') flag = <Flags.CN {...defaultProps}/>;
if(code === 'EUR') flag = <Flags.EU {...defaultProps}/>;
if(code === 'HRK') flag = <Flags.HR {...defaultProps}/>;
if(code === 'CZK') flag = <Flags.CZ {...defaultProps}/>;
if(code === 'DKK') flag = <Flags.DK {...defaultProps}/>;
if(code === 'GBP') flag = <Flags.GB {...defaultProps}/>;
if(code === 'HKD') flag = <Flags.HK {...defaultProps}/>;
if(code === 'HUF') flag = <Flags.HU {...defaultProps}/>;
if(code === 'INR') flag = <Flags.IN {...defaultProps}/>;
if(code === 'ISK') flag = <Flags.IS {...defaultProps}/>;
if(code === 'JPY') flag = <Flags.JP {...defaultProps}/>;
if(code === 'KRW') flag = <Flags.KR {...defaultProps}/>;
if(code === 'MXN') flag = <Flags.MX {...defaultProps}/>;
if(code === 'NOK') flag = <Flags.NO {...defaultProps}/>;
if(code === 'NZD') flag = <Flags.NZ {...defaultProps}/>;
if(code === 'PLN') flag = <Flags.PL {...defaultProps}/>;
if(code === 'RON') flag = <Flags.RO {...defaultProps}/>;
if(code === 'RUB') flag = <Flags.RU {...defaultProps}/>;
if(code === 'SEK') flag = <Flags.SE {...defaultProps}/>;
if(code === 'SGD') flag = <Flags.SG {...defaultProps}/>;
if(code === 'VES') flag = <Flags.VE {...defaultProps}/>;
if(code === 'TRY') flag = <Flags.TR {...defaultProps}/>;
if(code === 'USD') flag = <Flags.US {...defaultProps}/>;
if(code === 'ZAR') flag = <Flags.ZA {...defaultProps}/>;
if(code === 'COP') flag = <Flags.CO {...defaultProps}/>;
if(code === 'PEN') flag = <Flags.PE {...defaultProps}/>;
if(code === 'UYU') flag = <Flags.UY {...defaultProps}/>;
if(code === 'PYG') flag = <Flags.PY {...defaultProps}/>;
if(code === 'BOB') flag = <Flags.BO {...defaultProps}/>;
if(code === 'IDR') flag = <Flags.ID {...defaultProps}/>;
if(code === 'ANG') flag = <Flags.CW {...defaultProps}/>;
if(code === 'CRC') flag = <Flags.CR {...defaultProps}/>;
if(code === 'CUP') flag = <Flags.CU {...defaultProps}/>;
if(code === 'DOP') flag = <Flags.DO {...defaultProps}/>;
if(code === 'GHS') flag = <Flags.GH {...defaultProps}/>;
if(code === 'GTQ') flag = <Flags.GT {...defaultProps}/>;
if(code === 'ILS') flag = <Flags.IL {...defaultProps}/>;
if(code === 'JMD') flag = <Flags.JM {...defaultProps}/>;
if(code === 'KES') flag = <Flags.KE {...defaultProps}/>;
if(code === 'KZT') flag = <Flags.KZ {...defaultProps}/>;
if(code === 'MYR') flag = <Flags.MY {...defaultProps}/>;
if(code === 'NAD') flag = <Flags.NA {...defaultProps}/>;
if(code === 'NGN') flag = <Flags.NG {...defaultProps}/>;
if(code === 'AZN') flag = <Flags.AZ {...defaultProps}/>;
if(code === 'PAB') flag = <Flags.PA {...defaultProps}/>;
if(code === 'PHP') flag = <Flags.PH {...defaultProps}/>;
if(code === 'PKR') flag = <Flags.PK {...defaultProps}/>;
if(code === 'QAR') flag = <Flags.QA {...defaultProps}/>;
if(code === 'SAR') flag = <Flags.SA {...defaultProps}/>;
if(code === 'THB') flag = <Flags.TH {...defaultProps}/>;
if(code === 'TTD') flag = <Flags.TT {...defaultProps}/>;
if(code === 'VND') flag = <Flags.VN {...defaultProps}/>;
if(code === 'XOF') flag = <Flags.BJ {...defaultProps}/>;
if(code === 'TWD') flag = <Flags.TW {...defaultProps}/>;
if(code === 'TZS') flag = <Flags.TZ {...defaultProps}/>;
if(code === 'XAF') flag = <Flags.CM {...defaultProps}/>;
if(code === 'UAH') flag = <Flags.UA {...defaultProps}/>;
if(code === 'ANY') flag = <EarthIcon {...defaultProps}/>;
if(code === 'XAU') flag = <GoldIcon {...defaultProps}/>;
if(code === 'BTC') flag = <SwapCallsIcon color="primary"/>;
return (
<div style={{width:28, height: 20}}>{flag}</div>
);
};
export default FlagWithProps;

View File

@ -0,0 +1 @@
export { default } from "./FlagWithProps";

View File

@ -1,115 +0,0 @@
import React, { Component } from 'react'
import { withTranslation, Trans} from "react-i18next";
import {Typography, Link, DialogActions, DialogContent, Button, Grid} from "@mui/material"
import Image from 'material-ui-image'
import MediaQuery from 'react-responsive'
class InfoDialog extends Component {
render() {
const { t } = this.props;
return (
<div>
<DialogContent>
<MediaQuery minWidth={475}>
<Grid container xs={12}>
<Grid item xs={8}>
<Typography component="h4" variant="h4">{t("What is RoboSats?")}</Typography>
<Typography component="body2" variant="body2">
<p>{t("It is a BTC/FIAT peer-to-peer exchange over lightning.")} <br/>
{t("It simplifies matchmaking and minimizes the need of trust. RoboSats focuses in privacy and speed.")}</p>
<p>{t("RoboSats is an open source project ")} <Link
href='https://github.com/reckless-satoshi/robosats'>{t("(GitHub).")}</Link>
</p>
</Typography>
</Grid>
<Grid item xs={4} align="center">
<Image className='newAvatar'
disableError='true'
cover='true'
color='null'
src={window.location.origin +'/static/assets/images/v0.1.2-04.png'}
/>
</Grid>
</Grid>
</MediaQuery>
<MediaQuery maxWidth={474}>
<Typography component="h4" variant="h4">{t("What is RoboSats?")}</Typography>
<Typography component="body2" variant="body2">
<p>{t("It is a BTC/FIAT peer-to-peer exchange over lightning.")+" "} {t("It simplifies matchmaking and minimizes the need of trust. RoboSats focuses in privacy and speed.")}</p>
<img
width='100%'
src={window.location.origin +'/static/assets/images/v0.1.2-03.png'}
/>
<p>{t("RoboSats is an open source project ")} <Link
href='https://github.com/reckless-satoshi/robosats'>{t("(GitHub).")}</Link>
</p>
</Typography>
</MediaQuery>
<Typography component="h5" variant="h5">{t("How does it work?")}</Typography>
<Typography component="body2" variant="body2">
<p> {t("AnonymousAlice01 wants to sell bitcoin. She posts a sell order. BafflingBob02 wants to buy bitcoin and he takes Alice's order. Both have to post a small bond using lightning to prove they are real robots. Then, Alice posts the trade collateral also using a lightning hold invoice. RoboSats locks the invoice until Alice confirms she received the fiat, then the satoshis are released to Bob. Enjoy your satoshis, Bob!")}</p>
<p>{t("At no point, AnonymousAlice01 and BafflingBob02 have to entrust the bitcoin funds to each other. In case they have a conflict, RoboSats staff will help resolving the dispute.")}
{t("You can find a step-by-step description of the trade pipeline in ")}
<Link href='https://github.com/Reckless-Satoshi/robosats/blob/main/README.md#how-it-works'>{t("How it works")}</Link>.
{" "+t("You can also check the full guide in ")}
<Link href='https://github.com/Reckless-Satoshi/robosats/blob/main/docs/how-to-use.md'>{t("How to use")}</Link>.</p>
</Typography>
<Typography component="h5" variant="h5">{t("What payment methods are accepted?")}</Typography>
<Typography component="body2" variant="body2">
<p>{t("All of them as long as they are fast. You can write down your preferred payment method(s). You will have to match with a peer who also accepts that method. The step to exchange fiat has a expiry time of 24 hours before a dispute is automatically open. We highly recommend using instant fiat payment rails.")} </p>
</Typography>
<Typography component="h5" variant="h5">{t("Are there trade limits?")}</Typography>
<Typography component="body2" variant="body2">
<p>{t("Maximum single trade size is {{maxAmount}} Satoshis to minimize lightning routing failure. There is no limits to the number of trades per day. A robot can only have one order at a time. However, you can use multiple robots simultaneously in different browsers (remember to back up your robot tokens!).", {maxAmount: '1,200,000'})} </p>
</Typography>
<Typography component="h5" variant="h5">{t("Is RoboSats private?")}</Typography>
<Typography component="body2" variant="body2">
<p> {t("RoboSats will never ask you for your name, country or ID. RoboSats does not custody your funds and does not care who you are. RoboSats does not collect or custody any personal data. For best anonymity use Tor Browser and access the .onion hidden service.")} </p>
<p>{t("Your trading peer is the only one who can potentially guess anything about you. Keep your chat short and concise. Avoid providing non-essential information other than strictly necessary for the fiat payment.")} </p>
</Typography>
<Typography component="h5" variant="h5">{t("What are the risks?")}</Typography>
<Typography component="body2" variant="body2">
<p> {t("This is an experimental application, things could go wrong. Trade small amounts!")}</p>
<p> {t("The seller faces the same charge-back risk as with any other peer-to-peer service. Paypal or credit cards are not recommended.")}</p>
</Typography>
<Typography component="h5" variant="h5">{t("What is the trust model?")}</Typography>
<Typography component="body2" variant="body2">
<p> {t("The buyer and the seller never have to trust each other. Some trust on RoboSats is needed since linking the seller's hold invoice and buyer payment is not atomic (yet). In addition, disputes are solved by the RoboSats staff.")}</p>
<p> {t("To be totally clear. Trust requirements are minimized. However, there is still one way RoboSats could run away with your satoshis: by not releasing the satoshis to the buyer. It could be argued that such move is not in RoboSats' interest as it would damage the reputation for a small payout. However, you should hesitate and only trade small quantities at a time. For large amounts use an onchain escrow service such as Bisq")}</p>
<p> {t("You can build more trust on RoboSats by inspecting the source code.")} <Link href='https://github.com/reckless-satoshi/robosats'> {t("Project source code")}</Link>. </p>
</Typography>
<Typography component="h5" variant="h5">{t("What happens if RoboSats suddenly disappears?")}</Typography>
<Typography component="body2" variant="body2">
<p> {t("Your sats will return to you. Any hold invoice that is not settled would be automatically returned even if RoboSats goes down forever. This is true for both, locked bonds and trading escrows. However, there is a small window between the seller confirms FIAT RECEIVED and the moment the buyer receives the satoshis when the funds could be permanently lost if RoboSats disappears. This window is about 1 second long. Make sure to have enough inbound liquidity to avoid routing failures. If you have any problem, reach out trough the RoboSats public channels.")}</p>
</Typography>
<Typography component="h5" variant="h5">{t("Is RoboSats legal in my country?")}</Typography>
<Typography component="body2" variant="body2">
<p> {t("In many countries using RoboSats is no different than using Ebay or Craiglist. Your regulation may vary. It is your responsibility to comply.")}</p>
</Typography>
<Typography component="h5" variant="h5">{t("Disclaimer")}</Typography>
<Typography component="body2" variant="body2">
<p> {t("This lightning application is provided as is. It is in active development: trade with the utmost caution. There is no private support. Support is only offered via public channels ")}<Link href='https://t.me/robosats'>{t("(Telegram)")}</Link>{t(". RoboSats will never contact you. RoboSats will definitely never ask for your robot token.")}</p>
</Typography>
<DialogActions>
<Button onClick={this.props.handleCloseInfo}>{t("Close")}</Button>
</DialogActions>
</DialogContent>
</div>
)
}
}
export default withTranslation()(InfoDialog);

View File

@ -0,0 +1,26 @@
import React, { useState, useEffect } from "react";
import { Box, LinearProgress } from "@mui/material"
import { calcTimeDelta } from 'react-countdown';
export default function LinearDeterminate(props) {
const [progress, setProgress] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setProgress((oldProgress) => {
var left = calcTimeDelta( new Date(props.expires_at)).total /1000;
return (left / props.total_secs_exp) * 100;
});
}, 1000);
return () => {
clearInterval(timer);
};
}, []);
return (
<Box sx={{ width: '100%' }}>
<LinearProgress variant="determinate" value={progress} />
</Box>
);
}

View File

@ -1,12 +1,13 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { withTranslation } from "react-i18next"; import { withTranslation } from "react-i18next";
import { InputAdornment, LinearProgress, Dialog, IconButton, DialogActions, DialogContent, DialogContentText, DialogTitle, Accordion, AccordionDetails, AccordionSummary, Checkbox, Slider, Box, Tab, Tabs, SliderThumb, Tooltip, Paper, Button , Grid, Typography, TextField, Select, FormHelperText, MenuItem, FormControl, Radio, FormControlLabel, RadioGroup} from "@mui/material" import { InputAdornment, LinearProgress, Dialog, IconButton, DialogActions, DialogContent, DialogContentText, DialogTitle, Accordion, AccordionDetails, AccordionSummary, Checkbox, Slider, Box, Tab, Tabs, SliderThumb, Tooltip, Paper, Button , Grid, Typography, TextField, Select, FormHelperText, MenuItem, FormControl, Radio, FormControlLabel, RadioGroup} from "@mui/material"
import RangeSlider from "./RangeSlider";
import { LocalizationProvider, TimePicker} from '@mui/lab'; import { LocalizationProvider, TimePicker} from '@mui/lab';
import DateFnsUtils from "@date-io/date-fns"; import DateFnsUtils from "@date-io/date-fns";
import { Link as LinkRouter } from 'react-router-dom' import { Link as LinkRouter } from 'react-router-dom'
import { styled } from '@mui/material/styles'; import { StoreTokenDialog, NoRobotDialog } from "./Dialogs";
import getFlags from './getFlags'; import FlagWithProps from './FlagWithProps';
import AutocompletePayments from './AutocompletePayments'; import AutocompletePayments from './AutocompletePayments';
import currencyDict from '../../static/assets/currencies.json'; import currencyDict from '../../static/assets/currencies.json';
@ -18,7 +19,6 @@ import BuySatsIcon from "./icons/BuySatsIcon";
import BuySatsCheckedIcon from "./icons/BuySatsCheckedIcon"; import BuySatsCheckedIcon from "./icons/BuySatsCheckedIcon";
import SellSatsIcon from "./icons/SellSatsIcon"; import SellSatsIcon from "./icons/SellSatsIcon";
import SellSatsCheckedIcon from "./icons/SellSatsCheckedIcon"; import SellSatsCheckedIcon from "./icons/SellSatsCheckedIcon";
import ContentCopy from "@mui/icons-material/ContentCopy";
import { getCookie } from "../utils/cookies"; import { getCookie } from "../utils/cookies";
import { pn } from "../utils/prettyNumbers"; import { pn } from "../utils/prettyNumbers";
@ -41,25 +41,31 @@ class MakerPage extends Component {
minTradeSats: this.defaultMinTradeSats, minTradeSats: this.defaultMinTradeSats,
maxTradeSats: this.defaultMaxTradeSats, maxTradeSats: this.defaultMaxTradeSats,
maxBondlessSats: this.defaultMaxBondlessSats, maxBondlessSats: this.defaultMaxBondlessSats,
tabValue: 0,
openStoreToken: false,
is_explicit: false, is_explicit: false,
type: null, type: null,
currency: this.defaultCurrency, currency: this.defaultCurrency,
currencyCode: this.defaultCurrencyCode, currencyCode: this.defaultCurrencyCode,
payment_method: this.defaultPaymentMethod, payment_method: this.defaultPaymentMethod,
premium: 0, premium: 0,
satoshis: null, satoshis: "",
showAdvanced: false, showAdvanced: false,
allowBondless: false, allowBondless: false,
publicExpiryTime: new Date(0, 0, 0, 23, 59), publicExpiryTime: new Date(0, 0, 0, 23, 59),
escrowExpiryTime: new Date(0, 0, 0, 3, 0), escrowExpiryTime: new Date(0, 0, 0, 3, 0),
enableAmountRange: false, enableAmountRange: false,
minAmount: null,
bondSize: 1, bondSize: 1,
limits: null, limits: null,
minAmount: null, minAmount: "",
maxAmount: null, maxAmount: "",
loadingLimits: true, loadingLimits: true,
amount: "",
badPaymentMethod: "",
}
} }
componentDidMount() {
this.getLimits() this.getLimits()
} }
@ -268,7 +274,7 @@ class MakerPage extends Component {
const { t } = this.props; const { t } = this.props;
return( return(
<Paper elevation={12} style={{ padding: 8, width:'260px', align:'center'}}> <Paper elevation={12} style={{ padding: 8, width:'260px', align:'center'}}>
<Grid item xs={12} align="center" spacing={1}> <Grid item xs={12} align="center">
<div style={{position:'relative', left:'5px'}}> <div style={{position:'relative', left:'5px'}}>
<FormControl component="fieldset"> <FormControl component="fieldset">
<FormHelperText sx={{textAlign:"center"}}> <FormHelperText sx={{textAlign:"center"}}>
@ -277,13 +283,13 @@ class MakerPage extends Component {
<RadioGroup row value={this.state.type} onChange={this.handleTypeChange}> <RadioGroup row value={this.state.type} onChange={this.handleTypeChange}>
<FormControlLabel <FormControlLabel
value="0" value={0}
control={<Radio icon={<BuySatsIcon sx={{width:"30px",height:"30px"}} color="text.secondary"/>} checkedIcon={<BuySatsCheckedIcon sx={{width:"30px",height:"30px"}} color="primary"/>}/>} control={<Radio icon={<BuySatsIcon sx={{width:"30px",height:"30px"}} color="text.secondary"/>} checkedIcon={<BuySatsCheckedIcon sx={{width:"30px",height:"30px"}} color="primary"/>}/>}
label={this.state.type == 0 ? <Typography color="primary"><b>{t("Buy")}</b></Typography>: <Typography color="text.secondary">{t("Buy")}</Typography>} label={this.state.type == 0 ? <Typography color="primary"><b>{t("Buy")}</b></Typography>: <Typography color="text.secondary">{t("Buy")}</Typography>}
labelPlacement="end" labelPlacement="end"
/> />
<FormControlLabel <FormControlLabel
value="1" value={1}
control={<Radio color="secondary" icon={<SellSatsIcon sx={{width:"30px",height:"30px"}} color="text.secondary"/>} checkedIcon={<SellSatsCheckedIcon sx={{width:"30px",height:"30px"}} color="secondary"/>}/>} control={<Radio color="secondary" icon={<SellSatsIcon sx={{width:"30px",height:"30px"}} color="text.secondary"/>} checkedIcon={<SellSatsCheckedIcon sx={{width:"30px",height:"30px"}} color="secondary"/>}/>}
label={this.state.type == 1 ? <Typography color="secondary"><b>{t("Sell")}</b></Typography>: <Typography color="text.secondary">{t("Sell")}</Typography>} label={this.state.type == 1 ? <Typography color="secondary"><b>{t("Sell")}</b></Typography>: <Typography color="text.secondary">{t("Sell")}</Typography>}
labelPlacement="end" labelPlacement="end"
@ -293,18 +299,18 @@ class MakerPage extends Component {
</div> </div>
</Grid> </Grid>
<Grid containter xs={12} alignItems="stretch" style={{ display: "flex" }}> <Grid alignItems="stretch" style={{ display: "flex" }}>
<div style={{maxWidth:150}}> <div style={{maxWidth:150}}>
<Tooltip placement="top" enterTouchDelay="500" enterDelay="700" enterNextDelay="2000" title={t("Amount of fiat to exchange for bitcoin")}> <Tooltip placement="top" enterTouchDelay={500} enterDelay={700} enterNextDelay={2000} title={t("Amount of fiat to exchange for bitcoin")}>
<TextField <TextField
disabled = {this.state.enableAmountRange} disabled = {this.state.enableAmountRange}
variant = {this.state.enableAmountRange ? 'filled' : 'outlined'} variant = {this.state.enableAmountRange ? 'filled' : 'outlined'}
error={(this.state.amount <= this.getMinAmount() || this.state.amount >= this.getMaxAmount()) & this.state.amount != "" } error={(this.state.amount <= this.getMinAmount() || this.state.amount >= this.getMaxAmount()) & this.state.amount != "" ? true : false}
helperText={this.state.amount <= this.getMinAmount() & this.state.amount != "" ? t("Too low") helperText={this.state.amount <= this.getMinAmount() & this.state.amount != "" ? t("Too low")
: (this.state.amount >= this.getMaxAmount() & this.state.amount != "" ? t("Too high") : null)} : (this.state.amount >= this.getMaxAmount() & this.state.amount != "" ? t("Too high") : null)}
label={t("Amount")} label={t("Amount")}
type="number" type="number"
required="true" required={true}
value={this.state.amount} value={this.state.amount}
inputProps={{ inputProps={{
min:0 , min:0 ,
@ -317,43 +323,40 @@ class MakerPage extends Component {
<div > <div >
<Select <Select
sx={{width:'120px'}} sx={{width:'120px'}}
required="true" required={true}
defaultValue={this.defaultCurrency} defaultValue={this.defaultCurrency}
inputProps={{ inputProps={{
style: {textAlign:"center"} style: {textAlign:"center"}
}} }}
onChange={this.handleCurrencyChange}> onChange={this.handleCurrencyChange}>
{Object.entries(currencyDict) {Object.entries(currencyDict)
.map( ([key, value]) => <MenuItem value={parseInt(key)}> .map( ([key, value]) => <MenuItem key={key} value={parseInt(key)}>
<div style={{display:'flex',alignItems:'center', flexWrap:'wrap'}}>{getFlags(value)}{" "+value}</div> <div style={{display:'flex',alignItems:'center', flexWrap:'wrap'}}><FlagWithProps code={value}/>{" "+value}</div>
</MenuItem> )} </MenuItem> )}
</Select> </Select>
</div> </div>
</Grid> </Grid>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Tooltip placement="top" enterTouchDelay="300" enterDelay="700" enterNextDelay="2000" title={t("Enter your preferred fiat payment methods. Fast methods are highly recommended.")}>
<AutocompletePayments <AutocompletePayments
onAutocompleteChange={this.handlePaymentMethodChange} onAutocompleteChange={this.handlePaymentMethodChange}
optionsType={this.state.currency==1000 ? "swap":"fiat"} optionsType={this.state.currency==1000 ? "swap":"fiat"}
error={this.state.badPaymentMethod} error={this.state.badPaymentMethod}
helperText={this.state.badPaymentMethod ? t("Must be shorter than 65 characters"):""} helperText={this.state.badPaymentMethod ? t("Must be shorter than 65 characters"):""}
label={this.state.currency==1000 ? t("Swap Destination(s)") : t("Fiat Payment Method(s)")} label={this.state.currency==1000 ? t("Swap Destination(s)") : t("Fiat Payment Method(s)")}
tooltipTitle={t("Enter your preferred fiat payment methods. Fast methods are highly recommended.")}
listHeaderText={t("You can add new methods")} listHeaderText={t("You can add new methods")}
addNewButtonText={t("Add New")} addNewButtonText={t("Add New")}
/> />
</Tooltip>
</Grid> </Grid>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<FormControl component="fieldset"> <FormControl component="fieldset">
<FormHelperText > <FormHelperText sx={{textAlign:"center"}}>
<div align='center'> {t("Choose a Pricing Method")}
{t("Choose a Pricing Method")}
</div>
</FormHelperText> </FormHelperText>
<RadioGroup row defaultValue="relative"> <RadioGroup row defaultValue="relative">
<Tooltip placement="top" enterTouchDelay="0" enterDelay="1000" enterNextDelay="2000" title={t("Let the price move with the market")}> <Tooltip placement="top" enterTouchDelay={0} enterDelay={1000} enterNextDelay={2000} title={t("Let the price move with the market")}>
<FormControlLabel <FormControlLabel
value="relative" value="relative"
control={<Radio color="primary"/>} control={<Radio color="primary"/>}
@ -362,7 +365,7 @@ class MakerPage extends Component {
onClick={this.handleClickRelative} onClick={this.handleClickRelative}
/> />
</Tooltip> </Tooltip>
<Tooltip placement="top" enterTouchDelay="0" enterDelay="1000" enterNextDelay="2000" title={t("Set a fix amount of satoshis")}> <Tooltip placement="top" enterTouchDelay={0} enterDelay={1000} enterNextDelay={2000} title={t("Set a fix amount of satoshis")}>
<FormControlLabel <FormControlLabel
disabled={this.state.enableAmountRange} disabled={this.state.enableAmountRange}
value="explicit" value="explicit"
@ -384,7 +387,7 @@ class MakerPage extends Component {
error={this.state.badSatoshis} error={this.state.badSatoshis}
helperText={this.state.badSatoshis} helperText={this.state.badSatoshis}
type="number" type="number"
required="true" required={true}
value={this.state.satoshis} value={this.state.satoshis}
inputProps={{ inputProps={{
min:this.state.minTradeSats , min:this.state.minTradeSats ,
@ -410,7 +413,7 @@ class MakerPage extends Component {
/> />
</div> </div>
<Grid item> <Grid item>
<Tooltip placement="top" enterTouchDelay="0" enterDelay="1000" enterNextDelay="2000" title={this.state.is_explicit? t("Your order fixed exchange rate"): t("Your order's current exchange rate. Rate will move with the market.")}> <Tooltip placement="top" enterTouchDelay={0} enterDelay={1000} enterNextDelay={2000} title={this.state.is_explicit? t("Your order fixed exchange rate"): t("Your order's current exchange rate. Rate will move with the market.")}>
<Typography variant="caption" color="text.secondary"> <Typography variant="caption" color="text.secondary">
{(this.state.is_explicit ? t("Order rate:"): t("Order current rate:"))+" "+pn(this.priceNow())+" "+this.state.currencyCode+"/BTC"} {(this.state.is_explicit ? t("Order rate:"): t("Order current rate:"))+" "+pn(this.priceNow())+" "+this.state.currencyCode+"/BTC"}
</Typography> </Typography>
@ -467,36 +470,6 @@ class MakerPage extends Component {
return parseFloat(Number(min_amount*1.1).toPrecision(2)) return parseFloat(Number(min_amount*1.1).toPrecision(2))
} }
RangeSlider = styled(Slider)(({ theme }) => ({
color: 'primary',
height: 3,
padding: '13px 0',
'& .MuiSlider-thumb': {
height: 27,
width: 27,
backgroundColor: '#fff',
border: '1px solid currentColor',
'&:hover': {
boxShadow: '0 0 0 8px rgba(58, 133, 137, 0.16)',
},
'& .range-bar': {
height: 9,
width: 1,
backgroundColor: 'currentColor',
marginLeft: 1,
marginRight: 1,
},
},
'& .MuiSlider-track': {
height: 3,
},
'& .MuiSlider-rail': {
color: theme.palette.mode === 'dark' ? '#bfbfbf' : '#d8d8d8',
opacity: theme.palette.mode === 'dark' ? undefined : 1,
height: 3,
},
}));
RangeThumbComponent(props) { RangeThumbComponent(props) {
const { children, ...other } = props; const { children, ...other } = props;
return ( return (
@ -551,52 +524,50 @@ class MakerPage extends Component {
return( return(
<Paper elevation={12} style={{ padding: 8, width:'280px', align:'center'}}> <Paper elevation={12} style={{ padding: 8, width:'280px', align:'center'}}>
<Grid container xs={12} spacing={1}> <Grid container spacing={1}>
<Grid item xs={12} align="center" spacing={1}> <Grid item xs={12} align="center">
<FormControl align="center"> <FormControl align="center">
<FormHelperText> <Tooltip enterTouchDelay={0} placement="top" align="center" title={t("Let the taker chose an amount within the range")}>
<Tooltip enterTouchDelay="0" placement="top" align="center" title={t("Let the taker chose an amount within the range")}> <FormHelperText align="center" style={{display:'flex',alignItems:'center', flexWrap:'wrap'}}>
<div align="center" style={{display:'flex',alignItems:'center', flexWrap:'wrap'}}> <Checkbox onChange={(e)=>this.setState({enableAmountRange:e.target.checked, is_explicit: false})}/>
<Checkbox onChange={(e)=>this.setState({enableAmountRange:e.target.checked, is_explicit: false})}/> {this.state.enableAmountRange & this.state.minAmount != null? this.rangeText() : t("Enable Amount Range")}
{this.state.enableAmountRange & this.state.minAmount != null? <this.rangeText/> : t("Enable Amount Range")}
</div>
</Tooltip>
</FormHelperText> </FormHelperText>
<div style={{ display: this.state.loadingLimits == true ? '':'none'}}> </Tooltip>
<LinearProgress /> <div style={{ display: this.state.loadingLimits == true ? '':'none'}}>
</div> <LinearProgress />
<div style={{ display: this.state.loadingLimits == false ? '':'none'}}> </div>
<this.RangeSlider <div style={{ display: this.state.loadingLimits == false ? '':'none'}}>
disableSwap={true} <RangeSlider
sx={{width:200, align:"center"}} disableSwap={true}
disabled={!this.state.enableAmountRange || this.state.loadingLimits} sx={{width:200, align:"center"}}
value={[this.state.minAmount, this.state.maxAmount]} disabled={!this.state.enableAmountRange || this.state.loadingLimits}
step={(this.getMaxAmount()-this.getMinAmount())/5000} value={[Number(this.state.minAmount), Number(this.state.maxAmount)]}
valueLabelDisplay="auto" step={(this.getMaxAmount()-this.getMinAmount())/5000}
components={{ Thumb: this.RangeThumbComponent }} valueLabelDisplay="auto"
valueLabelFormat={(x) => (parseFloat(Number(x).toPrecision(x < 100 ? 2 : 3))+" "+this.state.currencyCode)} components={{ Thumb: this.RangeThumbComponent }}
marks={this.state.limits == null? valueLabelFormat={(x) => (parseFloat(Number(x).toPrecision(x < 100 ? 2 : 3))+" "+this.state.currencyCode)}
null marks={this.state.limits == null?
: null
[{value: this.getMinAmount(),label: this.getMinAmount()+" "+ this.state.currencyCode}, :
{value: this.getMaxAmount(),label: this.getMaxAmount()+" "+this.state.currencyCode}]} [{value: this.getMinAmount(),label: this.getMinAmount()+" "+ this.state.currencyCode},
min={this.getMinAmount()} {value: this.getMaxAmount(),label: this.getMaxAmount()+" "+this.state.currencyCode}]}
max={this.getMaxAmount()} min={this.getMinAmount()}
onChange={this.handleRangeAmountChange} max={this.getMaxAmount()}
/> onChange={this.handleRangeAmountChange}
</div> />
</div>
</FormControl> </FormControl>
</Grid> </Grid>
<Grid item xs={12} align="center" spacing={1}> <Grid item xs={12} align="center">
<Accordion elevation={0} sx={{width:'280px', position:'relative', left:'-12px'}}> <Accordion elevation={0} sx={{width:'280px', position:'relative', left:'-8px'}}>
<AccordionSummary expandIcon={<ExpandMoreIcon color="primary"/>}> <AccordionSummary expandIcon={<ExpandMoreIcon color="primary"/>}>
<Typography sx={{flexGrow: 1, textAlign: "center"}} color="text.secondary">{t("Expiry Timers")}</Typography> <Typography sx={{flexGrow: 1, textAlign: "center"}} color="text.secondary">{t("Expiry Timers")}</Typography>
</AccordionSummary> </AccordionSummary>
<AccordionDetails> <AccordionDetails>
<Grid container xs={12} spacing={1}> <Grid container spacing={1}>
<Grid item xs={12} align="center" spacing={1}> <Grid item xs={12} align="center">
<LocalizationProvider dateAdapter={DateFnsUtils}> <LocalizationProvider dateAdapter={DateFnsUtils}>
<TimePicker <TimePicker
sx={{width:210, align:"center"}} sx={{width:210, align:"center"}}
@ -625,7 +596,7 @@ class MakerPage extends Component {
</LocalizationProvider> </LocalizationProvider>
</Grid> </Grid>
<Grid item xs={12} align="center" spacing={1}> <Grid item xs={12} align="center">
<LocalizationProvider dateAdapter={DateFnsUtils}> <LocalizationProvider dateAdapter={DateFnsUtils}>
<TimePicker <TimePicker
sx={{width:210, align:"center"}} sx={{width:210, align:"center"}}
@ -658,15 +629,12 @@ class MakerPage extends Component {
</Accordion> </Accordion>
</Grid> </Grid>
<Grid item xs={12} align="center">
<Grid item xs={12} align="center" spacing={1}>
<FormControl align="center"> <FormControl align="center">
<Tooltip enterDelay="800" enterTouchDelay="0" placement="top" title={t("Set the skin-in-the-game, increase for higher safety assurance")}> <Tooltip enterDelay={800} enterTouchDelay={0} placement="top" title={t("Set the skin-in-the-game, increase for higher safety assurance")}>
<FormHelperText> <FormHelperText align="center" sx={{display:'flex',flexWrap:'wrap', transform: 'translate(20%, 0)'}}>
<div align="center" style={{display:'flex',flexWrap:'wrap', transform: 'translate(20%, 0)'}}> {t("Fidelity Bond Size")} <LockIcon sx={{height:20,width:20}}/>
{t("Fidelity Bond Size")} <LockIcon sx={{height:20,width:20}}/> </FormHelperText>
</div>
</FormHelperText>
</Tooltip> </Tooltip>
<Slider <Slider
sx={{width:220, align:"center"}} sx={{width:220, align:"center"}}
@ -684,14 +652,14 @@ class MakerPage extends Component {
</FormControl> </FormControl>
</Grid> </Grid>
<Grid item xs={12} align="center" spacing={1}> <Grid item xs={12} align="center">
<Tooltip enterTouchDelay="0" title={t("COMING SOON - High risk! Limited to {{limitSats}}K Sats",{ limitSats: this.state.maxBondlessSats/1000})}> <Tooltip enterTouchDelay={0} title={t("COMING SOON - High risk! Limited to {{limitSats}}K Sats",{ limitSats: this.state.maxBondlessSats/1000})}>
<FormControlLabel <FormControlLabel
label={t("Allow bondless takers")} label={t("Allow bondless takers")}
control={ control={
<Checkbox <Checkbox
disabled disabled
//disabled={this.state.type==0} //disabled={this.state.type==0 || this.state.type === null}
color="secondary" color="secondary"
checked={this.state.allowBondless} checked={this.state.allowBondless}
onChange={()=> this.setState({allowBondless: !this.state.allowBondless})} onChange={()=> this.setState({allowBondless: !this.state.allowBondless})}
@ -705,114 +673,51 @@ class MakerPage extends Component {
) )
} }
StoreTokenDialog = () =>{
const { t } = this.props;
// If there is a robot cookie, prompt user to store it
// Else, prompt user to generate a robot
if (getCookie("robot_token")){
return(
<Dialog
open={this.state.openStoreToken}
onClose={() => this.setState({openStoreToken:false})}
>
<DialogTitle >
{t("Store your robot token")}
</DialogTitle>
<DialogContent>
<DialogContentText>
{t("You might need to recover your robot avatar in the future: store it safely. You can simply copy it into another application.")}
</DialogContentText>
<br/>
<Grid align="center">
<TextField
sx={{width:"100%", maxWidth:"550px"}}
disabled
label={t("Back it up!")}
value={getCookie("robot_token") }
variant='filled'
size='small'
InputProps={{
endAdornment:
<Tooltip disableHoverListener enterTouchDelay="0" title={t("Copied!")}>
<IconButton onClick= {()=> (navigator.clipboard.writeText(getCookie("robot_token")) & this.props.setAppState({copiedToken:true}))}>
<ContentCopy color={this.props.copiedToken ? "inherit" : "primary"}/>
</IconButton>
</Tooltip>,
}}
/>
</Grid>
</DialogContent>
<DialogActions>
<Button onClick={() => this.setState({openStoreToken:false})} autoFocus>{t("Go back")}</Button>
<Button onClick={this.handleCreateOfferButtonPressed}>{t("Done")}</Button>
</DialogActions>
</Dialog>
)
}else{
return(
<Dialog
open={this.state.openStoreToken}
onClose={() => this.setState({openStoreToken:false})}
>
<DialogTitle>
{t("You do not have a robot avatar")}
</DialogTitle>
<DialogContent>
<DialogContentText>
{t("You need to generate a robot avatar in order to become an order maker")}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={() => this.setState({openStoreToken:false})} autoFocus>{t("Go back")}</Button>
<Button onClick={() => this.setState({openStoreToken:false})} to="/" component={LinkRouter}>{t("Generate Robot")}</Button>
</DialogActions>
</Dialog>
)
}
}
makeOrderBox=()=>{ makeOrderBox=()=>{
const [value, setValue] = React.useState(this.state.showAdvanced);
const { t } = this.props; const { t } = this.props;
const handleChange = (event, newValue) => {
this.setState({showAdvanced:newValue})
setValue(newValue);
};
return( return(
<Box sx={{width: this.state.showAdvanced? '270px':'252px'}}> <Box sx={{width: this.state.tabValue==1? '270px':'252px'}}>
<Box sx={{ borderBottom: 1, borderColor: 'divider', position:'relative',left:'5px'}}> <Box sx={{ borderBottom: 1, borderColor: 'divider', position:'relative',left:'5px'}}>
<Tabs value={value? value:0} onChange={handleChange} variant="fullWidth" > <Tabs value={this.state.tabValue} variant="fullWidth" >
<Tab label={t("Order")} {...this.a11yProps(0)} /> <Tab label={t("Order")} {...this.a11yProps(0)} onClick={() => this.setState({tabValue:0})}/>
<Tab label={t("Customize")} {...this.a11yProps(1)} /> <Tab label={t("Customize")} {...this.a11yProps(1)} onClick={() => this.setState({tabValue:1})}/>
</Tabs> </Tabs>
</Box> </Box>
<Grid item xs={12} align="center">
<Grid item xs={12} align="center" spacing={1}> <div style={{ display: this.state.tabValue == 0 ? '':'none'}}>
<div style={{ display: this.state.showAdvanced == false ? '':'none'}}> {this.StandardMakerOptions()}
<this.StandardMakerOptions/>
</div> </div>
<div style={{ display: this.state.showAdvanced == true ? '':'none'}}> <div style={{ display: this.state.tabValue == 1 ? '':'none'}}>
<this.AdvancedMakerOptions/> {this.AdvancedMakerOptions()}
</div> </div>
</Grid> </Grid>
</Box> </Box>
) )
} }
render() { render() {
const { t } = this.props; const { t } = this.props;
return ( return (
<Grid container xs={12} align="center" spacing={1} sx={{minWidth:380}}> <Grid container align="center" spacing={1} sx={{minWidth:380}}>
{/* <Grid item xs={12} align="center" sx={{minWidth:380}}> {getCookie("robot_token") ?
<Typography component="h4" variant="h4"> <StoreTokenDialog
ORDER MAKER open={this.state.openStoreToken}
</Typography> onClose={() => this.setState({openStoreToken:false})}
</Grid> */} onClickCopy={()=> (navigator.clipboard.writeText(getCookie("robot_token")) & this.props.setAppState({copiedToken:true}))}
<this.StoreTokenDialog/> copyIconColor={this.props.copiedToken ? "inherit" : "primary"}
onClickBack={() => this.setState({openStoreToken:false})}
onClickDone={this.handleCreateOfferButtonPressed}
/>
:
<NoRobotDialog
open={this.state.openStoreToken}
onClose={() => this.setState({openStoreToken:false})}
/>
}
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<this.makeOrderBox/> {this.makeOrderBox()}
</Grid> </Grid>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
{/* conditions to disable the make button */} {/* conditions to disable the make button */}
@ -823,7 +728,7 @@ class MakerPage extends Component {
(this.state.is_explicit & (this.state.badSatoshis != null || this.state.satoshis == null)) || (this.state.is_explicit & (this.state.badSatoshis != null || this.state.satoshis == null)) ||
(!this.state.is_explicit & this.state.badPremium != null)) (!this.state.is_explicit & this.state.badPremium != null))
? ?
<Tooltip enterTouchDelay="0" title={t("You must fill the order correctly")}> <Tooltip enterTouchDelay={0} title={t("You must fill the order correctly")}>
<div><Button disabled color="primary" variant="contained">{t("Create Order")}</Button></div> <div><Button disabled color="primary" variant="contained">{t("Create Order")}</Button></div>
</Tooltip> </Tooltip>
: :
@ -838,11 +743,11 @@ class MakerPage extends Component {
</Grid> </Grid>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
{this.state.badRequest ? {this.state.badRequest ?
<Typography component="subtitle2" variant="subtitle2" color="secondary"> <Typography component="h2" variant="subtitle2" color="secondary">
{this.state.badRequest} <br/> {this.state.badRequest} <br/>
</Typography> </Typography>
: ""} : ""}
<Typography component="subtitle2" variant="subtitle2"> <Typography component="h2" variant="subtitle2">
<div align='center'> <div align='center'>
{this.state.type==null ? {this.state.type==null ?
t("Create an order for ") t("Create an order for ")

View File

@ -1,14 +1,15 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { withTranslation} from "react-i18next"; import { withTranslation} from "react-i18next";
import {TextField,Chip, Tooltip, IconButton, Badge, Tab, Tabs, Alert, Paper, CircularProgress, Button , Grid, Typography, List, ListItem, ListItemIcon, ListItemText, ListItemAvatar, Avatar, Divider, Box, LinearProgress, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle} from "@mui/material" import {TextField,Chip, Tooltip, IconButton, Badge, Tab, Tabs, Alert, Paper, CircularProgress, Button , Grid, Typography, List, ListItem, ListItemIcon, ListItemText, ListItemAvatar, Avatar, Divider, Box, LinearProgress, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle} from "@mui/material"
import Countdown, { zeroPad, calcTimeDelta } from 'react-countdown'; import Countdown, { zeroPad } from 'react-countdown';
import MediaQuery from 'react-responsive' import { StoreTokenDialog, NoRobotDialog } from "./Dialogs";
import currencyDict from '../../static/assets/currencies.json';
import { Link as LinkRouter } from 'react-router-dom'
import currencyDict from '../../static/assets/currencies.json';
import PaymentText from './PaymentText' import PaymentText from './PaymentText'
import TradeBox from "./TradeBox"; import TradeBox from "./TradeBox";
import getFlags from './getFlags' import FlagWithProps from './FlagWithProps'
import LinearDeterminate from './LinearDeterminate';
import MediaQuery from 'react-responsive'
import { t } from "i18next"; import { t } from "i18next";
// icons // icons
@ -19,7 +20,6 @@ import PaymentsIcon from '@mui/icons-material/Payments';
import ArticleIcon from '@mui/icons-material/Article'; import ArticleIcon from '@mui/icons-material/Article';
import SendReceiveIcon from "./icons/SendReceiveIcon"; import SendReceiveIcon from "./icons/SendReceiveIcon";
import HourglassTopIcon from '@mui/icons-material/HourglassTop'; import HourglassTopIcon from '@mui/icons-material/HourglassTop';
import ContentCopy from "@mui/icons-material/ContentCopy";
import { getCookie } from "../utils/cookies"; import { getCookie } from "../utils/cookies";
import { pn } from "../utils/prettyNumbers"; import { pn } from "../utils/prettyNumbers";
@ -35,10 +35,10 @@ class OrderPage extends Component {
openCancel: false, openCancel: false,
openCollaborativeCancel: false, openCollaborativeCancel: false,
openInactiveMaker: false, openInactiveMaker: false,
showContractBox: 1, openStoreToken: false,
tabValue: 1,
orderId: this.props.match.params.orderId, orderId: this.props.match.params.orderId,
}; };
this.getOrderDetails(this.props.match.params.orderId);
// Refresh delays according to Order status // Refresh delays according to Order status
this.statusToDelay = { this.statusToDelay = {
@ -88,7 +88,6 @@ class OrderPage extends Component {
} }
getOrderDetails =(id)=> { getOrderDetails =(id)=> {
this.setState(null)
this.setState({orderId:id}) this.setState({orderId:id})
fetch('/api/order' + '?order_id=' + id) fetch('/api/order' + '?order_id=' + id)
.then((response) => response.json()) .then((response) => response.json())
@ -97,8 +96,10 @@ class OrderPage extends Component {
// These are used to refresh the data // These are used to refresh the data
componentDidMount() { componentDidMount() {
this.getOrderDetails(this.props.match.params.orderId);
this.interval = setInterval(this.tick, this.state.delay); this.interval = setInterval(this.tick, this.state.delay);
} }
componentDidUpdate() { componentDidUpdate() {
clearInterval(this.interval); clearInterval(this.interval);
this.interval = setInterval(this.tick, this.state.delay); this.interval = setInterval(this.tick, this.state.delay);
@ -178,11 +179,11 @@ class OrderPage extends Component {
const { t } = this.props; const { t } = this.props;
if(this.state.has_range){ if(this.state.has_range){
return( return(
<Grid container xs={12} align="center" alignItems="stretch" justifyContent="center" style={{ display: "flex"}}> <Grid container align="center" alignItems="stretch" justifyContent="center" style={{ display: "flex"}}>
<this.InactiveMakerDialog/> {this.InactiveMakerDialog()}
<this.StoreTokenDialog/> {this.tokenDialog()}
<div style={{maxWidth:120}}> <div style={{maxWidth:120}}>
<Tooltip placement="top" enterTouchDelay="500" enterDelay="700" enterNextDelay="2000" title={t("Enter amount of fiat to exchange for bitcoin")}> <Tooltip placement="top" enterTouchDelay={500} enterDelay={700} enterNextDelay={2000} title={t("Enter amount of fiat to exchange for bitcoin")}>
<Paper elevation={5} sx={{maxHeight:40}}> <Paper elevation={5} sx={{maxHeight:40}}>
<TextField <TextField
error={(this.state.takeAmount < this.state.min_amount || this.state.takeAmount > this.state.max_amount) & this.state.takeAmount != "" } error={(this.state.takeAmount < this.state.min_amount || this.state.takeAmount > this.state.max_amount) & this.state.takeAmount != "" }
@ -190,7 +191,7 @@ class OrderPage extends Component {
label={t("Amount {{currencyCode}}", {currencyCode: this.state.currencyCode})} label={t("Amount {{currencyCode}}", {currencyCode: this.state.currencyCode})}
size="small" size="small"
type="number" type="number"
required="true" required={true}
value={this.state.takeAmount} value={this.state.takeAmount}
inputProps={{ inputProps={{
min:this.state.min_amount , min:this.state.min_amount ,
@ -203,7 +204,7 @@ class OrderPage extends Component {
</Tooltip> </Tooltip>
</div> </div>
<div style={{height:38, top:'1px', position:'relative', display: (this.state.takeAmount < this.state.min_amount || this.state.takeAmount > this.state.max_amount || this.state.takeAmount == "" || this.state.takeAmount == null) ? '':'none'}}> <div style={{height:38, top:'1px', position:'relative', display: (this.state.takeAmount < this.state.min_amount || this.state.takeAmount > this.state.max_amount || this.state.takeAmount == "" || this.state.takeAmount == null) ? '':'none'}}>
<Tooltip placement="top" enterTouchDelay="0" enterDelay="500" enterNextDelay="1200" title={t("You must specify an amount first")}> <Tooltip placement="top" enterTouchDelay={0} enterDelay={500} enterNextDelay={1200} title={t("You must specify an amount first")}>
<Paper elevation={4}> <Paper elevation={4}>
<Button sx={{height:38}} variant='contained' color='primary' <Button sx={{height:38}} variant='contained' color='primary'
disabled={true}> disabled={true}>
@ -225,8 +226,8 @@ class OrderPage extends Component {
}else{ }else{
return( return(
<> <>
<this.InactiveMakerDialog/> {this.InactiveMakerDialog()}
<this.StoreTokenDialog/> {this.tokenDialog()}
<Button sx={{height:38}} variant='contained' color='primary' <Button sx={{height:38}} variant='contained' color='primary'
onClick={this.props.copiedToken ? (this.state.maker_status=='Inactive' ? this.handleClickOpenInactiveMakerDialog : this.takeOrder) : (() => this.setState({openStoreToken:true}))}> onClick={this.props.copiedToken ? (this.state.maker_status=='Inactive' ? this.handleClickOpenInactiveMakerDialog : this.takeOrder) : (() => this.setState({openStoreToken:true}))}>
{t("Take Order")} {t("Take Order")}
@ -237,41 +238,18 @@ class OrderPage extends Component {
} }
countdownTakeOrderRenderer = ({ seconds, completed }) => { countdownTakeOrderRenderer = ({ seconds, completed }) => {
if(isNaN(seconds)){return (<this.takeOrderButton/>)} if(isNaN(seconds)){return (this.takeOrderButton())}
if (completed) { if (completed) {
// Render a completed state // Render a completed state
return ( <this.takeOrderButton/>); return this.takeOrderButton();
} else{ } else{
return( return(
<Tooltip enterTouchDelay="0" title={t("Wait until you can take an order")}><div> <Tooltip enterTouchDelay={0} title={t("Wait until you can take an order")}><div>
<Button disabled={true} variant='contained' color='primary'>{t("Take Order")}</Button> <Button disabled={true} variant='contained' color='primary'>{t("Take Order")}</Button>
</div></Tooltip>) </div></Tooltip>)
} }
}; };
LinearDeterminate =()=> {
const [progress, setProgress] = React.useState(0);
React.useEffect(() => {
const timer = setInterval(() => {
setProgress((oldProgress) => {
var left = calcTimeDelta( new Date(this.state.expires_at)).total /1000;
return (left / this.state.total_secs_exp) * 100;
});
}, 1000);
return () => {
clearInterval(timer);
};
}, []);
return (
<Box sx={{ width: '100%' }}>
<LinearProgress variant="determinate" value={progress} />
</Box>
);
}
takeOrder=()=>{ takeOrder=()=>{
this.setState({loading:true}) this.setState({loading:true})
const requestOptions = { const requestOptions = {
@ -376,72 +354,26 @@ class OrderPage extends Component {
) )
} }
StoreTokenDialog = () =>{ tokenDialog = () =>{
const { t } = this.props; return(getCookie("robot_token") ?
<StoreTokenDialog
// If there is a robot cookie, prompt user to store it open={this.state.openStoreToken}
// Else, prompt user to generate a robot onClose={() => this.setState({openStoreToken:false})}
if (getCookie("robot_token")){ onClickCopy={()=> (navigator.clipboard.writeText(getCookie("robot_token")) & this.props.setAppState({copiedToken:true}))}
return( copyIconColor={this.props.copiedToken ? "inherit" : "primary"}
<Dialog onClickBack={() => this.setState({openStoreToken:false})}
open={this.state.openStoreToken} onClickDone={() => this.setState({openStoreToken:false}) &
onClose={() => this.setState({openStoreToken:false})} (this.state.maker_status=='Inactive' ?
> this.handleClickOpenInactiveMakerDialog()
<DialogTitle > : this.takeOrder())
{t("Store your robot token")} }/>
</DialogTitle> :
<DialogContent> <NoRobotDialog
<DialogContentText> open={this.state.openStoreToken}
{t("You might need to recover your robot avatar in the future: store it safely. You can simply copy it into another application.")} onClose={() => this.setState({openStoreToken:false})}
</DialogContentText> />
<br/> )
<Grid align="center"> }
<TextField
sx={{width:"100%", maxWidth:"550px"}}
disabled
label={t("Back it up!")}
value={getCookie("robot_token") }
variant='filled'
size='small'
InputProps={{
endAdornment:
<Tooltip disableHoverListener enterTouchDelay="0" title={t("Copied!")}>
<IconButton onClick= {()=> (navigator.clipboard.writeText(getCookie("robot_token")) & this.props.setAppState({copiedToken:true}))}>
<ContentCopy color={this.props.copiedToken ? "inherit" : "primary"}/>
</IconButton>
</Tooltip>,
}}
/>
</Grid>
</DialogContent>
<DialogActions>
<Button onClick={() => this.setState({openStoreToken:false})} autoFocus>{t("Go back")}</Button>
<Button onClick={() => this.setState({openStoreToken:false}) & (this.state.maker_status=='Inactive' ? this.handleClickOpenInactiveMakerDialog() : this.takeOrder())}>{t("Done")}</Button>
</DialogActions>
</Dialog>
)
}else{
return(
<Dialog
open={this.state.openStoreToken}
onClose={() => this.setState({openStoreToken:false})}
>
<DialogTitle>
{t("You do not have a robot avatar")}
</DialogTitle>
<DialogContent>
<DialogContentText>
{t("You need to generate a robot avatar in order to become an order maker")}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={() => this.setState({openStoreToken:false})} autoFocus>{t("Go back")}</Button>
<Button onClick={() => this.setState({openStoreToken:false})} to="/" component={LinkRouter}>{t("Generate Robot")}</Button>
</DialogActions>
</Dialog>
)
}
}
handleClickConfirmCollaborativeCancelButton=()=>{ handleClickConfirmCollaborativeCancelButton=()=>{
const requestOptions = { const requestOptions = {
@ -517,7 +449,7 @@ class OrderPage extends Component {
return( return(
<div id="openDialogCancelButton"> <div id="openDialogCancelButton">
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<this.CancelDialog/> {this.CancelDialog()}
<Button variant='contained' color='secondary' onClick={this.handleClickOpenConfirmCancelDialog}>{t("Cancel")}</Button> <Button variant='contained' color='secondary' onClick={this.handleClickOpenConfirmCancelDialog}>{t("Cancel")}</Button>
</Grid> </Grid>
</div> </div>
@ -528,7 +460,7 @@ class OrderPage extends Component {
if ([8,9].includes(this.state.status)){ if ([8,9].includes(this.state.status)){
return( return(
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<this.CollaborativeCancelDialog/> {this.CollaborativeCancelDialog()}
<Button variant='contained' color='secondary' onClick={this.handleClickOpenCollaborativeCancelDialog}>{t("Collaborative Cancel")}</Button> <Button variant='contained' color='secondary' onClick={this.handleClickOpenCollaborativeCancelDialog}>{t("Collaborative Cancel")}</Button>
</Grid> </Grid>
)} )}
@ -554,11 +486,11 @@ class OrderPage extends Component {
{t("Order Box")} {t("Order Box")}
</Typography> </Typography>
</MediaQuery> </MediaQuery>
<Paper elevation={12} style={{ padding: 8,}}> <Paper elevation={12} >
<List dense="true"> <List dense={true}>
<ListItem > <ListItem >
<ListItemAvatar sx={{ width: 56, height: 56 }}> <ListItemAvatar sx={{ width: 56, height: 56 }}>
<Tooltip placement="top" enterTouchDelay="0" title={t(this.state.maker_status)} > <Tooltip placement="top" enterTouchDelay={0} title={t(this.state.maker_status)} >
<Badge variant="dot" overlap="circular" badgeContent="" color={this.statusBadgeColor(this.state.maker_status)}> <Badge variant="dot" overlap="circular" badgeContent="" color={this.statusBadgeColor(this.state.maker_status)}>
<Badge overlap="circular" anchorOrigin={{horizontal: 'right', vertical: 'bottom'}} badgeContent={<div style={{position:"relative", left:"12px", top:"4px"}}> {!this.state.type ? <SendReceiveIcon sx={{transform: "scaleX(-1)"}} color="secondary"/> : <SendReceiveIcon color="primary"/>}</div>}> <Badge overlap="circular" anchorOrigin={{horizontal: 'right', vertical: 'bottom'}} badgeContent={<div style={{position:"relative", left:"12px", top:"4px"}}> {!this.state.type ? <SendReceiveIcon sx={{transform: "scaleX(-1)"}} color="secondary"/> : <SendReceiveIcon color="primary"/>}</div>}>
<Avatar className="flippedSmallAvatar" <Avatar className="flippedSmallAvatar"
@ -580,7 +512,7 @@ class OrderPage extends Component {
<ListItem align="left"> <ListItem align="left">
<ListItemText primary={this.state.taker_nick + (this.state.type ? " "+t("(Buyer)") : " "+t("(Seller)"))} secondary={t("Order taker")}/> <ListItemText primary={this.state.taker_nick + (this.state.type ? " "+t("(Buyer)") : " "+t("(Seller)"))} secondary={t("Order taker")}/>
<ListItemAvatar > <ListItemAvatar >
<Tooltip enterTouchDelay="0" title={t(this.state.taker_status)} > <Tooltip enterTouchDelay={0} title={t(this.state.taker_status)} >
<Badge variant="dot" overlap="circular" badgeContent="" color={this.statusBadgeColor(this.state.taker_status)}> <Badge variant="dot" overlap="circular" badgeContent="" color={this.statusBadgeColor(this.state.taker_status)}>
<Badge overlap="circular" anchorOrigin={{horizontal: 'left', vertical: 'bottom'}} badgeContent={<div style={{position:"relative", right:"12px", top:"4px"}}> {this.state.type ? <SendReceiveIcon color="secondary"/> : <SendReceiveIcon sx={{transform: "scaleX(-1)"}} color="primary"/> }</div>}> <Badge overlap="circular" anchorOrigin={{horizontal: 'left', vertical: 'bottom'}} badgeContent={<div style={{position:"relative", right:"12px", top:"4px"}}> {this.state.type ? <SendReceiveIcon color="secondary"/> : <SendReceiveIcon sx={{transform: "scaleX(-1)"}} color="primary"/> }</div>}>
<Avatar className="smallAvatar" <Avatar className="smallAvatar"
@ -609,8 +541,8 @@ class OrderPage extends Component {
<ListItem> <ListItem>
<ListItemIcon> <ListItemIcon>
<div style={{zoom:1.25,opacity: 0.7, '-ms-zoom': 1.25, '-webkit-zoom': 1.25,'-moz-transform': 'scale(1.25,1.25)', '-moz-transform-origin': 'left center'}}> <div style={{zoom:1.25,opacity: 0.7, msZoom: 1.25, WebkitZoom: 1.25, MozTransform: 'scale(1.25,1.25)', MozTransformOrigin: 'left center'}}>
{getFlags(this.state.currencyCode)} <FlagWithProps code={this.state.currencyCode} />
</div> </div>
</ListItemIcon> </ListItemIcon>
{this.state.has_range & this.state.amount == null ? {this.state.has_range & this.state.amount == null ?
@ -653,7 +585,7 @@ class OrderPage extends Component {
<ListItemIcon> <ListItemIcon>
<NumbersIcon/> <NumbersIcon/>
</ListItemIcon> </ListItemIcon>
<Grid container xs={12}> <Grid container>
<Grid item xs={4.5}> <Grid item xs={4.5}>
<ListItemText primary={this.state.orderId} secondary={t("Order ID")}/> <ListItemText primary={this.state.orderId} secondary={t("Order ID")}/>
</Grid> </Grid>
@ -682,7 +614,7 @@ class OrderPage extends Component {
<Countdown date={new Date(this.state.expires_at)} renderer={this.countdownRenderer} /> <Countdown date={new Date(this.state.expires_at)} renderer={this.countdownRenderer} />
</ListItemText> </ListItemText>
</ListItem> </ListItem>
<this.LinearDeterminate /> <LinearDeterminate key={this.state.expires_at} total_secs_exp={this.state.total_secs_exp} expires_at={this.state.expires_at}/>
</List> </List>
{/* If the user has a penalty/limit */} {/* If the user has a penalty/limit */}
@ -728,8 +660,8 @@ class OrderPage extends Component {
{/* Participants can see the "Cancel" Button, but cannot see the "Back" or "Take Order" buttons */} {/* Participants can see the "Cancel" Button, but cannot see the "Back" or "Take Order" buttons */}
{this.state.is_participant ? {this.state.is_participant ?
<> <>
<this.CancelButton/> {this.CancelButton()}
<this.BackButton/> {this.BackButton()}
</> </>
: :
<Grid container spacing={1}> <Grid container spacing={1}>
@ -748,7 +680,7 @@ class OrderPage extends Component {
doubleOrderPageDesktop=()=>{ doubleOrderPageDesktop=()=>{
return( return(
<Grid container xs={12} align="center" spacing={2} > <Grid container align="center" spacing={2} >
<Grid item xs={6} align="left" style={{ width:330}} > <Grid item xs={6} align="left" style={{ width:330}} >
{this.orderBox()} {this.orderBox()}
</Grid> </Grid>
@ -768,27 +700,21 @@ class OrderPage extends Component {
doubleOrderPagePhone=()=>{ doubleOrderPagePhone=()=>{
const { t } = this.props; const { t } = this.props;
const [value, setValue] = React.useState(this.state.showContractBox);
const handleChange = (event, newValue) => {
this.setState({showContractBox:newValue})
setValue(newValue);
};
return( return(
<Box sx={{ width: '100%'}}> <Box sx={{ width: '100%'}}>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}> <Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<Tabs value={value} onChange={handleChange} variant="fullWidth" > <Tabs value={this.state.tabValue} variant="fullWidth" >
<Tab label={t("Order")} {...this.a11yProps(0)} /> <Tab label={t("Order")} {...this.a11yProps(0)} onClick={() => this.setState({tabValue:0})}/>
<Tab label={t("Contract")} {...this.a11yProps(1)} /> <Tab label={t("Contract")} {...this.a11yProps(1)} onClick={() => this.setState({tabValue:1})}/>
</Tabs> </Tabs>
</Box> </Box>
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid item > <Grid item >
<div style={{ width:330, display: this.state.showContractBox == 0 ? '':'none'}}> <div style={{ width:330, display: this.state.tabValue == 0 ? '':'none'}}>
{this.orderBox()} {this.orderBox()}
</div> </div>
<div style={{display: this.state.showContractBox == 1 ? '':'none'}}> <div style={{display: this.state.tabValue == 1 ? '':'none'}}>
<TradeBox push={this.props.history.push} getOrderDetails={this.getOrderDetails} pauseLoading={this.state.pauseLoading} width={330} data={this.state} completeSetState={this.completeSetState} /> <TradeBox push={this.props.history.push} getOrderDetails={this.getOrderDetails} pauseLoading={this.state.pauseLoading} width={330} data={this.state} completeSetState={this.completeSetState} />
</div> </div>
</Grid> </Grid>
@ -802,7 +728,7 @@ class OrderPage extends Component {
return( return(
this.state.bad_request ? this.state.bad_request ?
<div align='center'> <div align='center'>
<Typography component="subtitle2" variant="subtitle2" color="secondary" > <Typography variant="subtitle2" color="secondary" >
{/* IMPLEMENT I18N for bad_request */} {/* IMPLEMENT I18N for bad_request */}
{t(this.state.bad_request)}<br/> {t(this.state.bad_request)}<br/>
</Typography> </Typography>
@ -813,12 +739,12 @@ class OrderPage extends Component {
<> <>
{/* Desktop View */} {/* Desktop View */}
<MediaQuery minWidth={920}> <MediaQuery minWidth={920}>
<this.doubleOrderPageDesktop/> {this.doubleOrderPageDesktop()}
</MediaQuery> </MediaQuery>
{/* SmarPhone View */} {/* SmarPhone View */}
<MediaQuery maxWidth={919}> <MediaQuery maxWidth={919}>
<this.doubleOrderPagePhone/> {this.doubleOrderPagePhone()}
</MediaQuery> </MediaQuery>
</> </>
: :

View File

@ -21,7 +21,7 @@ class PaymentText extends Component {
if(this.props.text.includes(method.name)){ if(this.props.text.includes(method.name)){
custom_methods = custom_methods.replace(method.name,'') custom_methods = custom_methods.replace(method.name,'')
rows.push( rows.push(
<Tooltip placement="top" enterTouchDelay="0" title={t(method.name)}> <Tooltip key={`${method.name}-${i}`} placement="top" enterTouchDelay={0} title={t(method.name)}>
<div style={{display: 'inline-block', width: this.props.size+2, height: this.props.size}}> <div style={{display: 'inline-block', width: this.props.size+2, height: this.props.size}}>
<PaymentIcon width={this.props.size} height={this.props.size} icon={method.icon}/> <PaymentIcon width={this.props.size} height={this.props.size} icon={method.icon}/>
</div> </div>
@ -34,7 +34,7 @@ class PaymentText extends Component {
var chars_left = custom_methods.replace(' ','').replace(' ','').replace(' ','').replace(' ','').replace(' ','') var chars_left = custom_methods.replace(' ','').replace(' ','').replace(' ','').replace(' ','').replace(' ','')
if(chars_left.length > 0){rows.push( if(chars_left.length > 0){rows.push(
<Tooltip placement="top" enterTouchDelay="0" title={this.props.verbose ? this.props.othersText: this.props.othersText+": "+ custom_methods} > <Tooltip key={"pushed"} placement="top" enterTouchDelay={0} title={this.props.verbose ? this.props.othersText: this.props.othersText+": "+ custom_methods} >
<div style={{position:'relative', display: 'inline-block',width: this.props.size+2, maxHeight: this.props.size, top:'-1px'}}> <div style={{position:'relative', display: 'inline-block',width: this.props.size+2, maxHeight: this.props.size, top:'-1px'}}>
<PaymentIcon width={this.props.size*1.1} height={this.props.size*1.1} icon={"custom"}/> <PaymentIcon width={this.props.size*1.1} height={this.props.size*1.1} icon={"custom"}/>
</div> </div>
@ -55,6 +55,6 @@ class PaymentText extends Component {
</div> </div>
) )
} }
}; }
export default withTranslation()(PaymentText); export default withTranslation()(PaymentText);

View File

@ -0,0 +1,34 @@
import { Slider } from "@mui/material"
import { styled } from '@mui/material/styles';
const RangeSlider = styled(Slider)(({ theme }) => ({
color: 'primary',
height: 3,
padding: '13px 0',
'& .MuiSlider-thumb': {
height: 27,
width: 27,
backgroundColor: '#fff',
border: '1px solid currentColor',
'&:hover': {
boxShadow: '0 0 0 8px rgba(58, 133, 137, 0.16)',
},
'& .range-bar': {
height: 9,
width: 1,
backgroundColor: 'currentColor',
marginLeft: 1,
marginRight: 1,
},
},
'& .MuiSlider-track': {
height: 3,
},
'& .MuiSlider-rail': {
color: theme.palette.mode === 'dark' ? '#bfbfbf' : '#d8d8d8',
opacity: theme.palette.mode === 'dark' ? undefined : 1,
height: 3,
},
}));
export default RangeSlider;

View File

@ -40,7 +40,7 @@ class TradeBox extends Component {
Sound = ({soundFileName}) => ( Sound = (soundFileName) => (
// Four filenames: "locked-invoice", "taker-found", "open-chat", "successful" // Four filenames: "locked-invoice", "taker-found", "open-chat", "successful"
<audio autoPlay src={`/static/assets/sounds/${soundFileName}.mp3`} /> <audio autoPlay src={`/static/assets/sounds/${soundFileName}.mp3`} />
) )
@ -182,19 +182,19 @@ class TradeBox extends Component {
return ( return (
<Grid container spacing={1}> <Grid container spacing={1}>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography component="body2" variant="body2"> <Typography variant="body2">
{t("Robots show commitment to their peers")} {t("Robots show commitment to their peers")}
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
{this.props.data.is_maker ? {this.props.data.is_maker ?
<Typography color="primary" component="subtitle1" variant="subtitle1"> <Typography color="primary" variant="subtitle1">
<b> <b>
{t("Lock {{amountSats}} Sats to PUBLISH order", {amountSats: pn(this.props.data.bond_satoshis)})} {t("Lock {{amountSats}} Sats to PUBLISH order", {amountSats: pn(this.props.data.bond_satoshis)})}
</b> {" " + this.stepXofY()} </b> {" " + this.stepXofY()}
</Typography> </Typography>
: :
<Typography color="primary" component="subtitle1" variant="subtitle1"> <Typography color="primary" variant="subtitle1">
<b> <b>
{t("Lock {{amountSats}} Sats to TAKE order", {amountSats: pn(this.props.data.bond_satoshis)})} {t("Lock {{amountSats}} Sats to TAKE order", {amountSats: pn(this.props.data.bond_satoshis)})}
</b> {" " + this.stepXofY()} </b> {" " + this.stepXofY()}
@ -205,7 +205,7 @@ class TradeBox extends Component {
<Box sx={{bgcolor:'#ffffff', width:'315px', position:'relative', left:'-5px'}} > <Box sx={{bgcolor:'#ffffff', width:'315px', position:'relative', left:'-5px'}} >
<QRCode value={this.props.data.bond_invoice} size={305} style={{position:'relative', top:'3px'}}/> <QRCode value={this.props.data.bond_invoice} size={305} style={{position:'relative', top:'3px'}}/>
</Box> </Box>
<Tooltip disableHoverListener enterTouchDelay="0" title={t("Copied!")}> <Tooltip disableHoverListener enterTouchDelay={0} title={t("Copied!")}>
<Button size="small" color="inherit" onClick={() => {navigator.clipboard.writeText(this.props.data.bond_invoice)}} align="center"> <ContentCopy/>{t("Copy to clipboard")}</Button> <Button size="small" color="inherit" onClick={() => {navigator.clipboard.writeText(this.props.data.bond_invoice)}} align="center"> <ContentCopy/>{t("Copy to clipboard")}</Button>
</Tooltip> </Tooltip>
</Grid> </Grid>
@ -215,7 +215,7 @@ class TradeBox extends Component {
variant="standard" variant="standard"
size="small" size="small"
defaultValue={this.props.data.bond_invoice} defaultValue={this.props.data.bond_invoice}
disabled="true" disabled={true}
helperText={t("This is a hold invoice, it will freeze in your wallet. It will be charged only if you cancel or lose a dispute.")} helperText={t("This is a hold invoice, it will freeze in your wallet. It will be charged only if you cancel or lose a dispute.")}
color = "secondary" color = "secondary"
/> />
@ -228,7 +228,7 @@ class TradeBox extends Component {
const {t} = this.props const {t} = this.props
return ( return (
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography color="primary" component="subtitle1" variant="subtitle1" align="center"> <Typography color="primary" variant="subtitle1" align="center">
<div style={{display:'flex', alignItems:'center', justifyContent:'center', flexWrap:'wrap'}}> <div style={{display:'flex', alignItems:'center', justifyContent:'center', flexWrap:'wrap'}}>
<LockIcon/> <LockIcon/>
{this.props.data.is_maker ? t("Your maker bond is locked") : t("Your taker bond is locked")} {this.props.data.is_maker ? t("Your maker bond is locked") : t("Your taker bond is locked")}
@ -242,7 +242,7 @@ class TradeBox extends Component {
const { t } = this.props; const { t } = this.props;
return ( return (
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography color="error" component="subtitle1" variant="subtitle1" align="center"> <Typography color="error" variant="subtitle1" align="center">
<div style={{display:'flex',alignItems:'center', justifyContent:'center', flexWrap:'wrap', align:"center"}} align="center"> <div style={{display:'flex',alignItems:'center', justifyContent:'center', flexWrap:'wrap', align:"center"}} align="center">
<BalanceIcon/> <BalanceIcon/>
{this.props.data.is_maker ? t("Your maker bond was settled") : t("Your taker bond was settled")} {this.props.data.is_maker ? t("Your maker bond was settled") : t("Your taker bond was settled")}
@ -256,7 +256,7 @@ class TradeBox extends Component {
const { t } = this.props; const { t } = this.props;
return ( return (
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography color="green" component="subtitle1" variant="subtitle1" align="center"> <Typography color="green" variant="subtitle1" align="center">
<div style={{display:'flex',alignItems:'center', justifyContent:'center', flexWrap:'wrap'}}> <div style={{display:'flex',alignItems:'center', justifyContent:'center', flexWrap:'wrap'}}>
<LockOpenIcon/> <LockOpenIcon/>
{this.props.data.is_maker ? t("Your maker bond was unlocked") : t("Your taker bond was unlocked")} {this.props.data.is_maker ? t("Your maker bond was unlocked") : t("Your taker bond was unlocked")}
@ -271,9 +271,9 @@ class TradeBox extends Component {
return ( return (
<Grid container spacing={1}> <Grid container spacing={1}>
{/* Make confirmation sound for HTLC received. */} {/* Make confirmation sound for HTLC received. */}
<this.Sound soundFileName="locked-invoice"/> {this.Sound("locked-invoice")}
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography color="green" component="subtitle1" variant="subtitle1"> <Typography color="green" variant="subtitle1">
<b> <b>
{t("Lock {{amountSats}} Sats as collateral", {amountSats:pn(this.props.data.escrow_satoshis)})} {t("Lock {{amountSats}} Sats as collateral", {amountSats:pn(this.props.data.escrow_satoshis)})}
</b>{" " + this.stepXofY()} </b>{" " + this.stepXofY()}
@ -283,7 +283,7 @@ class TradeBox extends Component {
<Box sx={{bgcolor:'#ffffff', width:'315px', position:'relative', left:'-5px'}} > <Box sx={{bgcolor:'#ffffff', width:'315px', position:'relative', left:'-5px'}} >
<QRCode value={this.props.data.escrow_invoice} size={305} style={{position:'relative', top:'3px'}}/> <QRCode value={this.props.data.escrow_invoice} size={305} style={{position:'relative', top:'3px'}}/>
</Box> </Box>
<Tooltip disableHoverListener enterTouchDelay="0" title={t("Copied!")}> <Tooltip disableHoverListener enterTouchDelay={0} title={t("Copied!")}>
<Button size="small" color="inherit" onClick={() => {navigator.clipboard.writeText(this.props.data.escrow_invoice)}} align="center"> <ContentCopy/>{t("Copy to clipboard")}</Button> <Button size="small" color="inherit" onClick={() => {navigator.clipboard.writeText(this.props.data.escrow_invoice)}} align="center"> <ContentCopy/>{t("Copy to clipboard")}</Button>
</Tooltip> </Tooltip>
</Grid> </Grid>
@ -293,7 +293,7 @@ class TradeBox extends Component {
variant="filled" variant="filled"
size="small" size="small"
defaultValue={this.props.data.escrow_invoice} defaultValue={this.props.data.escrow_invoice}
disabled="true" disabled={true}
helperText={t("This is a hold invoice, it will freeze in your wallet. It will be released to the buyer once you confirm to have received the {{currencyCode}}.",{currencyCode: this.props.data.currencyCode})} helperText={t("This is a hold invoice, it will freeze in your wallet. It will be released to the buyer once you confirm to have received the {{currencyCode}}.",{currencyCode: this.props.data.currencyCode})}
color = "secondary" color = "secondary"
/> />
@ -308,15 +308,15 @@ class TradeBox extends Component {
return ( return (
<Grid container spacing={1}> <Grid container spacing={1}>
{/* Make bell sound when taker is found */} {/* Make bell sound when taker is found */}
<this.Sound soundFileName="taker-found"/> {this.Sound("taker-found")}
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography component="subtitle1" variant="subtitle1"> <Typography variant="subtitle1">
<b>{t("A taker has been found!")}</b> {" " + this.stepXofY()} <b>{t("A taker has been found!")}</b> {" " + this.stepXofY()}
</Typography> </Typography>
</Grid> </Grid>
<Divider/> <Divider/>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography component="body2" variant="body2"> <Typography variant="body2">
{t("Please wait for the taker to lock a bond. If the taker does not lock a bond in time, the order will be made public again.")} {t("Please wait for the taker to lock a bond. If the taker does not lock a bond in time, the order will be made public again.")}
</Typography> </Typography>
</Grid> </Grid>
@ -387,21 +387,26 @@ class TradeBox extends Component {
return ( return (
<Grid container spacing={1}> <Grid container spacing={1}>
{/* Make confirmation sound for HTLC received. */} {/* Make confirmation sound for HTLC received. */}
<this.Sound soundFileName="locked-invoice"/> {this.Sound("locked-invoice")}
<this.EnableTelegramDialog/> {this.EnableTelegramDialog()}
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography component="subtitle1" variant="subtitle1"> <Typography variant="subtitle1">
<b> {t("Your order is public")} </b> {" " + this.stepXofY()} <b> {t("Your order is public")} </b> {" " + this.stepXofY()}
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<List dense="true"> <List dense={true}>
<Divider/> <Divider/>
<ListItem> <ListItem>
<Typography component="body2" variant="body2" align="left"> <Typography variant="body2" align="left">
<p>{t("Be patient while robots check the book. This box will ring 🔊 once a robot takes your order, then you will have {{deposit_timer_hours}}h {{deposit_timer_minutes}}m to reply. If you do not reply, you risk losing your bond.", this.depositHoursMinutes() )} </p> {t("Be patient while robots check the book. This box will ring 🔊 once a robot takes your order, then you will have {{deposit_timer_hours}}h {{deposit_timer_minutes}}m to reply. If you do not reply, you risk losing your bond.", this.depositHoursMinutes() )}
<p>{t("If the order expires untaken, your bond will return to you (no action needed).")}</p> </Typography>
</ListItem>
<ListItem>
<Typography variant="body2" align="left">
{t("If the order expires untaken, your bond will return to you (no action needed).")}
</Typography> </Typography>
</ListItem> </ListItem>
@ -431,7 +436,7 @@ class TradeBox extends Component {
{this.props.pauseLoading ? {this.props.pauseLoading ?
<CircularProgress sx={{width:"30px",height:"30px"}}/> <CircularProgress sx={{width:"30px",height:"30px"}}/>
: :
<Tooltip placement="top" enterTouchDelay="500" enterDelay="700" enterNextDelay="2000" title={t("Pause the public order")}> <Tooltip placement="top" enterTouchDelay={500} enterDelay={700} enterNextDelay={2000} title={t("Pause the public order")}>
<Button color="primary" onClick={this.handleClickPauseOrder}> <Button color="primary" onClick={this.handleClickPauseOrder}>
<PauseCircleIcon sx={{width:"36px",height:"36px"}}/> <PauseCircleIcon sx={{width:"36px",height:"36px"}}/>
</Button> </Button>
@ -464,16 +469,16 @@ class TradeBox extends Component {
<Grid container align="center" spacing={1}> <Grid container align="center" spacing={1}>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography component="subtitle1" variant="subtitle1"> <Typography variant="subtitle1">
<b> {t("Your order is paused")} </b> {" " + this.stepXofY()} <b> {t("Your order is paused")} </b> {" " + this.stepXofY()}
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<List dense="true"> <List dense={true}>
<Divider/> <Divider/>
<ListItem> <ListItem>
<Typography component="body2" variant="body2" align="left"> <Typography variant="body2" align="left">
{t("Your public order has been paused. At the moment it cannot be seen or taken by other robots. You can choose to unpause it at any time.")} {t("Your public order has been paused. At the moment it cannot be seen or taken by other robots. You can choose to unpause it at any time.")}
</Typography> </Typography>
</ListItem> </ListItem>
@ -566,14 +571,14 @@ class TradeBox extends Component {
<Grid container spacing={1}> <Grid container spacing={1}>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
{/* Make confirmation sound for HTLC received. */} {/* Make confirmation sound for HTLC received. */}
<this.Sound soundFileName="locked-invoice"/> {this.Sound("locked-invoice")}
<Typography color="primary" component="subtitle1" variant="subtitle1"> <Typography color="primary" variant="subtitle1">
<b> {t("Submit an invoice for {{amountSats}} Sats",{amountSats: pn(this.props.data.invoice_amount)})} <b> {t("Submit an invoice for {{amountSats}} Sats",{amountSats: pn(this.props.data.invoice_amount)})}
</b> {" " + this.stepXofY()} </b> {" " + this.stepXofY()}
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} align="left"> <Grid item xs={12} align="left">
<Typography component="body2" variant="body2"> <Typography variant="body2">
{t("The taker is committed! Before letting you send {{amountFiat}} {{currencyCode}}, we want to make sure you are able to receive the BTC. Please provide a valid invoice for {{amountSats}} Satoshis.", {t("The taker is committed! Before letting you send {{amountFiat}} {{currencyCode}}, we want to make sure you are able to receive the BTC. Please provide a valid invoice for {{amountSats}} Satoshis.",
{amountFiat: parseFloat(parseFloat(this.props.data.amount).toFixed(4)), {amountFiat: parseFloat(parseFloat(this.props.data.amount).toFixed(4)),
currencyCode: this.props.data.currencyCode, currencyCode: this.props.data.currencyCode,
@ -627,15 +632,25 @@ class TradeBox extends Component {
return ( return (
<Grid container spacing={1}> <Grid container spacing={1}>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography color="primary" component="subtitle1" variant="subtitle1"> <Typography color="primary" variant="subtitle1">
<b> {t("We have received your statement")} </b> <b> {t("We have received your statement")} </b>
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} align="left"> <Grid item xs={12} align="center">
<Typography component="body2" variant="body2"> <List dense={true}>
<p>{t("We are waiting for your trade counterpart statement. If you are hesitant about the state of the dispute or want to add more information, contact robosats@protonmail.com.")}</p> <Divider/>
<p>{t("Please, save the information needed to identify your order and your payments: order ID; payment hashes of the bonds or escrow (check on your lightning wallet); exact amount of satoshis; and robot nickname. You will have to identify yourself as the user involved in this trade via email (or other contact methods).")}</p> <ListItem>
</Typography> <Typography variant="body2">
{t("We are waiting for your trade counterpart statement. If you are hesitant about the state of the dispute or want to add more information, contact robosats@protonmail.com.")}
</Typography>
</ListItem>
<ListItem>
<Typography variant="body2">
{t("Please, save the information needed to identify your order and your payments: order ID; payment hashes of the bonds or escrow (check on your lightning wallet); exact amount of satoshis; and robot nickname. You will have to identify yourself as the user involved in this trade via email (or other contact methods).")}
</Typography>
</ListItem>
<Divider/>
</List>
</Grid> </Grid>
{this.showBondIsSettled()} {this.showBondIsSettled()}
</Grid> </Grid>
@ -647,15 +662,17 @@ class TradeBox extends Component {
<Grid container spacing={1}> <Grid container spacing={1}>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography color="primary" component="subtitle1" variant="subtitle1"> <Typography color="primary" variant="subtitle1">
<b> {t("A dispute has been opened")} </b> <b> {t("A dispute has been opened")} </b>
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} align="left"> <List dense={true}>
<Typography component="body2" variant="body2"> <Divider/>
{t("Please, submit your statement. Be clear and specific about what happened and provide the necessary evidence. You MUST provide a contact method: burner email, XMPP or telegram username to follow up with the staff. Disputes are solved at the discretion of real robots (aka humans), so be as helpful as possible to ensure a fair outcome. Max 5000 chars.")} <ListItem>
</Typography> <Typography variant="body2">
</Grid> {t("Please, submit your statement. Be clear and specific about what happened and provide the necessary evidence. You MUST provide a contact method: burner email, XMPP or telegram username to follow up with the staff. Disputes are solved at the discretion of real robots (aka humans), so be as helpful as possible to ensure a fair outcome. Max 5000 chars.")}
</Typography>
</ListItem>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<TextField <TextField
@ -674,6 +691,8 @@ class TradeBox extends Component {
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Button onClick={this.handleClickSubmitStatementButton} variant='contained' color='primary'>Submit</Button> <Button onClick={this.handleClickSubmitStatementButton} variant='contained' color='primary'>Submit</Button>
</Grid> </Grid>
</List>
{this.showBondIsSettled()} {this.showBondIsSettled()}
</Grid> </Grid>
)} )}
@ -684,15 +703,25 @@ class TradeBox extends Component {
return ( return (
<Grid container spacing={1}> <Grid container spacing={1}>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography color="primary" component="subtitle1" variant="subtitle1"> <Typography color="primary" variant="subtitle1">
<b> {t("We have the statements")} </b> <b> {t("We have the statements")} </b>
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} align="left"> <Grid item xs={12} align="center">
<Typography component="body2" variant="body2"> <List dense={true}>
<p>{t("Both statements have been received, wait for the staff to resolve the dispute. If you are hesitant about the state of the dispute or want to add more information, contact robosats@protonmail.com. If you did not provide a contact method, or are unsure whether you wrote it right, write us immediately.")} </p> <Divider/>
<p>{t("Please, save the information needed to identify your order and your payments: order ID; payment hashes of the bonds or escrow (check on your lightning wallet); exact amount of satoshis; and robot nickname. You will have to identify yourself as the user involved in this trade via email (or other contact methods).")}</p> <ListItem>
</Typography> <Typography variant="body2">
{t("Both statements have been received, wait for the staff to resolve the dispute. If you are hesitant about the state of the dispute or want to add more information, contact robosats@protonmail.com. If you did not provide a contact method, or are unsure whether you wrote it right, write us immediately.")}
</Typography>
</ListItem>
<ListItem>
<Typography variant="body2">
{t("Please, save the information needed to identify your order and your payments: order ID; payment hashes of the bonds or escrow (check on your lightning wallet); exact amount of satoshis; and robot nickname. You will have to identify yourself as the user involved in this trade via email (or other contact methods).")}
</Typography>
</ListItem>
<Divider/>
</List>
</Grid> </Grid>
{this.showBondIsSettled()} {this.showBondIsSettled()}
</Grid> </Grid>
@ -704,12 +733,12 @@ class TradeBox extends Component {
return ( return (
<Grid container spacing={1}> <Grid container spacing={1}>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography color="primary" component="subtitle1" variant="subtitle1"> <Typography color="primary" variant="subtitle1">
<b> {t("You have won the dispute")} </b> <b> {t("You have won the dispute")} </b>
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} align="left"> <Grid item xs={12} align="left">
<Typography component="body2" variant="body2"> <Typography variant="body2">
{t("You can claim the dispute resolution amount (escrow and fidelity bond) from your profile rewards. If there is anything the staff can help with, do not hesitate to contact to robosats@protonmail.com (or via your provided burner contact method).")} {t("You can claim the dispute resolution amount (escrow and fidelity bond) from your profile rewards. If there is anything the staff can help with, do not hesitate to contact to robosats@protonmail.com (or via your provided burner contact method).")}
</Typography> </Typography>
</Grid> </Grid>
@ -723,12 +752,12 @@ class TradeBox extends Component {
return ( return (
<Grid container spacing={1}> <Grid container spacing={1}>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography color="error" component="subtitle1" variant="subtitle1"> <Typography color="error" variant="subtitle1">
<b> {t("You have lost the dispute")} </b> <b> {t("You have lost the dispute")} </b>
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} align="left"> <Grid item xs={12} align="left">
<Typography component="body2" variant="body2"> <Typography variant="body2">
{t("Unfortunately you have lost the dispute. If you think this is a mistake you can ask to re-open the case via email to robosats@protonmail.com. However, chances of it being investigated again are low.")} {t("Unfortunately you have lost the dispute. If you think this is a mistake you can ask to re-open the case via email to robosats@protonmail.com. However, chances of it being investigated again are low.")}
</Typography> </Typography>
</Grid> </Grid>
@ -742,15 +771,25 @@ class TradeBox extends Component {
return( return(
<Grid container spacing={1}> <Grid container spacing={1}>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography component="subtitle1" variant="subtitle1"> <Typography variant="subtitle1">
<b>{t("Your invoice looks good!")}</b> {" " + this.stepXofY()} <b>{t("Your invoice looks good!")}</b> {" " + this.stepXofY()}
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography component="body2" variant="body2" align="left"> <List dense={true}>
<p>{t("We are waiting for the seller lock the trade amount.")}</p> <Divider/>
<p>{t("Just hang on for a moment. If the seller does not deposit, you will get your bond back automatically. In addition, you will receive a compensation (check the rewards in your profile).")}</p> <ListItem>
</Typography> <Typography variant="body2" align="left">
{t("We are waiting for the seller lock the trade amount.")}
</Typography>
</ListItem>
<ListItem>
<Typography variant="body2" align="left">
{t("Just hang on for a moment. If the seller does not deposit, you will get your bond back automatically. In addition, you will receive a compensation (check the rewards in your profile).")}
</Typography>
</ListItem>
<Divider/>
</List>
</Grid> </Grid>
{this.showBondIsLocked()} {this.showBondIsLocked()}
</Grid> </Grid>
@ -762,17 +801,28 @@ class TradeBox extends Component {
return( return(
<Grid container spacing={1}> <Grid container spacing={1}>
{/* Make confirmation sound for HTLC received. */} {/* Make confirmation sound for HTLC received. */}
<this.Sound soundFileName="locked-invoice"/> {this.Sound("locked-invoice")}
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography component="subtitle1" variant="subtitle1"> <Typography variant="subtitle1">
<b>{t("The trade collateral is locked!")}</b> {" " + this.stepXofY()} <b>{t("The trade collateral is locked!")}</b> {" " + this.stepXofY()}
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography component="body2" variant="body2" align="left"> <List dense={true}>
<p>{t("We are waiting for the buyer to post a lightning invoice. Once he does, you will be able to directly communicate the fiat payment details.")} </p> <Divider/>
<p>{t("Just hang on for a moment. If the buyer does not cooperate, you will get back the trade collateral and your bond automatically. In addition, you will receive a compensation (check the rewards in your profile).")}</p> <ListItem>
</Typography> <Typography variant="body2" align="left">
{t("We are waiting for the buyer to post a lightning invoice. Once he does, you will be able to directly communicate the fiat payment details.")}
</Typography>
</ListItem>
<ListItem>
<Typography variant="body2" align="left">
{t("Just hang on for a moment. If the buyer does not cooperate, you will get back the trade collateral and your bond automatically. In addition, you will receive a compensation (check the rewards in your profile).")}
</Typography>
</ListItem>
<Divider/>
</List>
</Grid> </Grid>
{this.showBondIsLocked()} {this.showBondIsLocked()}
</Grid> </Grid>
@ -892,13 +942,13 @@ handleRatingRobosatsChange=(e)=>{
return( return(
<Grid container spacing={1}> <Grid container spacing={1}>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography component="subtitle1" variant="subtitle1"> <Typography variant="subtitle1">
<b>{t("The order has expired")}</b> <b>{t("The order has expired")}</b>
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography component="body2" variant="body2"> <Typography variant="body2">
{t(this.props.data.expiry_message)} {t(this.props.data.expiry_message)}
</Typography> </Typography>
</Grid> </Grid>
@ -945,15 +995,15 @@ handleRatingRobosatsChange=(e)=>{
return( return(
<Grid container spacing={1}> <Grid container spacing={1}>
{/* Make confirmation sound for Chat Open. */} {/* Make confirmation sound for Chat Open. */}
<this.Sound soundFileName="chat-open"/> {this.Sound("chat-open")}
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography component="subtitle1" variant="subtitle1"> <Typography variant="subtitle1">
<b> {this.props.data.is_seller ? t("Chat with the buyer"): t("Chat with the seller")}</b> {" " + this.stepXofY()} <b> {this.props.data.is_seller ? t("Chat with the buyer"): t("Chat with the seller")}</b> {" " + this.stepXofY()}
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
{this.props.data.is_seller ? {this.props.data.is_seller ?
<Typography component="body2" variant="body2" align="center"> <Typography variant="body2" align="center">
{this.props.data.status == 9? {this.props.data.status == 9?
t("Say hi! Be helpful and concise. Let them know how to send you {{currencyCode}}.",{currencyCode: this.props.data.currencyCode}) t("Say hi! Be helpful and concise. Let them know how to send you {{currencyCode}}.",{currencyCode: this.props.data.currencyCode})
: :
@ -961,7 +1011,7 @@ handleRatingRobosatsChange=(e)=>{
} }
</Typography> </Typography>
: :
<Typography component="body2" variant="body2" align="center"> <Typography variant="body2" align="center">
{this.props.data.status == 9? {this.props.data.status == 9?
t("Say hi! Ask for payment details and click 'Confirm Sent' as soon as the payment is sent.") t("Say hi! Ask for payment details and click 'Confirm Sent' as soon as the payment is sent.")
: :
@ -989,14 +1039,14 @@ handleRatingRobosatsChange=(e)=>{
return( return(
<Grid container spacing={1}> <Grid container spacing={1}>
{/* Make confirmation sound for Chat Open. */} {/* Make confirmation sound for Chat Open. */}
<this.Sound soundFileName="successful"/> {this.Sound("successful")}
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography component="h6" variant="h6"> <Typography component="h6" variant="h6">
{t("🎉Trade finished!🥳")} {t("🎉Trade finished!🥳")}
</Typography> </Typography>
</Grid> </Grid>
{/* <Grid item xs={12} align="center"> {/* <Grid item xs={12} align="center">
<Typography component="body2" variant="body2" align="center"> <Typography variant="body2" align="center">
What do you think of <b>{this.props.data.is_maker ? this.props.data.taker_nick : this.props.data.maker_nick}</b>? What do you think of <b>{this.props.data.is_maker ? this.props.data.taker_nick : this.props.data.maker_nick}</b>?
</Typography> </Typography>
</Grid> </Grid>
@ -1004,7 +1054,7 @@ handleRatingRobosatsChange=(e)=>{
<Rating name="size-large" defaultValue={0} size="large" onChange={this.handleRatingUserChange} /> <Rating name="size-large" defaultValue={0} size="large" onChange={this.handleRatingUserChange} />
</Grid> */} </Grid> */}
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography component="body2" variant="body2" align="center"> <Typography variant="body2" align="center">
<Trans i18nKey="rate_robosats">What do you think of 🤖<b>RoboSats</b>?</Trans> <Trans i18nKey="rate_robosats">What do you think of 🤖<b>RoboSats</b>?</Trans>
</Typography> </Typography>
</Grid> </Grid>
@ -1013,7 +1063,7 @@ handleRatingRobosatsChange=(e)=>{
</Grid> </Grid>
{this.state.rating_platform==5 ? {this.state.rating_platform==5 ?
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography component="body2" variant="body2" align="center"> <Typography variant="body2" align="center">
<p><b>{t("Thank you! RoboSats loves you too ❤️")}</b></p> <p><b>{t("Thank you! RoboSats loves you too ❤️")}</b></p>
<p>{t("RoboSats gets better with more liquidity and users. Tell a bitcoiner friend about Robosats!")}</p> <p>{t("RoboSats gets better with more liquidity and users. Tell a bitcoiner friend about Robosats!")}</p>
</Typography> </Typography>
@ -1021,7 +1071,7 @@ handleRatingRobosatsChange=(e)=>{
: null} : null}
{this.state.rating_platform!=5 & this.state.rating_platform!=null ? {this.state.rating_platform!=5 & this.state.rating_platform!=null ?
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography component="body2" variant="body2" align="center"> <Typography variant="body2" align="center">
<p><b>{t("Thank you for using Robosats!")}</b></p> <p><b>{t("Thank you for using Robosats!")}</b></p>
<p><Trans i18nKey="let_us_know_hot_to_improve">Let us know how the platform could improve (<Link target='_blank' href="https://t.me/robosats">Telegram</Link> / <Link target='_blank' href="https://github.com/Reckless-Satoshi/robosats/issues">Github</Link>)</Trans></p> <p><Trans i18nKey="let_us_know_hot_to_improve">Let us know how the platform could improve (<Link target='_blank' href="https://t.me/robosats">Telegram</Link> / <Link target='_blank' href="https://github.com/Reckless-Satoshi/robosats/issues">Github</Link>)</Trans></p>
</Typography> </Typography>
@ -1056,7 +1106,7 @@ handleRatingRobosatsChange=(e)=>{
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography component="body2" variant="body2" align="center"> <Typography variant="body2" align="center">
{t("RoboSats is trying to pay your lightning invoice. Remember that lightning nodes must be online in order to receive payments.")} {t("RoboSats is trying to pay your lightning invoice. Remember that lightning nodes must be online in order to receive payments.")}
</Typography> </Typography>
<br/> <br/>
@ -1093,13 +1143,13 @@ handleRatingRobosatsChange=(e)=>{
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography component="body2" variant="body2" align="center"> <Typography variant="body2" align="center">
{t("Your invoice has expired or more than 3 payment attempts have been made. Muun wallet is not recommended. ")} {t("Your invoice has expired or more than 3 payment attempts have been made. Muun wallet is not recommended. ")}
<Link href="https://github.com/Reckless-Satoshi/robosats/issues/44"> {t("Check the list of compatible wallets")}</Link> <Link href="https://github.com/Reckless-Satoshi/robosats/issues/44"> {t("Check the list of compatible wallets")}</Link>
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography color="primary" component="subtitle1" variant="subtitle1"> <Typography color="primary" variant="subtitle1">
<b> {t("Submit an invoice for {{amountSats}} Sats",{amountSats: pn(this.props.data.invoice_amount)})}</b> <b> {t("Submit an invoice for {{amountSats}} Sats",{amountSats: pn(this.props.data.invoice_amount)})}</b>
</Typography> </Typography>
</Grid> </Grid>
@ -1133,7 +1183,7 @@ handleRatingRobosatsChange=(e)=>{
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography component="body2" variant="body2" align="center"> <Typography variant="body2" align="center">
{t("RoboSats will try to pay your invoice 3 times every 5 minutes. If it keeps failing, you will be able to submit a new invoice. Check whether you have enough inbound liquidity. Remember that lightning nodes must be online in order to receive payments.")} {t("RoboSats will try to pay your invoice 3 times every 5 minutes. If it keeps failing, you will be able to submit a new invoice. Check whether you have enough inbound liquidity. Remember that lightning nodes must be online in order to receive payments.")}
</Typography> </Typography>
<List> <List>
@ -1152,8 +1202,8 @@ handleRatingRobosatsChange=(e)=>{
const { t } = this.props; const { t } = this.props;
return ( return (
<Grid container spacing={1} style={{ width:this.props.width}}> <Grid container spacing={1} style={{ width:this.props.width}}>
<this.ConfirmDisputeDialog/> {this.ConfirmDisputeDialog()}
<this.ConfirmFiatReceivedDialog/> {this.ConfirmFiatReceivedDialog()}
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<MediaQuery minWidth={920}> <MediaQuery minWidth={920}>
<Typography component="h5" variant="h5"> <Typography component="h5" variant="h5">

View File

@ -1,9 +1,9 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { withTranslation } from "react-i18next"; import { withTranslation } from "react-i18next";
import { Button , Tooltip, Dialog, Grid, Typography, TextField, ButtonGroup, CircularProgress, IconButton} from "@mui/material" import { Button , Tooltip, Grid, Typography, TextField, ButtonGroup, CircularProgress, IconButton} from "@mui/material"
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import Image from 'material-ui-image' import Image from 'material-ui-image'
import InfoDialog from './InfoDialog' import { InfoDialog } from './Dialogs'
import SmartToyIcon from '@mui/icons-material/SmartToy'; import SmartToyIcon from '@mui/icons-material/SmartToy';
import CasinoIcon from '@mui/icons-material/Casino'; import CasinoIcon from '@mui/icons-material/Casino';
@ -19,25 +19,28 @@ class UserGenPage extends Component {
this.state = { this.state = {
openInfo: false, openInfo: false,
tokenHasChanged: false, tokenHasChanged: false,
token: ""
}; };
this.refCode = this.props.match.params.refCode; this.refCode = this.props.match.params.refCode;
}
componentDidMount() {
// Checks in parent HomePage if there is already a nick and token // Checks in parent HomePage if there is already a nick and token
// Displays the existing one // Displays the existing one
if (this.props.nickname != null){ if (this.props.nickname != null){
this.state = { this.setState({
nickname: this.props.nickname, nickname: this.props.nickname,
token: this.props.token? this.props.token : null, token: this.props.token? this.props.token : "",
avatar_url: '/static/assets/avatars/' + this.props.nickname + '.png', avatar_url: '/static/assets/avatars/' + this.props.nickname + '.png',
loadingRobot: false loadingRobot: false
} });
} }
else{ else{
var newToken = this.genBase62Token(36) var newToken = this.genBase62Token(36)
this.state = { this.setState({
token: newToken token: newToken
}; });
this.getGeneratedUser(newToken); this.getGeneratedUser(newToken);
} }
} }
@ -124,20 +127,6 @@ class UserGenPage extends Component {
this.setState({openInfo: false}); this.setState({openInfo: false});
}; };
InfoDialog =() =>{
return(
<Dialog
open={this.state.openInfo}
onClose={this.handleCloseInfo}
aria-labelledby="info-dialog-title"
aria-describedby="info-dialog-description"
scroll="paper"
>
<InfoDialog handleCloseInfo = {this.handleCloseInfo}/>
</Dialog>
)
}
render() { render() {
const { t, i18n} = this.props; const { t, i18n} = this.props;
return ( return (
@ -158,13 +147,13 @@ class UserGenPage extends Component {
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Tooltip enterTouchDelay="0" title={t("This is your trading avatar")}> <Tooltip enterTouchDelay={0} title={t("This is your trading avatar")}>
<div style={{ maxWidth: 200, maxHeight: 200 }}> <div style={{ maxWidth: 200, maxHeight: 200 }}>
<Image className='newAvatar' <Image className='newAvatar'
disableError='true' disableError={true}
cover='true' cover={true}
color='null' color='null'
src={this.state.avatar_url} src={this.state.avatar_url || ""}
/> />
</div> </div>
</Tooltip><br/> </Tooltip><br/>
@ -187,7 +176,7 @@ class UserGenPage extends Component {
<TextField sx={{maxWidth: 280}} <TextField sx={{maxWidth: 280}}
error={this.state.bad_request} error={this.state.bad_request}
label={t("Store your token safely")} label={t("Store your token safely")}
required='true' required={true}
value={this.state.token} value={this.state.token}
variant='standard' variant='standard'
helperText={this.state.bad_request} helperText={this.state.bad_request}
@ -200,13 +189,13 @@ class UserGenPage extends Component {
}} }}
InputProps={{ InputProps={{
startAdornment: startAdornment:
<Tooltip disableHoverListener enterTouchDelay="0" title={t("Copied!")}> <Tooltip disableHoverListener enterTouchDelay={0} title={t("Copied!")}>
<IconButton onClick= {()=> (navigator.clipboard.writeText(this.state.token) & this.props.setAppState({copiedToken:true}))}> <IconButton onClick= {()=> (navigator.clipboard.writeText(this.state.token) & this.props.setAppState({copiedToken:true}))}>
<ContentCopy color={this.props.avatarLoaded & !this.props.copiedToken & !this.state.bad_request ? 'primary' : 'inherit' } sx={{width:18, height:18}}/> <ContentCopy color={this.props.avatarLoaded & !this.props.copiedToken & !this.state.bad_request ? 'primary' : 'inherit' } sx={{width:18, height:18}}/>
</IconButton> </IconButton>
</Tooltip>, </Tooltip>,
endAdornment: endAdornment:
<Tooltip enterTouchDelay="250" title={t("Generate a new token")}> <Tooltip enterTouchDelay={250} title={t("Generate a new token")}>
<IconButton onClick={this.handleClickNewRandomToken}><CasinoIcon/></IconButton> <IconButton onClick={this.handleClickNewRandomToken}><CasinoIcon/></IconButton>
</Tooltip>, </Tooltip>,
}} }}
@ -220,7 +209,7 @@ class UserGenPage extends Component {
<span> {t("Generate Robot")}</span> <span> {t("Generate Robot")}</span>
</Button> </Button>
: :
<Tooltip enterTouchDelay="0" enterDelay="500" enterNextDelay="2000" title={t("You must enter a new token first")}> <Tooltip enterTouchDelay={0} enterDelay={500} enterNextDelay={2000} title={t("You must enter a new token first")}>
<div> <div>
<Button disabled={true} type="submit" size='small' > <Button disabled={true} type="submit" size='small' >
<SmartToyIcon sx={{width:18, height:18}} /> <SmartToyIcon sx={{width:18, height:18}} />
@ -234,17 +223,17 @@ class UserGenPage extends Component {
<ButtonGroup variant="contained" aria-label="outlined primary button group"> <ButtonGroup variant="contained" aria-label="outlined primary button group">
<Button disabled={this.state.loadingRobot} color='primary' to='/make/' component={Link}>{t("Make Order")}</Button> <Button disabled={this.state.loadingRobot} color='primary' to='/make/' component={Link}>{t("Make Order")}</Button>
<Button color='inherit' style={{color: '#111111'}} onClick={this.handleClickOpenInfo}>{t("Info")}</Button> <Button color='inherit' style={{color: '#111111'}} onClick={this.handleClickOpenInfo}>{t("Info")}</Button>
<this.InfoDialog/> <InfoDialog open={Boolean(this.state.openInfo)} onClose = {this.handleCloseInfo}/>
<Button disabled={this.state.loadingRobot} color='secondary' to='/book/' component={Link}>{t("View Book")}</Button> <Button disabled={this.state.loadingRobot} color='secondary' to='/book/' component={Link}>{t("View Book")}</Button>
</ButtonGroup> </ButtonGroup>
</Grid> </Grid>
<Grid item xs={12} align="center" spacing={2} sx={{width:370}}> <Grid item xs={12} align="center" sx={{width:370}}>
<Grid item> <Grid item>
<div style={{height:40}}/> <div style={{height:40}}/>
</Grid> </Grid>
<div style={{width:370, left:30}}> <div style={{width:370, left:30}}>
<Grid container xs={12} align="center"> <Grid container align="center">
<Grid item xs={0.8}/> <Grid item xs={0.8}/>
<Grid item xs={7.5} align="right"> <Grid item xs={7.5} align="right">
<Typography component="h5" variant="h5"> <Typography component="h5" variant="h5">

View File

@ -1,78 +0,0 @@
import React, { Component } from 'react';
import Flags from 'country-flag-icons/react/3x2'
import SwapCallsIcon from '@mui/icons-material/SwapCalls';
import GoldIcon from './icons/GoldIcon';
import EarthIcon from './icons/EarthIcon'
export default function getFlags(code){
const props = {width:20,height:20}
var flag = "";
if(code == 'AUD') flag = <Flags.AU {...props}/>;
if(code == 'ARS') flag = <Flags.AR {...props}/>;
if(code == 'BRL') flag = <Flags.BR {...props}/>;
if(code == 'CAD') flag = <Flags.CA {...props}/>;
if(code == 'CHF') flag = <Flags.CH {...props}/>;
if(code == 'CLP') flag = <Flags.CL {...props}/>;
if(code == 'CNY') flag = <Flags.CN {...props}/>;
if(code == 'EUR') flag = <Flags.EU {...props}/>;
if(code == 'HRK') flag = <Flags.HR {...props}/>;
if(code == 'CZK') flag = <Flags.CZ {...props}/>;
if(code == 'DKK') flag = <Flags.DK {...props}/>;
if(code == 'GBP') flag = <Flags.GB {...props}/>;
if(code == 'HKD') flag = <Flags.HK {...props}/>;
if(code == 'HUF') flag = <Flags.HU {...props}/>;
if(code == 'INR') flag = <Flags.IN {...props}/>;
if(code == 'ISK') flag = <Flags.IS {...props}/>;
if(code == 'JPY') flag = <Flags.JP {...props}/>;
if(code == 'KRW') flag = <Flags.KR {...props}/>;
if(code == 'MXN') flag = <Flags.MX {...props}/>;
if(code == 'NOK') flag = <Flags.NO {...props}/>;
if(code == 'NZD') flag = <Flags.NZ {...props}/>;
if(code == 'PLN') flag = <Flags.PL {...props}/>;
if(code == 'RON') flag = <Flags.RO {...props}/>;
if(code == 'RUB') flag = <Flags.RU {...props}/>;
if(code == 'SEK') flag = <Flags.SE {...props}/>;
if(code == 'SGD') flag = <Flags.SG {...props}/>;
if(code == 'VES') flag = <Flags.VE {...props}/>;
if(code == 'TRY') flag = <Flags.TR {...props}/>;
if(code == 'USD') flag = <Flags.US {...props}/>;
if(code == 'ZAR') flag = <Flags.ZA {...props}/>;
if(code == 'COP') flag = <Flags.CO {...props}/>;
if(code == 'PEN') flag = <Flags.PE {...props}/>;
if(code == 'UYU') flag = <Flags.UY {...props}/>;
if(code == 'PYG') flag = <Flags.PY {...props}/>;
if(code == 'BOB') flag = <Flags.BO {...props}/>;
if(code == 'IDR') flag = <Flags.ID {...props}/>;
if(code == 'ANG') flag = <Flags.CW {...props}/>;
if(code == 'CRC') flag = <Flags.CR {...props}/>;
if(code == 'CUP') flag = <Flags.CU {...props}/>;
if(code == 'DOP') flag = <Flags.DO {...props}/>;
if(code == 'GHS') flag = <Flags.GH {...props}/>;
if(code == 'GTQ') flag = <Flags.GT {...props}/>;
if(code == 'ILS') flag = <Flags.IL {...props}/>;
if(code == 'JMD') flag = <Flags.JM {...props}/>;
if(code == 'KES') flag = <Flags.KE {...props}/>;
if(code == 'KZT') flag = <Flags.KZ {...props}/>;
if(code == 'MYR') flag = <Flags.MY {...props}/>;
if(code == 'NAD') flag = <Flags.NA {...props}/>;
if(code == 'NGN') flag = <Flags.NG {...props}/>;
if(code == 'AZN') flag = <Flags.AZ {...props}/>;
if(code == 'PAB') flag = <Flags.PA {...props}/>;
if(code == 'PHP') flag = <Flags.PH {...props}/>;
if(code == 'PKR') flag = <Flags.PK {...props}/>;
if(code == 'QAR') flag = <Flags.QA {...props}/>;
if(code == 'SAR') flag = <Flags.SA {...props}/>;
if(code == 'THB') flag = <Flags.TH {...props}/>;
if(code == 'TTD') flag = <Flags.TT {...props}/>;
if(code == 'VND') flag = <Flags.VN {...props}/>;
if(code == 'XOF') flag = <Flags.BJ {...props}/>;
if(code == 'TWD') flag = <Flags.TW {...props}/>;
if(code == 'TZS') flag = <Flags.TZ {...props}/>;
if(code == 'XAF') flag = <Flags.CM {...props}/>;
if(code == 'UAH') flag = <Flags.UA {...props}/>;
if(code == 'ANY') flag = <EarthIcon {...props}/>;
if(code == 'XAU') flag = <GoldIcon {...props}/>;
if(code == 'BTC') flag = <SwapCallsIcon color="primary"/>;
return <div style={{width:28, height: 20}}>{flag}</div>;
};

View File

@ -3,7 +3,7 @@ import { SvgIcon } from "@mui/material"
export default function GoldIcon(props) { export default function GoldIcon(props) {
return ( return (
<SvgIcon x="0px" y="0px" viewBox="0 0 511.882 511.882"> <SvgIcon {...props} x="0px" y="0px" viewBox="0 0 511.882 511.882">
<polygon style={{fill:"#F6BB42"}} points="350.216,176.572 278.374,158.615 37.038,264.123 0,338.207 125.753,374.324 386.13,258.531 <polygon style={{fill:"#F6BB42"}} points="350.216,176.572 278.374,158.615 37.038,264.123 0,338.207 125.753,374.324 386.13,258.531
"/> "/>
<polygon style={{fill:"#FFCE54"}} points="350.216,176.572 107.756,284.345 125.753,374.324 386.13,258.531 "/> <polygon style={{fill:"#FFCE54"}} points="350.216,176.572 107.756,284.345 125.753,374.324 386.13,258.531 "/>

View File

@ -1,11 +0,0 @@
export const pn = (value) => {
if (value == null || value == undefined) {
return;
}
let parts = value.toString().split(".");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
return parts.join(".");
};

View File

@ -0,0 +1,11 @@
export const pn = (value?: number | null): string | undefined => {
if (value === null || value === undefined) {
return;
}
const parts = value.toString().split(".");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
return parts.join(".");
};

17
frontend/tsconfig.json Normal file
View File

@ -0,0 +1,17 @@
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"esModuleInterop": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
},
"include": ["src"]
}

View File

@ -1,37 +0,0 @@
const path = require("path");
const webpack = require("webpack");
module.exports = {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "./static/frontend"),
filename: "[name].js",
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
},
],
},
optimization: {
minimize: true,
},
plugins: [
new webpack.DefinePlugin({
"process.env": {
// This has effect on the react lib size
NODE_ENV: JSON.stringify("production"),
},
}),
//new webpack.optimize.AggressiveMergingPlugin() //Merge chunks
],
resolve: {
extensions: ['.ts', '.js'],
},
};

View File

@ -0,0 +1,33 @@
import path from "path";
import { Configuration } from "webpack";
const config: Configuration = {
entry: "./src/index.js",
module: {
rules: [
{
test: /\.(ts|js)x?$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: [
"@babel/preset-env",
"@babel/preset-react",
"@babel/preset-typescript",
],
},
},
},
],
},
resolve: {
extensions: [".tsx", ".ts", ".jsx", ".js"],
},
output: {
path: path.resolve(__dirname, "static/frontend"),
filename: "main.js",
},
};
export default config;