Small fixes

This commit is contained in:
Reckless_Satoshi 2023-02-20 12:36:38 -08:00
parent b98c7a1e9f
commit 766666f8ef
No known key found for this signature in database
GPG Key ID: 9C4585B561315571
4 changed files with 299 additions and 649 deletions

View File

@ -12,6 +12,9 @@ import {
Link,
Typography,
useTheme,
Accordion,
AccordionSummary,
AccordionDetails,
} from '@mui/material';
import { Page } from '../NavBar';
import { Robot } from '../../models';
@ -65,21 +68,15 @@ const Onboarding = ({
};
return (
<Grid container direction='column' alignItems='center' spacing={2} padding={2}>
<Grid item>
<Box>
<Accordion expanded={step === '1'} disableGutters={true}>
<AccordionSummary>
<Typography variant='h5' color={step == '1' ? 'text.primary' : 'text.disabled'}>
{t('1. Generate a token')}
</Typography>
<Box
sx={{
backgroundColor: 'background.paper',
border: '1px solid',
borderRadius: '4px',
borderColor: theme.palette.mode === 'dark' ? '#434343' : '#c4c4c4',
}}
>
<Collapse in={step == '1'}>
<Grid container direction='column' alignItems='center' spacing={1} padding={1.5}>
</AccordionSummary>
<AccordionDetails>
<Grid container direction='column' alignItems='center' spacing={1} padding={1}>
<Grid item>
<Typography>
{t(
@ -149,24 +146,17 @@ const Onboarding = ({
</Grid>
)}
</Grid>
</Collapse>
</Box>
</Grid>
</AccordionDetails>
</Accordion>
<Grid item sx={{ width: '100%' }}>
<Accordion expanded={step === '2'} disableGutters={true}>
<AccordionSummary>
<Typography variant='h5' color={step == '2' ? 'text.primary' : 'text.disabled'}>
{t('2. Meet your robot identity')}
</Typography>
<Box
sx={{
backgroundColor: 'background.paper',
border: '1px solid',
borderRadius: '4px',
borderColor: theme.palette.mode === 'dark' ? '#434343' : '#c4c4c4',
}}
>
<Collapse in={step == '2'}>
<Grid container direction='column' alignItems='center' spacing={1} padding={1.5}>
</AccordionSummary>
<AccordionDetails>
<Grid container direction='column' alignItems='center' spacing={1}>
<Grid item>
<Typography>
{robot.avatarLoaded && robot.nickname ? (
@ -238,23 +228,16 @@ const Onboarding = ({
</Collapse>
</Grid>
</Grid>
</Collapse>
</Box>
</Grid>
</AccordionDetails>
</Accordion>
<Grid item>
<Accordion expanded={step === '3'} disableGutters={true}>
<AccordionSummary>
<Typography variant='h5' color={step == '3' ? 'text.primary' : 'text.disabled'}>
{t('3. Browse or create an order')}
</Typography>
<Box
sx={{
backgroundColor: 'background.paper',
border: '1px solid',
borderRadius: '4px',
borderColor: theme.palette.mode === 'dark' ? '#434343' : '#c4c4c4',
}}
>
<Collapse in={step == '3'}>
</AccordionSummary>
<AccordionDetails>
<Grid container direction='column' alignItems='center' spacing={1} padding={1.5}>
<Grid item>
<Typography>
@ -301,16 +284,15 @@ const Onboarding = ({
<NewTabIcon sx={{ width: '0.8em' }} />
</Button>
</Grid>
</Grid>
</Collapse>
</Box>
</Grid>
<Grid item>
<Grid item sx={{ position: 'relative', top: '0.6em' }}>
<Button color='inherit' onClick={() => setView('profile')}>
{t('See profile')}
</Button>
</Grid>
</Grid>
</AccordionDetails>
</Accordion>
</Box>
);
};

View File

@ -1,17 +1,20 @@
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Button, Grid, LinearProgress, Typography, useTheme } from '@mui/material';
import { Bolt, Logout } from '@mui/icons-material';
import { useHistory } from 'react-router-dom';
import { Button, Link, Grid, LinearProgress, Typography, Alert } from '@mui/material';
import { Bolt, Logout, Refresh } from '@mui/icons-material';
import RobotAvatar from '../../components/RobotAvatar';
import TokenInput from './TokenInput';
import { Page } from '../NavBar';
import { Robot } from '../../models';
import { genBase62Token } from '../../utils';
interface RobotProfileProps {
robot: Robot;
setRobot: (state: Robot) => void;
setView: (state: 'welcome' | 'onboarding' | 'recovery' | 'profile') => void;
inputToken: string;
setCurrentOrder: (state: number) => void;
logoutRobot: () => void;
setInputToken: (state: string) => void;
getGenerateRobot: (token: string) => void;
@ -27,7 +30,10 @@ const RobotProfile = ({
setRobot,
inputToken,
setInputToken,
setCurrentOrder,
getGenerateRobot,
logoutRobot,
setPage,
setView,
badRequest,
baseUrl,
@ -35,7 +41,12 @@ const RobotProfile = ({
width,
}: RobotProfileProps): JSX.Element => {
const { t } = useTranslation();
const theme = useTheme();
const history = useHistory();
const getNewRobot = () => {
logoutRobot();
setTimeout(() => getGenerateRobot(genBase62Token(36)), 10);
};
return (
<Grid container direction='column' alignItems='center' spacing={2} padding={2}>
@ -73,7 +84,7 @@ const RobotProfile = ({
</Typography>
) : (
<>
<b>{t('Rebuilding your robot!')}</b>
<b>{t('Building your robot!')}</b>
<LinearProgress />
</>
)}
@ -102,18 +113,60 @@ const RobotProfile = ({
<Grid item>
<Typography variant='subtitle2' color='primary'>
{t('Welcome back!')}
<br />
</Typography>
</Grid>
) : (
<></>
)}
{robot.activeOrderId ? (
<Grid item>
{/* This robot has an active order
This robot has a past order. Reusing Robot degrades your privacy: Get a new robot! */}
<Typography>
{t('One active order')}
<Link
onClick={() => {
history.push('/order/' + robot.activeOrderId);
setPage('order');
setCurrentOrder(robot.activeOrderId);
}}
>
{` #${robot.activeOrderId}`}
</Link>
</Typography>
</Grid>
) : null}
{robot.lastOrderId ? (
<Grid item>
<Typography align='center'>
{t('Your past order')}
<Link
onClick={() => {
history.push('/order/' + robot.lastOrderId);
setPage('order');
setCurrentOrder(robot.lastOrderId);
}}
>
{` #${robot.lastOrderId}`}
</Link>
</Typography>
<Alert severity='warning'>
<Grid container direction='column' alignItems='center'>
<Grid item>
{t(
'Reusing trading identity degrades your privacy against other users, coordinators and observers.',
)}
</Grid>
<Grid item sx={{ position: 'relative', right: '1em' }}>
<Button color='inherit' size='small' onClick={getNewRobot}>
<Refresh />
{t('Generate a new Robot')}
</Button>
</Grid>
</Grid>
</Alert>
</Grid>
) : null}
<Grid item sx={{ width: '100%' }}>
<TokenInput

View File

@ -1,6 +1,15 @@
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Paper, Grid, CircularProgress, Box, Alert, Typography, useTheme } from '@mui/material';
import {
Paper,
Grid,
CircularProgress,
Box,
Alert,
Typography,
useTheme,
AlertTitle,
} from '@mui/material';
import { useParams } from 'react-router-dom';
import { Page } from '../NavBar';
@ -134,13 +143,13 @@ const RobotPage = ({
};
const logoutRobot = () => {
setRobot({ ...robot, nickname: undefined, token: undefined, avatarLoaded: false });
setInputToken('');
setRobotFound(false);
systemClient.deleteCookie('sessionid');
systemClient.deleteItem('robot_token');
systemClient.deleteItem('pub_key');
systemClient.deleteItem('enc_priv_key');
setTimeout(() => setRobot(new Robot()), 10);
};
if (window?.NativeRobosats & (torStatus != 'DONE')) {
@ -154,7 +163,7 @@ const RobotPage = ({
>
<Grid container direction='column' alignItems='center' spacing={1} padding={2}>
<Grid item>
<Typography align='center' variant='h5'>
<Typography align='center' variant='h6'>
{t('Connecting to TOR')}
</Typography>
</Grid>
@ -182,7 +191,7 @@ const RobotPage = ({
</Grid>
<Grid item>
<Alert>
<b>{t('Your traffic is encrypted and annonimized using TOR. ')}</b>
<AlertTitle>{t('Connection encrypted and anonymized using TOR.')}</AlertTitle>
{t(
'This ensures maximum privacy, however you might feel the app behaves slow. If connection is lost, restart the app.',
)}
@ -226,6 +235,7 @@ const RobotPage = ({
robot={robot}
robotFound={robotFound}
setRobot={setRobot}
setCurrentOrder={setCurrentOrder}
badRequest={badRequest}
logoutRobot={logoutRobot}
width={width}

View File

@ -1,395 +0,0 @@
import React, { Component } from 'react';
import { withTranslation } from 'react-i18next';
import {
Button,
Tooltip,
Grid,
Typography,
TextField,
ButtonGroup,
CircularProgress,
IconButton,
} from '@mui/material';
import SmartToyIcon from '@mui/icons-material/SmartToy';
import CasinoIcon from '@mui/icons-material/Casino';
import ContentCopy from '@mui/icons-material/ContentCopy';
import BoltIcon from '@mui/icons-material/Bolt';
import DownloadIcon from '@mui/icons-material/Download';
import { RoboSatsNoTextIcon } from '../components/Icons';
import { sha256 } from 'js-sha256';
import { genBase62Token, tokenStrength, saveAsJson } from '../utils';
import { genKey } from '../pgp';
import { systemClient } from '../services/System';
import { apiClient } from '../services/api/index';
import RobotAvatar from '../components/RobotAvatar';
class UserGenPage extends Component {
constructor(props) {
super(props);
this.state = {
tokenHasChanged: false,
inputToken: '',
found: false,
};
this.refCode = this.props.match.params.refCode;
}
componentDidMount() {
// Checks in parent HomePage if there is already a nick and token
// Displays the existing one
if (this.props.robot.nickname != null) {
this.setState({ inputToken: this.props.robot.token });
} else if (this.props.robot.token) {
this.setState({ inputToken: this.props.robot.token });
this.getGeneratedUser(this.props.robot.token);
} else {
const newToken = genBase62Token(36);
this.setState({
inputToken: newToken,
});
this.getGeneratedUser(newToken);
}
}
getGeneratedUser = (token) => {
const strength = tokenStrength(token);
const refCode = this.refCode;
this.props.setRobot({ ...this.props.robot, loading: true, avatarLoaded: false });
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,
};
});
requestBody.then((body) =>
apiClient.post(this.props.baseUrl, '/api/user/', body).then((data) => {
this.setState({ found: data.found, bad_request: data.bad_request });
this.props.setCurrentOrder(
data.active_order_id
? data.active_order_id
: data.last_order_id
? data.last_order_id
: null,
);
// Add nick and token to App state (token only if not a bad request)
data.bad_request
? this.props.setRobot({
...this.props.robot,
avatarLoaded: true,
loading: false,
nickname: data.nickname ?? this.props.robot.nickname,
activeOrderId: data.active_order_id ?? null,
referralCode: data.referral_code ?? this.props.referralCode,
earnedRewards: data.earned_rewards ?? this.props.earnedRewards,
lastOrderId: data.last_order_id ?? this.props.lastOrderId,
stealthInvoices: data.wants_stealth ?? this.props.stealthInvoices,
tgEnabled: data.tg_enabled,
tgBotName: data.tg_bot_name,
tgToken: data.tg_token,
})
: this.props.setRobot({
...this.props.robot,
nickname: data.nickname,
token,
loading: 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 ?? 0,
stealthInvoices: data.wants_stealth,
tgEnabled: data.tg_enabled,
tgBotName: data.tg_bot_name,
tgToken: data.tg_token,
bitsEntropy: data.token_bits_entropy,
shannonEntropy: data.token_shannon_entropy,
pubKey: data.public_key,
encPrivKey: data.encrypted_private_key,
copiedToken: data.found ? true : this.props.robot.copiedToken,
}) &
systemClient.setItem('robot_token', token) &
systemClient.setItem('pub_key', data.public_key.split('\n').join('\\')) &
systemClient.setItem('enc_priv_key', data.encrypted_private_key.split('\n').join('\\'));
}),
);
};
delGeneratedUser() {
apiClient.delete(this.props.baseUrl, '/api/user');
systemClient.deleteCookie('sessionid');
systemClient.deleteItem('robot_token');
systemClient.deleteItem('pub_key');
systemClient.deleteItem('enc_priv_key');
}
handleClickNewRandomToken = () => {
const inputToken = genBase62Token(36);
this.setState({
inputToken,
tokenHasChanged: true,
});
this.props.setRobot({ ...this.props.robot, copiedToken: true });
};
handleChangeToken = (e) => {
this.setState({
inputToken: e.target.value.split(' ').join(''),
tokenHasChanged: true,
});
};
handleClickSubmitToken = () => {
this.delGeneratedUser();
this.getGeneratedUser(this.state.inputToken);
this.setState({ tokenHasChanged: false });
this.props.setRobot({
...this.props.robot,
avatarLoaded: false,
nickname: null,
token: null,
copiedToken: false,
lastOrderId: null,
activeOrderId: null,
});
};
createJsonFile = () => {
return {
token: this.props.robot.token,
token_shannon_entropy: this.props.robot.shannonEntropy,
token_bit_entropy: this.props.robot.bitsEntropy,
public_key: this.props.robot.pub_key,
encrypted_private_key: this.props.robot.enc_priv_key,
};
};
render() {
const { t, i18n } = this.props;
const fontSize = this.props.theme.typography.fontSize;
const fontSizeFactor = fontSize / 14; // to scale sizes, default fontSize is 14
return (
<Grid container spacing={1}>
<Grid item>
<div className='clickTrough' />
</Grid>
<Grid
item
xs={12}
align='center'
sx={{ width: 370 * fontSizeFactor, height: 260 * fontSizeFactor }}
>
{this.props.robot.avatarLoaded && this.props.robot.nickname ? (
<div>
<Grid item xs={12} align='center'>
<Typography component='h5' variant='h5'>
<b>
{this.props.robot.nickname && systemClient.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.props.robot.nickname}</a>
<BoltIcon
sx={{
color: '#fcba03',
height: 33 * fontSizeFactor,
width: 33 * fontSizeFactor,
}}
/>
</div>
) : (
''
)}
</b>
</Typography>
</Grid>
<Grid item xs={12} align='center'>
<RobotAvatar
nickname={this.props.robot.nickname}
smooth={true}
style={{ maxWidth: 203 * fontSizeFactor, maxHeight: 203 * fontSizeFactor }}
imageStyle={{
transform: '',
border: '2px solid #555',
filter: 'drop-shadow(1px 1px 1px #000000)',
height: `${201 * fontSizeFactor}px`,
width: `${201 * fontSizeFactor}px`,
}}
tooltip={t('This is your trading avatar')}
tooltipPosition='top'
baseUrl={this.props.baseUrl}
/>
<br />
</Grid>
</div>
) : (
<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>
) : (
''
)}
<Grid container align='center'>
<Grid item xs={12} align='center'>
<TextField
sx={{ maxWidth: 280 * fontSizeFactor }}
error={!!this.state.bad_request}
label={t('Store your token safely')}
required={true}
value={this.state.inputToken}
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,
}}
>
<Grid container>
<Grid item xs={6}>
<Tooltip
enterTouchDelay={250}
title={t('Save token and PGP credentials to file')}
>
<span>
<IconButton
color='primary'
disabled={
!this.props.robot.avatarLoaded ||
!(systemClient.getItem('robot_token') == this.state.inputToken)
}
onClick={() =>
saveAsJson(
this.props.robot.nickname + '.json',
this.createJsonFile(),
)
}
>
<DownloadIcon
sx={{ width: 22 * fontSizeFactor, height: 22 * fontSizeFactor }}
/>
</IconButton>
</span>
</Tooltip>
</Grid>
<Grid item xs={6}>
<Tooltip disableHoverListener enterTouchDelay={0} title={t('Copied!')}>
<IconButton
color={this.props.robot.copiedToken ? 'inherit' : 'primary'}
disabled={
!this.props.robot.avatarLoaded ||
!(systemClient.getItem('robot_token') === this.state.inputToken)
}
onClick={() =>
systemClient.copyToClipboard(systemClient.getItem('robot_token')) &
this.props.setRobot({ ...this.props.robot, copiedToken: true })
}
>
<ContentCopy
sx={{ width: 18 * fontSizeFactor, height: 18 * fontSizeFactor }}
/>
</IconButton>
</Tooltip>
</Grid>
</Grid>
</div>
),
endAdornment: (
<Tooltip enterTouchDelay={250} title={t('Generate a new token')}>
<IconButton onClick={this.handleClickNewRandomToken}>
<CasinoIcon
sx={{ width: 18 * fontSizeFactor, height: 18 * fontSizeFactor }}
/>
</IconButton>
</Tooltip>
),
}}
/>
</Grid>
</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>
) : (
<Tooltip
enterTouchDelay={0}
enterDelay={500}
enterNextDelay={2000}
title={t('You must enter a new token first')}
>
<div>
<Button disabled={true} type='submit' size='small'>
<SmartToyIcon sx={{ width: 18 * fontSizeFactor, height: 18 * fontSizeFactor }} />
<span>{t('Generate Robot')}</span>
</Button>
</div>
</Tooltip>
)}
</Grid>
<Grid item xs={12} align='center' sx={{ width: '26.43em' }}>
<Grid item>
<div style={{ height: '2.143em' }} />
</Grid>
<div style={{ width: '26.43em', left: '2.143em' }}>
<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: '3.143em', width: '3.143em' }} />
</Grid>
</Grid>
</div>
</Grid>
</Grid>
);
}
}
export default withTranslation()(UserGenPage);