robosats/frontend/src/components/UserGenPage.js

435 lines
15 KiB
JavaScript
Raw Normal View History

2022-09-09 17:18:04 +00:00
import React, { Component } from 'react';
import { withTranslation } from 'react-i18next';
import {
Button,
Tooltip,
Grid,
Typography,
TextField,
ButtonGroup,
CircularProgress,
IconButton,
} from '@mui/material';
import { Link } from 'react-router-dom';
import SmoothImage from 'react-smooth-image';
import { InfoDialog } from './Dialogs';
import SmartToyIcon from '@mui/icons-material/SmartToy';
import CasinoIcon from '@mui/icons-material/Casino';
2022-09-09 17:18:04 +00:00
import ContentCopy from '@mui/icons-material/ContentCopy';
import BoltIcon from '@mui/icons-material/Bolt';
import DownloadIcon from '@mui/icons-material/Download';
2022-09-09 17:18:04 +00:00
import { RoboSatsNoTextIcon } from './Icons';
import { sha256 } from 'js-sha256';
2022-09-09 17:18:04 +00:00
import { genBase62Token, tokenStrength } from '../utils/token';
import { genKey } from '../utils/pgp';
import { getCookie, writeCookie, deleteCookie } from '../utils/cookies';
import { saveAsJson } from '../utils/saveFile';
import { copyToClipboard } from '../utils/clipboard';
import { apiClient } from '../services/api/index';
class UserGenPage extends Component {
constructor(props) {
super(props);
this.state = {
2022-01-15 00:28:19 +00:00
openInfo: false,
tokenHasChanged: false,
2022-09-09 17:18:04 +00:00
token: '',
};
this.refCode = this.props.match.params.refCode;
2022-05-02 19:28:34 +00:00
}
2022-05-02 19:28:34 +00:00
componentDidMount() {
// Checks in parent HomePage if there is already a nick and token
// Displays the existing one
2022-09-09 17:18:04 +00:00
if (this.props.nickname != null) {
2022-05-02 19:28:34 +00:00
this.setState({
nickname: this.props.nickname,
2022-09-09 17:18:04 +00:00
token: this.props.token ? this.props.token : '',
avatarUrl: '/static/assets/avatars/' + this.props.nickname + '.png',
2022-09-09 17:18:04 +00:00
loadingRobot: false,
2022-05-02 19:28:34 +00:00
});
2022-09-09 17:18:04 +00:00
} else {
const newToken = genBase62Token(36);
2022-05-02 19:28:34 +00:00
this.setState({
2022-09-09 17:18:04 +00:00
token: newToken,
2022-05-02 19:28:34 +00:00
});
this.getGeneratedUser(newToken);
}
}
2022-09-09 17:18:04 +00:00
getGeneratedUser = (token) => {
const strength = tokenStrength(token);
2022-09-09 17:18:04 +00:00
const refCode = this.refCode;
const requestBody = genKey(token).then(function (key) {
return {
token_sha256: sha256(token),
public_key: key.publicKeyArmored,
encrypted_private_key: key.encryptedPrivateKeyArmored,
unique_values: strength.uniqueValues,
counts: strength.counts,
length: token.length,
ref_code: refCode,
2022-09-09 17:18:04 +00:00
};
});
requestBody.then((body) =>
apiClient.post('/api/user/', body).then((data) => {
this.setState({
nickname: data.nickname,
bit_entropy: data.token_bits_entropy,
avatarUrl: '/static/assets/avatars/' + data.nickname + '.png',
shannon_entropy: data.token_shannon_entropy,
bad_request: data.bad_request,
found: data.found,
loadingRobot: false,
stealthInvoices: data.wants_stealth,
}) &
// Add nick and token to App state (token only if not a bad request)
(data.bad_request
? this.props.setAppState({
nickname: data.nickname,
avatarLoaded: false,
activeOrderId: data.active_order_id ? data.active_order_id : null,
referralCode: data.referral_code,
earnedRewards: data.earned_rewards,
lastOrderId: data.last_order_id ? data.last_order_id : null,
stealthInvoices: data.wants_stealth,
})
: this.props.setAppState({
nickname: data.nickname,
token,
avatarLoaded: false,
activeOrderId: data.active_order_id ? data.active_order_id : null,
lastOrderId: data.last_order_id ? data.last_order_id : null,
referralCode: data.referral_code,
earnedRewards: data.earned_rewards,
stealthInvoices: data.wants_stealth,
2022-09-16 17:32:16 +00:00
tgEnabled: data.tg_enabled,
tgBotName: data.tg_bot_name,
tgToken: data.tg_token,
}) &
writeCookie('robot_token', token) &
writeCookie('pub_key', data.public_key.split('\n').join('\\')) &
writeCookie('enc_priv_key', data.encrypted_private_key.split('\n').join('\\'))) &
// If the robot has been found (recovered) we assume the token is backed up
(data.found ? this.props.setAppState({ copiedToken: true }) : null);
}),
);
2022-09-09 17:18:04 +00:00
};
2022-05-02 19:28:34 +00:00
delGeneratedUser() {
apiClient.delete('/api/user');
2022-09-09 17:18:04 +00:00
deleteCookie('sessionid');
deleteCookie('robot_token');
deleteCookie('pub_key');
deleteCookie('enc_priv_key');
}
2022-09-09 17:18:04 +00:00
handleClickNewRandomToken = () => {
const token = genBase62Token(36);
this.setState({
token,
tokenHasChanged: true,
});
2022-09-09 17:18:04 +00:00
this.props.setAppState({ copiedToken: true });
};
2022-09-09 17:18:04 +00:00
handleChangeToken = (e) => {
this.setState({
2022-06-11 13:12:09 +00:00
token: e.target.value.split(' ').join(''),
tokenHasChanged: true,
2022-09-09 17:18:04 +00:00
});
};
2022-09-09 17:18:04 +00:00
handleClickSubmitToken = () => {
2022-02-01 19:43:33 +00:00
this.delGeneratedUser();
this.getGeneratedUser(this.state.token);
2022-09-09 17:18:04 +00:00
this.setState({ loadingRobot: true, tokenHasChanged: false });
this.props.setAppState({
avatarLoaded: false,
nickname: null,
token: null,
copiedToken: false,
lastOrderId: null,
2022-09-09 17:18:04 +00:00
activeOrderId: null,
});
};
2022-01-15 00:28:19 +00:00
handleClickOpenInfo = () => {
2022-09-09 17:18:04 +00:00
this.setState({ openInfo: true });
2022-01-15 00:28:19 +00:00
};
handleCloseInfo = () => {
2022-09-09 17:18:04 +00:00
this.setState({ openInfo: false });
2022-01-15 00:28:19 +00:00
};
createJsonFile = () => {
2022-09-09 17:18:04 +00:00
return {
token: getCookie('robot_token'),
token_shannon_entropy: this.state.shannon_entropy,
token_bit_entropy: this.state.bit_entropy,
public_key: getCookie('pub_key').split('\\').join('\n'),
encrypted_private_key: getCookie('enc_priv_key').split('\\').join('\n'),
};
};
render() {
2022-09-09 17:18:04 +00:00
const { t, i18n } = this.props;
const fontSize = this.props.theme.typography.fontSize;
2022-08-26 01:07:28 +00:00
const fontSizeFactor = fontSize / 14; // to scale sizes, default fontSize is 14
return (
<Grid container spacing={1}>
<Grid item>
2022-09-09 17:18:04 +00:00
<div className='clickTrough' />
</Grid>
2022-09-09 17:18:04 +00:00
<Grid
item
xs={12}
align='center'
sx={{ width: 370 * fontSizeFactor, height: 260 * fontSizeFactor }}
>
{this.props.avatarLoaded && this.state.avatarUrl ? (
<div>
2022-09-09 17:18:04 +00:00
<Grid item xs={12} align='center'>
<Typography component='h5' variant='h5'>
<b>
{this.state.nickname && getCookie('sessionid') ? (
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexWrap: 'wrap',
height: 45 * fontSizeFactor,
}}
>
<BoltIcon
sx={{
color: '#fcba03',
height: 33 * fontSizeFactor,
width: 33 * fontSizeFactor,
}}
/>
<a>{this.state.nickname}</a>
<BoltIcon
sx={{
color: '#fcba03',
height: 33 * fontSizeFactor,
width: 33 * fontSizeFactor,
}}
/>
</div>
) : (
''
)}
</b>
</Typography>
</Grid>
2022-09-09 17:18:04 +00:00
<Grid item xs={12} align='center'>
<Tooltip enterTouchDelay={0} title={t('This is your trading avatar')}>
<div style={{ maxWidth: 200 * fontSizeFactor, maxHeight: 200 * fontSizeFactor }}>
<SmoothImage
src={this.state.avatarUrl}
imageStyles={{
borderRadius: '50%',
border: '2px solid #555',
filter: 'drop-shadow(1px 1px 1px #000000)',
height: `${195 * fontSizeFactor}px`,
width: `${200 * fontSizeFactor}px`,
}}
/>
</div>
</Tooltip>
2022-09-09 17:18:04 +00:00
<br />
</Grid>
</div>
2022-09-09 17:18:04 +00:00
) : (
<CircularProgress sx={{ position: 'relative', top: 100 }} />
)}
</Grid>
{this.state.found ? (
<Grid item xs={12} align='center'>
<Typography variant='subtitle2' color='primary'>
{this.state.found ? t('A robot avatar was found, welcome back!') : null}
<br />
</Typography>
</Grid>
2022-09-09 17:18:04 +00:00
) : (
''
)}
<Grid container align='center'>
<Grid item xs={12} align='center'>
<TextField
sx={{ maxWidth: 280 * fontSizeFactor }}
error={!!this.state.bad_request}
2022-09-09 17:18:04 +00:00
label={t('Store your token safely')}
required={true}
value={this.state.token}
variant='standard'
helperText={this.state.bad_request}
size='small'
onChange={this.handleChangeToken}
onKeyPress={(e) => {
if (e.key === 'Enter') {
this.handleClickSubmitToken();
}
}}
InputProps={{
startAdornment: (
<div
style={{
width: 50 * fontSizeFactor,
minWidth: 50 * fontSizeFactor,
position: 'relative',
left: -6,
}}
>
2022-05-26 21:16:02 +00:00
<Grid container>
2022-09-09 17:18:04 +00:00
<Grid item xs={6}>
<Tooltip
enterTouchDelay={250}
title={t('Save token and PGP credentials to file')}
>
2022-05-26 21:16:02 +00:00
<span>
2022-09-09 17:18:04 +00:00
<IconButton
color='primary'
disabled={
!(getCookie('robot_token') == this.state.token) ||
!this.props.avatarLoaded
}
onClick={() =>
saveAsJson(this.state.nickname + '.json', this.createJsonFile())
}
>
2022-09-09 17:18:04 +00:00
<DownloadIcon
sx={{ width: 22 * fontSizeFactor, height: 22 * fontSizeFactor }}
/>
</IconButton>
2022-05-26 21:16:02 +00:00
</span>
</Tooltip>
</Grid>
<Grid item xs={6}>
2022-09-09 17:18:04 +00:00
<Tooltip disableHoverListener enterTouchDelay={0} title={t('Copied!')}>
<IconButton
color={this.props.copiedToken ? 'inherit' : 'primary'}
disabled={
!(getCookie('robot_token') == this.state.token) ||
!this.props.avatarLoaded
}
onClick={() =>
copyToClipboard(getCookie('robot_token')) &
this.props.setAppState({ copiedToken: true })
}
>
<ContentCopy
sx={{ width: 18 * fontSizeFactor, height: 18 * fontSizeFactor }}
/>
</IconButton>
</Tooltip>
</Grid>
</Grid>
2022-09-09 17:18:04 +00:00
</div>
),
endAdornment: (
<Tooltip enterTouchDelay={250} title={t('Generate a new token')}>
2022-08-26 01:07:28 +00:00
<IconButton onClick={this.handleClickNewRandomToken}>
2022-09-09 17:18:04 +00:00
<CasinoIcon
sx={{ width: 18 * fontSizeFactor, height: 18 * fontSizeFactor }}
/>
2022-08-26 01:07:28 +00:00
</IconButton>
2022-09-09 17:18:04 +00:00
</Tooltip>
),
}}
/>
</Grid>
2022-09-09 17:18:04 +00:00
</Grid>
<Grid item xs={12} align='center'>
{this.state.tokenHasChanged ? (
<Button type='submit' size='small' onClick={this.handleClickSubmitToken}>
<SmartToyIcon sx={{ width: 18 * fontSizeFactor, height: 18 * fontSizeFactor }} />
<span> {t('Generate Robot')}</span>
</Button>
2022-09-09 17:18:04 +00:00
) : (
<Tooltip
enterTouchDelay={0}
enterDelay={500}
enterNextDelay={2000}
title={t('You must enter a new token first')}
>
<div>
2022-09-09 17:18:04 +00:00
<Button disabled={true} type='submit' size='small'>
<SmartToyIcon sx={{ width: 18 * fontSizeFactor, height: 18 * fontSizeFactor }} />
<span>{t('Generate Robot')}</span>
</Button>
</div>
</Tooltip>
2022-09-09 17:18:04 +00:00
)}
</Grid>
<Grid item xs={12} align='center'>
<ButtonGroup variant='contained' aria-label='outlined primary button group'>
<Button
disabled={
this.state.loadingRobot ||
!(this.props.token ? getCookie('robot_token') == this.props.token : true)
}
color='primary'
to='/make/'
component={Link}
>
{t('Make Order')}
</Button>
<Button color='inherit' style={{ color: '#111111' }} onClick={this.handleClickOpenInfo}>
{t('Info')}
</Button>
<InfoDialog
open={Boolean(this.state.openInfo)}
maxAmount='4,000,000'
onClose={this.handleCloseInfo}
/>
<Button
disabled={
this.state.loadingRobot ||
!(this.props.token ? getCookie('robot_token') == this.props.token : true)
}
color='secondary'
to='/book/'
component={Link}
>
{t('View Book')}
</Button>
</ButtonGroup>
</Grid>
2022-09-09 17:18:04 +00:00
<Grid item xs={12} align='center' sx={{ width: 370 * fontSizeFactor }}>
<Grid item>
<div style={{ height: 30 * fontSizeFactor }} />
</Grid>
2022-09-09 17:18:04 +00:00
<div style={{ width: 370 * fontSizeFactor, left: 30 * fontSizeFactor }}>
<Grid container align='center'>
<Grid item xs={0.8} />
<Grid item xs={7.5} align='right'>
<Typography component='h5' variant='h5'>
{t('Simple and Private LN P2P Exchange')}
</Typography>
</Grid>
<Grid item xs={2.5} align='left'>
<RoboSatsNoTextIcon
color='primary'
sx={{ height: 72 * fontSizeFactor, width: 72 * fontSizeFactor }}
/>
</Grid>
</Grid>
</div>
</Grid>
</Grid>
);
}
}
export default withTranslation()(UserGenPage);