Refactor utils

This commit is contained in:
Reckless_Satoshi 2022-10-26 04:16:06 -07:00
parent b4205761fe
commit d3d0f3ee1a
No known key found for this signature in database
GPG Key ID: 9C4585B561315571
24 changed files with 685 additions and 688 deletions

View File

@ -1,642 +0,0 @@
import React, { Component } from 'react';
import { withTranslation } from 'react-i18next';
import {
Badge,
Tooltip,
ListItemAvatar,
Paper,
Grid,
IconButton,
Select,
MenuItem,
ListItemText,
ListItem,
ListItemIcon,
ListItemButton,
} from '@mui/material';
import MediaQuery from 'react-responsive';
import Flags from 'country-flag-icons/react/3x2';
import { Link as LinkRouter } from 'react-router-dom';
import { apiClient } from '../services/api';
import { systemClient } from '../services/System';
import RobotAvatar from '../components/RobotAvatar';
// Icons
import BarChartIcon from '@mui/icons-material/BarChart';
import PeopleIcon from '@mui/icons-material/People';
import InventoryIcon from '@mui/icons-material/Inventory';
import SellIcon from '@mui/icons-material/Sell';
import SmartToyIcon from '@mui/icons-material/SmartToy';
import PercentIcon from '@mui/icons-material/Percent';
import PriceChangeIcon from '@mui/icons-material/PriceChange';
// Missing flags
import { CataloniaFlag, BasqueCountryFlag } from '../components/Icons';
import {
CommunityDialog,
ExchangeSummaryDialog,
ProfileDialog,
StatsDialog,
UpdateClientDialog,
} from '../components/Dialogs';
class BottomBar extends Component {
constructor(props) {
super(props);
this.state = {
profileShown: false,
openStatsForNerds: false,
openCommunity: false,
openExchangeSummary: false,
openClaimRewards: false,
openProfile: false,
showRewards: false,
rewardInvoice: null,
badInvoice: false,
showRewardsSpinner: false,
withdrawn: false,
};
}
handleClickOpenStatsForNerds = () => {
this.setState({ openStatsForNerds: true });
};
handleClickCloseStatsForNerds = () => {
this.setState({ openStatsForNerds: false });
};
handleClickOpenCommunity = () => {
this.setState({ openCommunity: true });
};
handleClickCloseCommunity = () => {
this.setState({ openCommunity: false });
};
handleClickOpenProfile = () => {
this.setState({ openProfile: true, profileShown: true });
};
handleClickCloseProfile = () => {
this.setState({ openProfile: false });
};
handleClickOpenExchangeSummary = () => {
this.setState({ openExchangeSummary: true });
};
handleClickCloseExchangeSummary = () => {
this.setState({ openExchangeSummary: false });
};
handleSubmitInvoiceClicked = (e, rewardInvoice) => {
this.setState({ badInvoice: false, showRewardsSpinner: true });
apiClient
.post('/api/reward/', {
invoice: rewardInvoice,
})
.then((data) => {
this.setState({ badInvoice: data.bad_invoice, showRewardsSpinner: false });
this.props.setInfo({
...this.props.info,
badInvoice: data.bad_invoice,
openClaimRewards: !data.successful_withdrawal,
withdrawn: !!data.successful_withdrawal,
showRewardsSpinner: false,
});
this.props.setRobot({
...this.props.robot,
earnedRewards: data.successful_withdrawal ? 0 : this.props.robot.earnedRewards,
});
});
e.preventDefault();
};
handleSetStealthInvoice = (wantsStealth) => {
apiClient
.put('/api/stealth/', { wantsStealth })
.then((data) =>
this.props.setRobot({ ...this.props.robot, stealthInvoices: data?.wantsStealth }),
);
};
getHost() {
const url =
window.location != window.parent.location
? this.getHost(document.referrer)
: document.location.href;
return url.split('/')[2];
}
showProfileButton = () => {
return (
this.props.robot.avatarLoaded &&
(this.props.robot.token
? systemClient.getCookie('robot_token') === this.props.robot.token
: true) &&
systemClient.getCookie('sessionid')
);
};
bottomBarDesktop = () => {
const { t } = this.props;
const hasRewards = this.props.robot.earnedRewards > 0;
const hasOrder = !!(
(this.props.robot.activeOrderId > 0) &
!this.state.profileShown &
this.props.robot.avatarLoaded
);
const fontSize = this.props.theme.typography.fontSize;
const fontSizeFactor = fontSize / 14; // default fontSize is 14
const typographyProps = {
primaryTypographyProps: { fontSize },
secondaryTypographyProps: { fontSize: (fontSize * 12) / 14 },
};
return (
<Paper
elevation={6}
style={{ height: '2.5em', width: `${(this.props.windowSize.width / 16) * 14}em` }}
>
<Grid container>
<Grid item xs={1.9}>
<div style={{ display: this.showProfileButton() ? '' : 'none' }}>
<ListItemButton onClick={this.handleClickOpenProfile}>
<Tooltip
open={(hasRewards || hasOrder) && this.showProfileButton()}
title={
(hasRewards ? t('You can claim satoshis!') + ' ' : '') +
(hasOrder ? t('You have an active order') : '')
}
>
<ListItemAvatar sx={{ width: 30 * fontSizeFactor, height: 30 * fontSizeFactor }}>
<RobotAvatar
style={{ marginTop: -13 }}
statusColor={
(this.props.robot.activeOrderId > 0) & !this.state.profileShown
? 'primary'
: undefined
}
nickname={this.props.robot.nickname}
onLoad={() =>
this.props.setRobot({ ...this.props.robot, avatarLoaded: true })
}
/>
</ListItemAvatar>
</Tooltip>
<ListItemText primary={this.props.robot.nickname} />
</ListItemButton>
</div>
</Grid>
<Grid item xs={1.9}>
<ListItem className='bottomItem'>
<ListItemIcon size='small'>
<IconButton
disabled={!this.showProfileButton()}
color='primary'
to={`/book/`}
component={LinkRouter}
>
<InventoryIcon />
</IconButton>
</ListItemIcon>
<ListItemText
{...typographyProps}
primary={this.props.info.num_public_buy_orders}
secondary={t('Public Buy Orders')}
/>
</ListItem>
</Grid>
<Grid item xs={1.9}>
<ListItem className='bottomItem'>
<ListItemIcon size='small'>
<IconButton
disabled={!this.showProfileButton()}
color='primary'
to={`/book/`}
component={LinkRouter}
>
<SellIcon />
</IconButton>
</ListItemIcon>
<ListItemText
{...typographyProps}
primary={this.props.info.num_public_sell_orders}
secondary={t('Public Sell Orders')}
/>
</ListItem>
</Grid>
<Grid item xs={1.9}>
<ListItem className='bottomItem'>
<ListItemIcon size='small'>
<IconButton
disabled={!this.showProfileButton()}
color='primary'
to={`/`}
component={LinkRouter}
>
<SmartToyIcon />
</IconButton>
</ListItemIcon>
<ListItemText
{...typographyProps}
primary={this.props.info.active_robots_today}
secondary={t('Today Active Robots')}
/>
</ListItem>
</Grid>
<Grid item xs={1.9}>
<ListItem className='bottomItem'>
<ListItemIcon size='small'>
<IconButton color='primary' onClick={this.handleClickOpenExchangeSummary}>
<PriceChangeIcon />
</IconButton>
</ListItemIcon>
<ListItemText
{...typographyProps}
primary={this.props.info.last_day_nonkyc_btc_premium + '%'}
secondary={t('24h Avg Premium')}
/>
</ListItem>
</Grid>
<Grid item xs={1.5}>
<ListItem className='bottomItem'>
<ListItemIcon size='small'>
<IconButton color='primary' onClick={this.handleClickOpenExchangeSummary}>
<PercentIcon />
</IconButton>
</ListItemIcon>
<ListItemText
{...typographyProps}
primary={(this.props.info.maker_fee + this.props.info.taker_fee) * 100}
secondary={t('Trade Fee')}
/>
</ListItem>
</Grid>
<Grid container item xs={1}>
<Grid item xs={6}>
{this.LangSelect()}
</Grid>
<Grid item xs={3}>
<Tooltip enterTouchDelay={250} title={t('Show community and support links')}>
<IconButton
color='primary'
aria-label='Community'
onClick={this.handleClickOpenCommunity}
>
<PeopleIcon />
</IconButton>
</Tooltip>
</Grid>
<Grid item xs={3}>
<Tooltip enterTouchDelay={250} title={t('Show stats for nerds')}>
<IconButton
color='primary'
aria-label='Stats for Nerds'
onClick={this.handleClickOpenStatsForNerds}
>
<BarChartIcon />
</IconButton>
</Tooltip>
</Grid>
</Grid>
</Grid>
</Paper>
);
};
handleChangeLang = (e) => {
const { i18n } = this.props;
i18n.changeLanguage(e.target.value);
};
LangSelect = () => {
const { i18n } = this.props;
const lang = i18n.resolvedLanguage == null ? 'en' : i18n.resolvedLanguage.substring(0, 2);
const flagProps = {
width: 20,
height: 20,
};
return (
<Select
size='small'
value={lang}
inputProps={{
style: { textAlign: 'center' },
}}
renderValue={(value) => value.toUpperCase()}
onChange={this.handleChangeLang}
>
<MenuItem value={'en'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.US {...flagProps} />
</div>
EN
</MenuItem>
<MenuItem value={'es'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.ES {...flagProps} />
</div>
ES
</MenuItem>
<MenuItem value={'de'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.DE {...flagProps} />
</div>
DE
</MenuItem>
<MenuItem value={'pl'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.PL {...flagProps} />
</div>
PL
</MenuItem>
<MenuItem value={'fr'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.FR {...flagProps} />
</div>
FR
</MenuItem>
<MenuItem value={'ru'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.RU {...flagProps} />
</div>
RU
</MenuItem>
<MenuItem value={'it'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.IT {...flagProps} />
</div>
IT
</MenuItem>
<MenuItem value={'pt'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.BR {...flagProps} />
</div>
PT
</MenuItem>
<MenuItem value={'zh-si'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.CN {...flagProps} />
</div>
简体
</MenuItem>
<MenuItem value={'zh-tr'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.CN {...flagProps} />
</div>
繁體
</MenuItem>
<MenuItem value={'sv'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.SE {...flagProps} />
</div>
SV
</MenuItem>
<MenuItem value={'cs'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.CZ {...flagProps} />
</div>
CS
</MenuItem>
<MenuItem value={'th'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.TH {...flagProps} />
</div>
TH
</MenuItem>
<MenuItem value={'ca'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<CataloniaFlag {...flagProps} />
</div>
CA
</MenuItem>
<MenuItem value={'eu'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<BasqueCountryFlag {...flagProps} />
</div>
EU
</MenuItem>
</Select>
);
};
bottomBarPhone = () => {
const { t } = this.props;
const hasRewards = this.props.robot.earnedRewards > 0;
const hasOrder = !!(
(this.props.info.active_order_id > 0) &
!this.state.profileShown &
this.props.robot.avatarLoaded
);
return (
<Paper
elevation={6}
style={{ height: '2.85em', width: `${(this.props.windowSize.width / 16) * 14}em` }}
>
<Grid container>
<Grid item xs={1.6}>
<div style={{ display: this.showProfileButton() ? '' : 'none' }}>
<Tooltip
open={(hasRewards || hasOrder) && this.showProfileButton()}
title={
(hasRewards ? t('You can claim satoshis!') + ' ' : '') +
(hasOrder ? t('You have an active order') : '')
}
>
<IconButton
onClick={this.handleClickOpenProfile}
sx={{ margin: 0, bottom: 17, right: 8 }}
>
<RobotAvatar
style={{ width: 55, height: 55 }}
avatarClass='phoneFlippedSmallAvatar'
statusColor={
(this.props.activeOrderId > 0) & !this.state.profileShown
? 'primary'
: undefined
}
nickname={this.props.robot.nickname}
onLoad={() => this.props.setRobot({ ...this.props.robot, avatarLoaded: true })}
/>
</IconButton>
</Tooltip>
</div>
</Grid>
<Grid item xs={1.6} align='center'>
<Tooltip enterTouchDelay={300} title={t('Number of public BUY orders')}>
<IconButton
disabled={!this.showProfileButton()}
color='primary'
to={`/book/`}
component={LinkRouter}
>
<Badge badgeContent={this.props.info.num_public_buy_orders} color='action'>
<InventoryIcon />
</Badge>
</IconButton>
</Tooltip>
</Grid>
<Grid item xs={1.6} align='center'>
<Tooltip enterTouchDelay={300} title={t('Number of public SELL orders')}>
<IconButton
disabled={!this.showProfileButton()}
color='primary'
to={`/book/`}
component={LinkRouter}
>
<Badge badgeContent={this.props.info.num_public_sell_orders} color='action'>
<SellIcon />
</Badge>
</IconButton>
</Tooltip>
</Grid>
<Grid item xs={1.6} align='center'>
<Tooltip enterTouchDelay={300} title={t('Today active robots')}>
<IconButton
disabled={!this.showProfileButton()}
color='primary'
to={`/`}
component={LinkRouter}
>
<Badge badgeContent={this.props.info.active_robots_today} color='action'>
<SmartToyIcon />
</Badge>
</IconButton>
</Tooltip>
</Grid>
<Grid item xs={1.8} align='center'>
<Tooltip enterTouchDelay={300} title={t('24h non-KYC bitcoin premium')}>
<IconButton color='primary' onClick={this.handleClickOpenExchangeSummary}>
<Badge
badgeContent={this.props.info.last_day_nonkyc_btc_premium + '%'}
color='action'
>
<PriceChangeIcon />
</Badge>
</IconButton>
</Tooltip>
</Grid>
<Grid container item xs={3.8}>
<Grid item xs={6}>
{this.LangSelect()}
</Grid>
<Grid item xs={3}>
<Tooltip enterTouchDelay={250} title={t('Show community and support links')}>
<IconButton
color='primary'
aria-label='Community'
onClick={this.handleClickOpenCommunity}
>
<PeopleIcon />
</IconButton>
</Tooltip>
</Grid>
<Grid item xs={3}>
<Tooltip enterTouchDelay={250} title={t('Show stats for nerds')}>
<IconButton
color='primary'
aria-label='Stats for Nerds'
onClick={() => this.props.fetchInfo()}
onClick={this.handleClickOpenStatsForNerds}
>
<BarChartIcon />
</IconButton>
</Tooltip>
</Grid>
</Grid>
</Grid>
</Paper>
);
};
render() {
return (
<div>
<CommunityDialog
open={this.state.openCommunity}
handleClickCloseCommunity={this.handleClickCloseCommunity}
/>
<UpdateClientDialog
open={this.state.openUpdateClient}
coordinatorVersion={this.props.info.coordinatorVersion}
clientVersion={this.props.info.clientVersion}
handleClickClose={() =>
this.props.setInfo({ ...this.props.info, openUpdateClient: false })
}
/>
<ExchangeSummaryDialog
open={this.state.openExchangeSummary}
handleClickCloseExchangeSummary={this.handleClickCloseExchangeSummary}
numPublicBuyOrders={this.props.info.num_public_buy_orders}
numPublicSellOrders={this.props.info.num_public_sell_orders}
bookLiquidity={this.props.info.book_liquidity}
activeRobotsToday={this.props.info.active_robots_today}
lastDayNonkycBtcPremium={this.props.info.last_day_nonkyc_btc_premium}
makerFee={this.props.info.maker_fee}
takerFee={this.props.info.taker_fee}
swapFeeRate={this.props.info.current_swap_fee_rate}
/>
<ProfileDialog
open={this.state.openProfile}
handleClickCloseProfile={this.handleClickCloseProfile}
nickname={this.props.robot.nickname}
activeOrderId={this.props.robot.activeOrderId}
lastOrderId={this.props.robot.lastOrderId}
referralCode={this.props.robot.referralCode}
tgEnabled={this.props.robot.tgEnabled}
tgBotName={this.props.robot.tgBotName}
tgToken={this.props.robot.tgToken}
handleSubmitInvoiceClicked={this.handleSubmitInvoiceClicked}
host={this.getHost()}
showRewardsSpinner={this.state.showRewardsSpinner}
withdrawn={this.props.info.withdrawn}
badInvoice={this.props.info.badInvoice}
earnedRewards={this.props.robot.earnedRewards}
updateRobot={(newParam) => this.props.setRobot({ ...robot, ...newParam })}
stealthInvoices={this.props.robot.stealthInvoices}
handleSetStealthInvoice={this.handleSetStealthInvoice}
/>
<StatsDialog
open={this.state.openStatsForNerds}
handleClickCloseStatsForNerds={this.handleClickCloseStatsForNerds}
coordinatorVersion={this.props.info.coordinatorVersion}
clientVersion={this.props.info.clientVersion}
lndVersion={this.props.info.lnd_version}
network={this.props.info.network}
nodeAlias={this.props.info.node_alias}
nodeId={this.props.info.node_id}
alternativeName={this.props.info.alternative_name}
alternativeSite={this.props.info.alternative_site}
commitHash={this.props.info.robosats_running_commit_hash}
lastDayVolume={this.props.info.last_day_volume}
lifetimeVolume={this.props.info.lifetime_volume}
/>
<MediaQuery minWidth={1200}>{this.bottomBarDesktop()}</MediaQuery>
<MediaQuery maxWidth={1199}>{this.bottomBarPhone()}</MediaQuery>
</div>
);
}
}
export default withTranslation()(BottomBar);

View File

@ -6,11 +6,11 @@ import UserGenPage from './UserGenPage';
import MakerPage from './MakerPage';
import BookPage from './BookPage';
import OrderPage from './OrderPage';
import BottomBar from './BottomBar';
import NavBar from './NavBar';
import { LearnDialog } from '../components/Dialogs';
import { apiClient } from '../services/api';
import checkVer from '../utils/checkVer';
import { checkVer } from '../utils';
import {
Book,
@ -211,7 +211,7 @@ const Main = ({ settings, setSettings }: MainProps): JSX.Element => {
bottom: 0,
}}
>
<BottomBar
<NavBar
theme={theme}
windowSize={windowSize}
redirectTo={(location: string) => history.push(location)}

View File

@ -5,7 +5,7 @@ import { Button, Grid, Paper, Collapse, Typography } from '@mui/material';
import { LimitList, Maker, Book, Favorites } from '../../models';
import filterOrders from '../../utils/filterOrders';
import { filterOrders } from '../../utils';
import MakerForm from '../../components/MakerForm';
import BookTable from '../../components/BookTable';

View File

@ -0,0 +1,633 @@
import React, { Component } from 'react';
import { withTranslation } from 'react-i18next';
import {
Badge,
Tooltip,
ListItemAvatar,
Paper,
Grid,
IconButton,
Select,
MenuItem,
ListItemText,
ListItem,
ListItemIcon,
ListItemButton,
} from '@mui/material';
import MediaQuery from 'react-responsive';
import Flags from 'country-flag-icons/react/3x2';
import { Link as LinkRouter } from 'react-router-dom';
import { apiClient } from '../../services/api';
import { systemClient } from '../../services/System';
import RobotAvatar from '../../components/RobotAvatar';
// Icons
import BarChartIcon from '@mui/icons-material/BarChart';
import PeopleIcon from '@mui/icons-material/People';
import InventoryIcon from '@mui/icons-material/Inventory';
import SellIcon from '@mui/icons-material/Sell';
import SmartToyIcon from '@mui/icons-material/SmartToy';
import PercentIcon from '@mui/icons-material/Percent';
import PriceChangeIcon from '@mui/icons-material/PriceChange';
// Missing flags
import { CataloniaFlag, BasqueCountryFlag } from '../../components/Icons';
import {
CommunityDialog,
ExchangeSummaryDialog,
ProfileDialog,
StatsDialog,
UpdateClientDialog,
} from '../../components/Dialogs';
class NavBar extends Component {
constructor(props) {
super(props);
this.state = {
profileShown: false,
openStatsForNerds: false,
openCommunity: false,
openExchangeSummary: false,
openClaimRewards: false,
openProfile: false,
showRewards: false,
rewardInvoice: null,
badInvoice: false,
showRewardsSpinner: false,
withdrawn: false,
};
}
handleClickOpenStatsForNerds = () => {
this.setState({ openStatsForNerds: true });
};
handleClickCloseStatsForNerds = () => {
this.setState({ openStatsForNerds: false });
};
handleClickOpenCommunity = () => {
this.setState({ openCommunity: true });
};
handleClickCloseCommunity = () => {
this.setState({ openCommunity: false });
};
handleClickOpenProfile = () => {
this.setState({ openProfile: true, profileShown: true });
};
handleClickCloseProfile = () => {
this.setState({ openProfile: false });
};
handleClickOpenExchangeSummary = () => {
this.setState({ openExchangeSummary: true });
};
handleClickCloseExchangeSummary = () => {
this.setState({ openExchangeSummary: false });
};
handleSubmitInvoiceClicked = (e, rewardInvoice) => {
this.setState({ badInvoice: false, showRewardsSpinner: true });
apiClient
.post('/api/reward/', {
invoice: rewardInvoice,
})
.then((data) => {
this.setState({ badInvoice: data.bad_invoice, showRewardsSpinner: false });
this.props.setInfo({
...this.props.info,
badInvoice: data.bad_invoice,
openClaimRewards: !data.successful_withdrawal,
withdrawn: !!data.successful_withdrawal,
showRewardsSpinner: false,
});
this.props.setRobot({
...this.props.robot,
earnedRewards: data.successful_withdrawal ? 0 : this.props.robot.earnedRewards,
});
});
e.preventDefault();
};
handleSetStealthInvoice = (wantsStealth) => {
apiClient
.put('/api/stealth/', { wantsStealth })
.then((data) =>
this.props.setRobot({ ...this.props.robot, stealthInvoices: data?.wantsStealth }),
);
};
showProfileButton = () => {
return (
this.props.robot.avatarLoaded &&
(this.props.robot.token
? systemClient.getCookie('robot_token') === this.props.robot.token
: true) &&
systemClient.getCookie('sessionid')
);
};
bottomBarDesktop = () => {
const { t } = this.props;
const hasRewards = this.props.robot.earnedRewards > 0;
const hasOrder = !!(
(this.props.robot.activeOrderId > 0) &
!this.state.profileShown &
this.props.robot.avatarLoaded
);
const fontSize = this.props.theme.typography.fontSize;
const fontSizeFactor = fontSize / 14; // default fontSize is 14
const typographyProps = {
primaryTypographyProps: { fontSize },
secondaryTypographyProps: { fontSize: (fontSize * 12) / 14 },
};
return (
<Paper
elevation={6}
style={{ height: '2.5em', width: `${(this.props.windowSize.width / 16) * 14}em` }}
>
<Grid container>
<Grid item xs={1.9}>
<div style={{ display: this.showProfileButton() ? '' : 'none' }}>
<ListItemButton onClick={this.handleClickOpenProfile}>
<Tooltip
open={(hasRewards || hasOrder) && this.showProfileButton()}
title={
(hasRewards ? t('You can claim satoshis!') + ' ' : '') +
(hasOrder ? t('You have an active order') : '')
}
>
<ListItemAvatar sx={{ width: 30 * fontSizeFactor, height: 30 * fontSizeFactor }}>
<RobotAvatar
style={{ marginTop: -13 }}
statusColor={
(this.props.robot.activeOrderId > 0) & !this.state.profileShown
? 'primary'
: undefined
}
nickname={this.props.robot.nickname}
onLoad={() =>
this.props.setRobot({ ...this.props.robot, avatarLoaded: true })
}
/>
</ListItemAvatar>
</Tooltip>
<ListItemText primary={this.props.robot.nickname} />
</ListItemButton>
</div>
</Grid>
<Grid item xs={1.9}>
<ListItem className='bottomItem'>
<ListItemIcon size='small'>
<IconButton
disabled={!this.showProfileButton()}
color='primary'
to={`/book/`}
component={LinkRouter}
>
<InventoryIcon />
</IconButton>
</ListItemIcon>
<ListItemText
{...typographyProps}
primary={this.props.info.num_public_buy_orders}
secondary={t('Public Buy Orders')}
/>
</ListItem>
</Grid>
<Grid item xs={1.9}>
<ListItem className='bottomItem'>
<ListItemIcon size='small'>
<IconButton
disabled={!this.showProfileButton()}
color='primary'
to={`/book/`}
component={LinkRouter}
>
<SellIcon />
</IconButton>
</ListItemIcon>
<ListItemText
{...typographyProps}
primary={this.props.info.num_public_sell_orders}
secondary={t('Public Sell Orders')}
/>
</ListItem>
</Grid>
<Grid item xs={1.9}>
<ListItem className='bottomItem'>
<ListItemIcon size='small'>
<IconButton
disabled={!this.showProfileButton()}
color='primary'
to={`/`}
component={LinkRouter}
>
<SmartToyIcon />
</IconButton>
</ListItemIcon>
<ListItemText
{...typographyProps}
primary={this.props.info.active_robots_today}
secondary={t('Today Active Robots')}
/>
</ListItem>
</Grid>
<Grid item xs={1.9}>
<ListItem className='bottomItem'>
<ListItemIcon size='small'>
<IconButton color='primary' onClick={this.handleClickOpenExchangeSummary}>
<PriceChangeIcon />
</IconButton>
</ListItemIcon>
<ListItemText
{...typographyProps}
primary={this.props.info.last_day_nonkyc_btc_premium + '%'}
secondary={t('24h Avg Premium')}
/>
</ListItem>
</Grid>
<Grid item xs={1.5}>
<ListItem className='bottomItem'>
<ListItemIcon size='small'>
<IconButton color='primary' onClick={this.handleClickOpenExchangeSummary}>
<PercentIcon />
</IconButton>
</ListItemIcon>
<ListItemText
{...typographyProps}
primary={(this.props.info.maker_fee + this.props.info.taker_fee) * 100}
secondary={t('Trade Fee')}
/>
</ListItem>
</Grid>
<Grid container item xs={1}>
<Grid item xs={6}>
{this.LangSelect()}
</Grid>
<Grid item xs={3}>
<Tooltip enterTouchDelay={250} title={t('Show community and support links')}>
<IconButton
color='primary'
aria-label='Community'
onClick={this.handleClickOpenCommunity}
>
<PeopleIcon />
</IconButton>
</Tooltip>
</Grid>
<Grid item xs={3}>
<Tooltip enterTouchDelay={250} title={t('Show stats for nerds')}>
<IconButton
color='primary'
aria-label='Stats for Nerds'
onClick={this.handleClickOpenStatsForNerds}
>
<BarChartIcon />
</IconButton>
</Tooltip>
</Grid>
</Grid>
</Grid>
</Paper>
);
};
handleChangeLang = (e) => {
const { i18n } = this.props;
i18n.changeLanguage(e.target.value);
};
LangSelect = () => {
const { i18n } = this.props;
const lang = i18n.resolvedLanguage == null ? 'en' : i18n.resolvedLanguage.substring(0, 2);
const flagProps = {
width: 20,
height: 20,
};
return (
<Select
size='small'
value={lang}
inputProps={{
style: { textAlign: 'center' },
}}
renderValue={(value) => value.toUpperCase()}
onChange={this.handleChangeLang}
>
<MenuItem value={'en'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.US {...flagProps} />
</div>
EN
</MenuItem>
<MenuItem value={'es'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.ES {...flagProps} />
</div>
ES
</MenuItem>
<MenuItem value={'de'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.DE {...flagProps} />
</div>
DE
</MenuItem>
<MenuItem value={'pl'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.PL {...flagProps} />
</div>
PL
</MenuItem>
<MenuItem value={'fr'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.FR {...flagProps} />
</div>
FR
</MenuItem>
<MenuItem value={'ru'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.RU {...flagProps} />
</div>
RU
</MenuItem>
<MenuItem value={'it'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.IT {...flagProps} />
</div>
IT
</MenuItem>
<MenuItem value={'pt'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.BR {...flagProps} />
</div>
PT
</MenuItem>
<MenuItem value={'zh-si'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.CN {...flagProps} />
</div>
</MenuItem>
<MenuItem value={'zh-tr'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.CN {...flagProps} />
</div>
</MenuItem>
<MenuItem value={'sv'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.SE {...flagProps} />
</div>
SV
</MenuItem>
<MenuItem value={'cs'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.CZ {...flagProps} />
</div>
CS
</MenuItem>
<MenuItem value={'th'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<Flags.TH {...flagProps} />
</div>
TH
</MenuItem>
<MenuItem value={'ca'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<CataloniaFlag {...flagProps} />
</div>
CA
</MenuItem>
<MenuItem value={'eu'}>
<div style={{ width: 24, position: 'relative', top: 3 }}>
<BasqueCountryFlag {...flagProps} />
</div>
EU
</MenuItem>
</Select>
);
};
bottomBarPhone = () => {
const { t } = this.props;
const hasRewards = this.props.robot.earnedRewards > 0;
const hasOrder = !!(
(this.props.info.active_order_id > 0) &
!this.state.profileShown &
this.props.robot.avatarLoaded
);
return (
<Paper
elevation={6}
style={{ height: '2.85em', width: `${(this.props.windowSize.width / 16) * 14}em` }}
>
<Grid container>
<Grid item xs={1.6}>
<div style={{ display: this.showProfileButton() ? '' : 'none' }}>
<Tooltip
open={(hasRewards || hasOrder) && this.showProfileButton()}
title={
(hasRewards ? t('You can claim satoshis!') + ' ' : '') +
(hasOrder ? t('You have an active order') : '')
}
>
<IconButton
onClick={this.handleClickOpenProfile}
sx={{ margin: 0, bottom: 17, right: 8 }}
>
<RobotAvatar
style={{ width: 55, height: 55 }}
avatarClass='phoneFlippedSmallAvatar'
statusColor={
(this.props.activeOrderId > 0) & !this.state.profileShown
? 'primary'
: undefined
}
nickname={this.props.robot.nickname}
onLoad={() => this.props.setRobot({ ...this.props.robot, avatarLoaded: true })}
/>
</IconButton>
</Tooltip>
</div>
</Grid>
<Grid item xs={1.6} align='center'>
<Tooltip enterTouchDelay={300} title={t('Number of public BUY orders')}>
<IconButton
disabled={!this.showProfileButton()}
color='primary'
to={`/book/`}
component={LinkRouter}
>
<Badge badgeContent={this.props.info.num_public_buy_orders} color='action'>
<InventoryIcon />
</Badge>
</IconButton>
</Tooltip>
</Grid>
<Grid item xs={1.6} align='center'>
<Tooltip enterTouchDelay={300} title={t('Number of public SELL orders')}>
<IconButton
disabled={!this.showProfileButton()}
color='primary'
to={`/book/`}
component={LinkRouter}
>
<Badge badgeContent={this.props.info.num_public_sell_orders} color='action'>
<SellIcon />
</Badge>
</IconButton>
</Tooltip>
</Grid>
<Grid item xs={1.6} align='center'>
<Tooltip enterTouchDelay={300} title={t('Today active robots')}>
<IconButton
disabled={!this.showProfileButton()}
color='primary'
to={`/`}
component={LinkRouter}
>
<Badge badgeContent={this.props.info.active_robots_today} color='action'>
<SmartToyIcon />
</Badge>
</IconButton>
</Tooltip>
</Grid>
<Grid item xs={1.8} align='center'>
<Tooltip enterTouchDelay={300} title={t('24h non-KYC bitcoin premium')}>
<IconButton color='primary' onClick={this.handleClickOpenExchangeSummary}>
<Badge
badgeContent={this.props.info.last_day_nonkyc_btc_premium + '%'}
color='action'
>
<PriceChangeIcon />
</Badge>
</IconButton>
</Tooltip>
</Grid>
<Grid container item xs={3.8}>
<Grid item xs={6}>
{this.LangSelect()}
</Grid>
<Grid item xs={3}>
<Tooltip enterTouchDelay={250} title={t('Show community and support links')}>
<IconButton
color='primary'
aria-label='Community'
onClick={this.handleClickOpenCommunity}
>
<PeopleIcon />
</IconButton>
</Tooltip>
</Grid>
<Grid item xs={3}>
<Tooltip enterTouchDelay={250} title={t('Show stats for nerds')}>
<IconButton
color='primary'
aria-label='Stats for Nerds'
onClick={() => this.props.fetchInfo()}
onClick={this.handleClickOpenStatsForNerds}
>
<BarChartIcon />
</IconButton>
</Tooltip>
</Grid>
</Grid>
</Grid>
</Paper>
);
};
render() {
return (
<div>
<CommunityDialog
open={this.state.openCommunity}
handleClickCloseCommunity={this.handleClickCloseCommunity}
/>
<UpdateClientDialog
open={this.state.openUpdateClient}
coordinatorVersion={this.props.info.coordinatorVersion}
clientVersion={this.props.info.clientVersion}
handleClickClose={() =>
this.props.setInfo({ ...this.props.info, openUpdateClient: false })
}
/>
<ExchangeSummaryDialog
open={this.state.openExchangeSummary}
handleClickCloseExchangeSummary={this.handleClickCloseExchangeSummary}
numPublicBuyOrders={this.props.info.num_public_buy_orders}
numPublicSellOrders={this.props.info.num_public_sell_orders}
bookLiquidity={this.props.info.book_liquidity}
activeRobotsToday={this.props.info.active_robots_today}
lastDayNonkycBtcPremium={this.props.info.last_day_nonkyc_btc_premium}
makerFee={this.props.info.maker_fee}
takerFee={this.props.info.taker_fee}
swapFeeRate={this.props.info.current_swap_fee_rate}
/>
<ProfileDialog
open={this.state.openProfile}
handleClickCloseProfile={this.handleClickCloseProfile}
nickname={this.props.robot.nickname}
activeOrderId={this.props.robot.activeOrderId}
lastOrderId={this.props.robot.lastOrderId}
referralCode={this.props.robot.referralCode}
tgEnabled={this.props.robot.tgEnabled}
tgBotName={this.props.robot.tgBotName}
tgToken={this.props.robot.tgToken}
handleSubmitInvoiceClicked={this.handleSubmitInvoiceClicked}
showRewardsSpinner={this.state.showRewardsSpinner}
withdrawn={this.props.info.withdrawn}
badInvoice={this.props.info.badInvoice}
earnedRewards={this.props.robot.earnedRewards}
updateRobot={(newParam) => this.props.setRobot({ ...robot, ...newParam })}
stealthInvoices={this.props.robot.stealthInvoices}
handleSetStealthInvoice={this.handleSetStealthInvoice}
/>
<StatsDialog
open={this.state.openStatsForNerds}
handleClickCloseStatsForNerds={this.handleClickCloseStatsForNerds}
coordinatorVersion={this.props.info.coordinatorVersion}
clientVersion={this.props.info.clientVersion}
lndVersion={this.props.info.lnd_version}
network={this.props.info.network}
nodeAlias={this.props.info.node_alias}
nodeId={this.props.info.node_id}
alternativeName={this.props.info.alternative_name}
alternativeSite={this.props.info.alternative_site}
commitHash={this.props.info.robosats_running_commit_hash}
lastDayVolume={this.props.info.last_day_volume}
lifetimeVolume={this.props.info.lifetime_volume}
/>
<MediaQuery minWidth={1200}>{this.bottomBarDesktop()}</MediaQuery>
<MediaQuery maxWidth={1199}>{this.bottomBarPhone()}</MediaQuery>
</div>
);
}
}
export default withTranslation()(NavBar);

View File

@ -44,12 +44,10 @@ import ArticleIcon from '@mui/icons-material/Article';
import HourglassTopIcon from '@mui/icons-material/HourglassTop';
import CheckIcon from '@mui/icons-material/Check';
import { pn } from '../../utils/prettyNumbers';
import { pn, getWebln, statusBadgeColor } from '../../utils';
import { systemClient } from '../../services/System';
import { getWebln } from '../../utils/webln';
import { apiClient } from '../../services/api';
import RobotAvatar from '../../components/RobotAvatar';
import statusBadgeColor from '../../utils/statusBadgeColor';
import { PaymentStringAsIcons } from '../../components/PaymentMethods';
class OrderPage extends Component {

View File

@ -21,9 +21,8 @@ import DownloadIcon from '@mui/icons-material/Download';
import { RoboSatsNoTextIcon } from '../components/Icons';
import { sha256 } from 'js-sha256';
import { genBase62Token, tokenStrength } from '../utils/token';
import { genKey } from '../utils/pgp';
import { saveAsJson } from '../utils/saveFile';
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';

View File

@ -19,15 +19,12 @@ import {
import { DataGrid, GridPagination } from '@mui/x-data-grid';
import currencyDict from '../../../static/assets/currencies.json';
import { Book, Favorites } from '../../models';
import filterOrders from '../../utils/filterOrders';
import { filterOrders, hexToRgb, statusBadgeColor, pn, amountToString } from '../../utils';
import BookControl from './BookControl';
import { FlagWithProps } from '../Icons';
import { pn, amountToString } from '../../utils/prettyNumbers';
import { PaymentStringAsIcons } from '../PaymentMethods';
import RobotAvatar from '../RobotAvatar';
import hexToRgb from '../../utils/hexToRgb';
import statusBadgeColor from '../../utils/statusBadgeColor';
// Icons
import { Fullscreen, FullscreenExit, Refresh } from '@mui/icons-material';

View File

@ -23,12 +23,10 @@ import { useTranslation } from 'react-i18next';
import { useHistory } from 'react-router-dom';
import { Order, LimitList } from '../../../models';
import RobotAvatar from '../../RobotAvatar';
import { amountToString } from '../../../utils/prettyNumbers';
import { amountToString, matchMedian, statusBadgeColor } from '../../../utils';
import currencyDict from '../../../../static/assets/currencies.json';
import { PaymentStringAsIcons } from '../../PaymentMethods';
import getNivoScheme from '../NivoScheme';
import median from '../../../utils/match';
import statusBadgeColor from '../../../utils/statusBadgeColor';
interface DepthChartProps {
orders: Order[];
@ -93,7 +91,7 @@ const DepthChart: React.FC<DepthChartProps> = ({
if (xType === 'base_amount') {
const prices: number[] = enrichedOrders.map((order) => order?.base_amount || 0);
const medianValue = ~~median(prices);
const medianValue = ~~matchMedian(prices);
const maxValue = prices.sort((a, b) => b - a).slice(0, 1)[0] || 1500;
const maxRange = maxValue - medianValue;
const rangeSteps = maxRange / 10;
@ -104,7 +102,7 @@ const DepthChart: React.FC<DepthChartProps> = ({
} else {
if (lastDayPremium === undefined) {
const premiums: number[] = enrichedOrders.map((order) => order?.premium || 0);
setCenter(~~median(premiums));
setCenter(~~matchMedian(premiums));
} else {
setCenter(lastDayPremium);
}

View File

@ -14,7 +14,7 @@ import {
Link,
} from '@mui/material';
import { saveAsJson } from '../../utils/saveFile';
import { saveAsJson } from '../../utils';
import { systemClient } from '../../services/System';
// Icons

View File

@ -21,7 +21,7 @@ import PriceChangeIcon from '@mui/icons-material/PriceChange';
import BookIcon from '@mui/icons-material/Book';
import LinkIcon from '@mui/icons-material/Link';
import { pn } from '../../utils/prettyNumbers';
import { pn } from '../../utils';
interface Props {
open: boolean;

View File

@ -14,6 +14,7 @@ import {
} from '@mui/material';
import SmoothImage from 'react-smooth-image';
import MediaQuery from 'react-responsive';
import { pn } from '../../utils';
// Icons
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
@ -148,7 +149,7 @@ const InfoDialog = ({ maxAmount, open, onClose }: Props): JSX.Element => {
<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 },
{ maxAmount: pn(maxAmount) },
)}{' '}
</p>
</Typography>

View File

@ -37,7 +37,7 @@ import EmojiEventsIcon from '@mui/icons-material/EmojiEvents';
import { UserNinjaIcon, BitcoinIcon } from '../Icons';
import { systemClient } from '../../services/System';
import { getWebln } from '../../utils/webln';
import { getHost, getWebln } from '../../utils';
import RobotAvatar from '../RobotAvatar';
interface Props {
@ -51,7 +51,6 @@ interface Props {
tgBotName: string;
tgToken: string;
handleSubmitInvoiceClicked: (e: any, invoice: string) => void;
host: string;
showRewardsSpinner: boolean;
withdrawn: boolean;
badInvoice: boolean | string;
@ -72,7 +71,6 @@ const ProfileDialog = ({
tgBotName,
tgToken,
handleSubmitInvoiceClicked,
host,
showRewardsSpinner,
withdrawn,
badInvoice,
@ -83,6 +81,7 @@ const ProfileDialog = ({
}: Props): JSX.Element => {
const { t } = useTranslation();
const theme = useTheme();
const host = getHost();
const [rewardInvoice, setRewardInvoice] = useState<string>('');
const [showRewards, setShowRewards] = useState<boolean>(false);

View File

@ -23,7 +23,7 @@ import EqualizerIcon from '@mui/icons-material/Equalizer';
import { AmbossIcon, BitcoinSignIcon, RoboSatsNoTextIcon } from '../Icons';
import { pn } from '../../utils/prettyNumbers';
import { pn } from '../../utils';
interface Props {
open: boolean;

View File

@ -14,7 +14,7 @@ import {
import { FlagWithProps } from '../Icons';
import RangeSlider from './RangeSlider';
import currencyDict from '../../../static/assets/currencies.json';
import { pn } from '../../utils/prettyNumbers';
import { pn } from '../../utils';
const RangeThumbComponent = function (props: object) {
const { children, ...other } = props;

View File

@ -37,7 +37,7 @@ import { FlagWithProps } from '../Icons';
import AutocompletePayments from './AutocompletePayments';
import AmountRange from './AmountRange';
import currencyDict from '../../../static/assets/currencies.json';
import { pn } from '../../utils/prettyNumbers';
import { pn } from '../../utils';
import { SelfImprovement, Lock, HourglassTop, DeleteSweep, Edit } from '@mui/icons-material';
import { LoadingButton } from '@mui/lab';

View File

@ -15,8 +15,8 @@ import {
Typography,
} from '@mui/material';
import ReconnectingWebSocket from 'reconnecting-websocket';
import { encryptMessage, decryptMessage } from '../../utils/pgp';
import { saveAsJson } from '../../utils/saveFile';
import { encryptMessage, decryptMessage } from '../../pgp';
import { saveAsJson } from '../../utils';
import { AuditPGPDialog } from '../Dialogs';
import RobotAvatar from '../RobotAvatar';
import { systemClient } from '../../services/System';

View File

@ -18,8 +18,7 @@ import {
AccordionDetails,
Typography,
} from '@mui/material';
import { pn } from '../../utils/prettyNumbers';
import { saveAsJson } from '../../utils/saveFile';
import { pn, saveAsJson } from '../../utils';
import RobotAvatar from '../RobotAvatar';
// Icons

View File

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import { withTranslation, Trans } from 'react-i18next';
import { Paper, Alert, AlertTitle, Button, Link } from '@mui/material';
import MediaQuery from 'react-responsive';
import { getHost } from '../utils';
class UnsafeAlert extends Component {
constructor(props) {
@ -12,18 +13,10 @@ class UnsafeAlert extends Component {
};
}
getHost() {
const url =
window.location !== window.parent.location
? this.getHost(document.referrer)
: document.location.href;
return url.split('/')[2];
}
isSelfhosted() {
const http = new XMLHttpRequest();
try {
http.open('HEAD', `${location.protocol}//${this.getHost()}/selfhosted`, false);
http.open('HEAD', `${location.protocol}//${getHost()}/selfhosted`, false);
http.send();
return http.status === 200;
} catch {
@ -72,7 +65,7 @@ class UnsafeAlert extends Component {
}
// Show unsafe alert
if (!window.NativeRobosats && !this.safe_urls.includes(this.getHost())) {
if (!window.NativeRobosats && !this.safe_urls.includes(getHost())) {
return (
<div>
<MediaQuery minWidth={800}>

View File

@ -0,0 +1,7 @@
const getHost = function () {
const url =
window.location != window.parent.location ? document.referrer : document.location.href;
return url.split('/')[2];
};
export default getHost;

View File

@ -0,0 +1,11 @@
export { default as checkVer } from './checkVer';
export { default as filterOrders } from './filterOrders';
export { default as getHost } from './getHost';
export { default as hexToRgb } from './hexToRgb';
export { default as matchMedian } from './match';
export { default as pn } from './prettyNumbers';
export { amountToString } from './prettyNumbers';
export { default as saveAsJson } from './saveFile';
export { default as statusBadgeColor } from './saveFile';
export { genBase62Token, tokenStrength } from './token';
export { default as getWebln } from './webln';

View File

@ -1,7 +1,7 @@
export const median = (arr: number[]) => {
export const matchMedian = (arr: number[]) => {
const mid = Math.floor(arr.length / 2);
const nums = [...arr].sort((a, b) => a - b);
return arr.length % 2 !== 0 ? nums[mid] : (nums[mid - 1] + nums[mid]) / 2;
};
export default median;
export default matchMedian;

View File

@ -3,7 +3,7 @@
* @param {filename} data -- object to save
*/
export const saveAsJson = (filename, dataObjToWrite) => {
const saveAsJson = (filename, dataObjToWrite) => {
const blob = new Blob([JSON.stringify(dataObjToWrite, null, 2)], { type: 'text/json' });
const link = document.createElement('a');
@ -20,3 +20,5 @@ export const saveAsJson = (filename, dataObjToWrite) => {
link.dispatchEvent(evt);
link.remove();
};
export default saveAsJson;

View File

@ -1,6 +1,6 @@
import { requestProvider, WeblnProvider } from 'webln';
export const getWebln = async (): Promise<WeblnProvider> => {
const getWebln = async (): Promise<WeblnProvider> => {
const resultPromise = new Promise<WeblnProvider>(async (resolve, reject) => {
try {
const webln = await requestProvider();
@ -16,3 +16,5 @@ export const getWebln = async (): Promise<WeblnProvider> => {
return await resultPromise;
};
export default getWebln;