Run linter and add linter GH action

This commit is contained in:
Reckless_Satoshi 2022-09-09 10:18:04 -07:00
parent 9d73f1ec34
commit 14487a9c2d
No known key found for this signature in database
GPG Key ID: 9C4585B561315571
67 changed files with 8457 additions and 5810 deletions

View File

@ -16,7 +16,7 @@ on:
branches: [ "main" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
# branches: [ "main" ]
schedule:
- cron: '39 10 * * 2'

39
.github/workflows/linter.yml vendored Normal file
View File

@ -0,0 +1,39 @@
name: Lint
on:
# Trigger the workflow on push or pull request,
# but only for the main branch
push:
branches:
- main
pull_request:
branches:
- main
jobs:
run-linters:
name: Run linters
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v2
- name: 'Setup node'
uses: actions/setup-node@v3
with:
node-version: 16.17.0
cache: npm
cache-dependency-path: frontend/package-lock.json
# ESLint and Prettier must be in `package.json`
- name: 'Install NPM Dependencies'
run: |
cd frontend
npm install -f
- name: Run linters
uses: wearerequired/lint-action@v2
with:
prettier: true
prettier_dir: frontend

View File

@ -1,13 +1,13 @@
import React, { Component , Suspense } from "react";
import React, { Component, Suspense } from 'react';
import ReactDOM from 'react-dom/client';
import HomePage from "./HomePage";
import { CssBaseline, IconButton , Link} from "@mui/material";
import HomePage from './HomePage';
import { CssBaseline, IconButton, Link } from '@mui/material';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import UnsafeAlert from "./UnsafeAlert";
import { LearnDialog } from "./Dialogs";
import UnsafeAlert from './UnsafeAlert';
import { LearnDialog } from './Dialogs';
import { I18nextProvider } from "react-i18next";
import i18n from "./i18n";
import { I18nextProvider } from 'react-i18next';
import i18n from './i18n';
//Icons
import DarkModeIcon from '@mui/icons-material/DarkMode';
@ -24,63 +24,77 @@ export default class App extends Component {
dark: window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches,
expandedSettings: false,
openLearn: false,
theme: {typography: { fontSize: 14 }},
}
theme: { typography: { fontSize: 14 } },
};
}
lightTheme = createTheme ({});
lightTheme = createTheme({});
darkTheme = createTheme({
palette: {
mode: 'dark',
background: {
default: "#070707"
default: '#070707',
},
},
});
onSettingsClick = () => {
this.setState({
expandedSettings: ! this.state.expandedSettings
})
}
expandedSettings: !this.state.expandedSettings,
});
};
onZoomClick = (direction) => {
let zoomChange;
direction === "out" ? zoomChange = -1 : zoomChange = 1;
this.setState(({theme}) => ({
theme: {
direction === 'out' ? (zoomChange = -1) : (zoomChange = 1);
this.setState(({ theme }) => ({
theme: {
...theme,
typography: {
fontSize: this.state.theme.typography.fontSize + zoomChange,
},
}
},
}));
}
};
render() {
return (
<Suspense fallback="loading language">
<I18nextProvider i18n={i18n}>
<ThemeProvider theme={this.state.dark ? this.darkTheme : createTheme(this.state.theme)}>
<CssBaseline/>
<LearnDialog open={this.state.openLearn} onClose={()=> this.setState({openLearn:false})}/>
<IconButton sx={{position:'fixed',right:'34px'}} onClick={()=> this.setState({openLearn:true})}><SchoolIcon/></IconButton>
<IconButton sx={{position:'fixed',right:'0px'}} onClick={()=>this.setState({dark:!this.state.dark})}>
{this.state.dark ? <LightModeIcon/>:<DarkModeIcon/>}
</IconButton>
<IconButton sx={{position:'fixed',right:'34px'}} onClick={()=> this.setState({openLearn:true})}><SchoolIcon/></IconButton>
<UnsafeAlert className="unsafeAlert"/>
<HomePage {...this.state}/>
</ThemeProvider>
</I18nextProvider>
<Suspense fallback='loading language'>
<I18nextProvider i18n={i18n}>
<ThemeProvider theme={this.state.dark ? this.darkTheme : createTheme(this.state.theme)}>
<CssBaseline />
<LearnDialog
open={this.state.openLearn}
onClose={() => this.setState({ openLearn: false })}
/>
<IconButton
sx={{ position: 'fixed', right: '34px' }}
onClick={() => this.setState({ openLearn: true })}
>
<SchoolIcon />
</IconButton>
<IconButton
sx={{ position: 'fixed', right: '0px' }}
onClick={() => this.setState({ dark: !this.state.dark })}
>
{this.state.dark ? <LightModeIcon /> : <DarkModeIcon />}
</IconButton>
<IconButton
sx={{ position: 'fixed', right: '34px' }}
onClick={() => this.setState({ openLearn: true })}
>
<SchoolIcon />
</IconButton>
<UnsafeAlert className='unsafeAlert' />
<HomePage {...this.state} />
</ThemeProvider>
</I18nextProvider>
</Suspense>
);
}
}
const root = ReactDOM.createRoot(
document.getElementById("app")
);
const root = ReactDOM.createRoot(document.getElementById('app'));
root.render(<App />);

View File

@ -1,30 +1,30 @@
import React, { useState } from "react";
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { useAutocomplete } from '@mui/base/AutocompleteUnstyled';
import { styled } from '@mui/material/styles';
import { Button, Tooltip } from "@mui/material";
import { paymentMethods, swapDestinations } from "./payment-methods/Methods";
import { Button, Tooltip } from '@mui/material';
import { paymentMethods, swapDestinations } from './payment-methods/Methods';
// Icons
import DashboardCustomizeIcon from '@mui/icons-material/DashboardCustomize';
import AddIcon from '@mui/icons-material/Add';
import PaymentIcon from './payment-methods/Icons'
import PaymentIcon from './payment-methods/Icons';
import CheckIcon from '@mui/icons-material/Check';
import CloseIcon from '@mui/icons-material/Close';
const Root = styled('div')(
({ theme }) => `
color: ${
theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.65)' : 'rgba(0,0,0,.85)'
};
color: ${theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.65)' : 'rgba(0,0,0,.85)'};
font-size: 14px;
`,
);
const Label = styled('label')(
({ theme , error}) => `
color: ${theme.palette.mode === 'dark' ? (error? '#f44336': '#cfcfcf') : (error? '#dd0000':'#717171')};
({ theme, error }) => `
color: ${
theme.palette.mode === 'dark' ? (error ? '#f44336' : '#cfcfcf') : error ? '#dd0000' : '#717171'
};
align: center;
padding: 0 0 4px;
line-height: 1.5; f44336
@ -34,11 +34,13 @@ const Label = styled('label')(
);
const InputWrapper = styled('div')(
({ theme , error}) => `
({ theme, error }) => `
width: 244px;
min-height: 44px;
max-height: 124px;
border: 1px solid ${theme.palette.mode === 'dark' ? (error? '#f44336': '#434343') : (error? '#dd0000':'#c4c4c4')};
border: 1px solid ${
theme.palette.mode === 'dark' ? (error ? '#f44336' : '#434343') : error ? '#dd0000' : '#c4c4c4'
};
background-color: ${theme.palette.mode === 'dark' ? '#141414' : '#fff'};
border-radius: 4px;
padding: 1px;
@ -47,18 +49,32 @@ const InputWrapper = styled('div')(
overflow-y:auto;
&:hover {
border-color: ${theme.palette.mode === 'dark' ? (error? '#f44336':'#ffffff') : (error? '#dd0000' :'#2f2f2f')};
border-color: ${
theme.palette.mode === 'dark'
? error
? '#f44336'
: '#ffffff'
: error
? '#dd0000'
: '#2f2f2f'
};
}
&.focused {
border: 2px solid ${theme.palette.mode === 'dark' ? (error? '#f44336':'#90caf9') : (error? '#dd0000' :'#1976d2')};
border: 2px solid ${
theme.palette.mode === 'dark'
? error
? '#f44336'
: '#90caf9'
: error
? '#dd0000'
: '#1976d2'
};
}
& input {
background-color: ${theme.palette.mode === 'dark' ? '#141414' : '#fff'};
color: ${
theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.65)' : 'rgba(0,0,0,.85)'
};
color: ${theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.65)' : 'rgba(0,0,0,.85)'};
height: 30px;
box-sizing: border-box;
padding: 4px 6px;
@ -78,10 +94,10 @@ function Tag(props) {
const { label, icon, onDelete, ...other } = props;
return (
<div {...other}>
<div style={{position:'relative',left:'-5px',top:'4px'}}>
<PaymentIcon width={22} height={22} icon={icon}/>
</div>
<span style={{position:'relative',left:'2px'}}>{label}</span>
<div style={{ position: 'relative', left: '-5px', top: '4px' }}>
<PaymentIcon width={22} height={22} icon={icon} />
</div>
<span style={{ position: 'relative', left: '2px' }}>{label}</span>
<CloseIcon onClick={onDelete} />
</div>
);
@ -100,9 +116,7 @@ const StyledTag = styled(Tag)(
height: 34px;
margin: 2px;
line-height: 22px;
background-color: ${
theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.08)' : '#fafafa'
};
background-color: ${theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.08)' : '#fafafa'};
border: 1px solid ${theme.palette.mode === 'dark' ? '#303030' : '#e8e8e8'};
border-radius: 2px;
box-sizing: content-box;
@ -131,7 +145,7 @@ const StyledTag = styled(Tag)(
);
const ListHeader = styled('span')(
({ theme }) => `
({ theme }) => `
color: ${theme.palette.mode === 'dark' ? '#90caf9' : '#1976d2'};
align: left;
line-height:10px;
@ -202,83 +216,119 @@ export default function AutocompletePayments(props) {
getOptionProps,
groupedOptions,
value,
focused="true",
focused = 'true',
setAnchorEl,
} = useAutocomplete({
sx: {width:'200px', align:'left'},
sx: { width: '200px', align: 'left' },
id: 'payment-methods',
multiple: true,
options: props.optionsType=="fiat" ? paymentMethods : swapDestinations,
options: props.optionsType == 'fiat' ? paymentMethods : swapDestinations,
getOptionLabel: (option) => option.name,
onInputChange: (e) => setVal(e ? (e.target.value ? e.target.value : "") : ""),
onInputChange: (e) => setVal(e ? (e.target.value ? e.target.value : '') : ''),
onChange: (event, value) => props.onAutocompleteChange(optionsToString(value)),
onClose: () => (setVal(() => "")),
onClose: () => setVal(() => ''),
});
const [val, setVal] = useState();
function optionsToString(newValue){
function optionsToString(newValue) {
var str = '';
var arrayLength = newValue.length;
for (var i = 0; i < arrayLength; i++) {
str += newValue[i].name + ' ';
}
for (var i = 0; i < arrayLength; i++) {
str += newValue[i].name + ' ';
}
return str.slice(0, -1);
}
function handleAddNew(inputProps){
paymentMethods.push({name: inputProps.value, icon:'custom'})
var a = value.push({name: inputProps.value, icon:'custom'});
setVal(() => "");
function handleAddNew(inputProps) {
paymentMethods.push({ name: inputProps.value, icon: 'custom' });
var a = value.push({ name: inputProps.value, icon: 'custom' });
setVal(() => '');
if(a || a == null){props.onAutocompleteChange(optionsToString(value))}
return false
};
if (a || a == null) {
props.onAutocompleteChange(optionsToString(value));
}
return false;
}
return (
<Root>
<div style={{height:'5px'}}></div>
<Tooltip placement="top" enterTouchDelay={300} enterDelay={700} enterNextDelay={2000} title={props.tooltipTitle}>
<div {...getRootProps()} >
<Label {...getInputLabelProps()} error={props.error ? "error" : null}> {props.label}</Label>
<InputWrapper ref={setAnchorEl} error={props.error ? "error" : null} className={focused ? 'focused' : ''}>
<div style={{ height: '5px' }}></div>
<Tooltip
placement='top'
enterTouchDelay={300}
enterDelay={700}
enterNextDelay={2000}
title={props.tooltipTitle}
>
<div {...getRootProps()}>
<Label {...getInputLabelProps()} error={props.error ? 'error' : null}>
{' '}
{props.label}
</Label>
<InputWrapper
ref={setAnchorEl}
error={props.error ? 'error' : null}
className={focused ? 'focused' : ''}
>
{value.map((option, index) => (
<StyledTag label={t(option.name)} icon={option.icon} {...getTagProps({ index })} />
))}
<input {...getInputProps()} value={val ? val :""}/>
<input {...getInputProps()} value={val ? val : ''} />
</InputWrapper>
</div>
</Tooltip>
{groupedOptions.length > 0 ? (
<Listbox {...getListboxProps()}>
<div style={{position:'fixed', minHeight:'20px', marginLeft: 120-props.listHeaderText.length*3, marginTop: '-13px'}}>
<ListHeader ><i>{props.listHeaderText+""} </i> </ListHeader>
</div>
<div
style={{
position: 'fixed',
minHeight: '20px',
marginLeft: 120 - props.listHeaderText.length * 3,
marginTop: '-13px',
}}
>
<ListHeader>
<i>{props.listHeaderText + ''} </i>{' '}
</ListHeader>
</div>
{groupedOptions.map((option, index) => (
<li key={option.name} {...getOptionProps({ option, index })}>
<Button fullWidth={true} color='inherit' size="small" sx={{textTransform: "none"}} style={{justifyContent: "flex-start"}}>
<div style={{position:'relative', right: '4px', top:'4px'}}>
<AddIcon style={{color : '#1976d2'}} sx={{width:18,height:18}} />
</div>
{t(option.name)}
<Button
fullWidth={true}
color='inherit'
size='small'
sx={{ textTransform: 'none' }}
style={{ justifyContent: 'flex-start' }}
>
<div style={{ position: 'relative', right: '4px', top: '4px' }}>
<AddIcon style={{ color: '#1976d2' }} sx={{ width: 18, height: 18 }} />
</div>
{t(option.name)}
</Button>
<div style={{position:'relative', top: '5px'}}><CheckIcon/></div>
<div style={{ position: 'relative', top: '5px' }}>
<CheckIcon />
</div>
</li>
))}
{val != null?
(val.length > 2 ?
<Button size="small" fullWidth={true} onClick={() => handleAddNew(getInputProps())}><DashboardCustomizeIcon sx={{width:18,height:18}}/>{props.addNewButtonText}</Button>
:null)
:null}
{val != null ? (
val.length > 2 ? (
<Button size='small' fullWidth={true} onClick={() => handleAddNew(getInputProps())}>
<DashboardCustomizeIcon sx={{ width: 18, height: 18 }} />
{props.addNewButtonText}
</Button>
) : null
) : null}
</Listbox>
) :
//Here goes what happens if there is no groupedOptions
(getInputProps().value.length > 0 ?
) : //Here goes what happens if there is no groupedOptions
getInputProps().value.length > 0 ? (
<Listbox {...getListboxProps()}>
<Button fullWidth={true} onClick={() => handleAddNew(getInputProps())}><DashboardCustomizeIcon sx={{width:20,height:20}}/>{props.addNewButtonText}</Button>
<Button fullWidth={true} onClick={() => handleAddNew(getInputProps())}>
<DashboardCustomizeIcon sx={{ width: 20, height: 20 }} />
{props.addNewButtonText}
</Button>
</Listbox>
:null)
}
) : null}
</Root>
);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,55 +1,77 @@
import React, { useEffect, useState } from "react"
import { ResponsiveLine, Serie, Datum, PointTooltipProps, PointMouseHandler, Point, CustomLayer } from '@nivo/line'
import { Box, CircularProgress, Grid, IconButton, MenuItem, Paper, Select, useTheme } from "@mui/material"
import React, { useEffect, useState } from 'react';
import {
ResponsiveLine,
Serie,
Datum,
PointTooltipProps,
PointMouseHandler,
Point,
CustomLayer,
} from '@nivo/line';
import {
Box,
CircularProgress,
Grid,
IconButton,
MenuItem,
Paper,
Select,
useTheme,
} from '@mui/material';
import { AddCircleOutline, RemoveCircleOutline } from '@mui/icons-material';
import { useTranslation } from "react-i18next";
import { useHistory } from "react-router-dom"
import { Order } from "../../../models/Order.model";
import { LimitList } from "../../../models/Limit.model";
import RobotAvatar from '../../Robots/RobotAvatar'
import { amountToString } from "../../../utils/prettyNumbers";
import { useTranslation } from 'react-i18next';
import { useHistory } from 'react-router-dom';
import { Order } from '../../../models/Order.model';
import { LimitList } from '../../../models/Limit.model';
import RobotAvatar from '../../Robots/RobotAvatar';
import { amountToString } from '../../../utils/prettyNumbers';
import currencyDict from '../../../../static/assets/currencies.json';
import PaymentText from "../../PaymentText";
import getNivoScheme from "../NivoScheme"
import median from "../../../utils/match";
import PaymentText from '../../PaymentText';
import getNivoScheme from '../NivoScheme';
import median from '../../../utils/match';
interface DepthChartProps {
bookLoading: boolean
orders: Order[]
lastDayPremium: number | undefined
currency: number
setAppState: (state: object) => void
limits: LimitList
compact?: boolean
bookLoading: boolean;
orders: Order[];
lastDayPremium: number | undefined;
currency: number;
setAppState: (state: object) => void;
limits: LimitList;
compact?: boolean;
}
const DepthChart: React.FC<DepthChartProps> = ({
bookLoading, orders, lastDayPremium, currency, setAppState, limits, compact
const DepthChart: React.FC<DepthChartProps> = ({
bookLoading,
orders,
lastDayPremium,
currency,
setAppState,
limits,
compact,
}) => {
const { t } = useTranslation()
const history = useHistory()
const theme = useTheme()
const [enrichedOrders, setEnrichedOrders] = useState<Order[]>([])
const [series, setSeries] = useState<Serie[]>([])
const [xRange, setXRange] = useState<number>(8)
const [xType, setXType] = useState<string>("premium")
const [currencyCode, setCurrencyCode] = useState<number>(1)
const [center, setCenter] = useState<number>(0)
const { t } = useTranslation();
const history = useHistory();
const theme = useTheme();
const [enrichedOrders, setEnrichedOrders] = useState<Order[]>([]);
const [series, setSeries] = useState<Serie[]>([]);
const [xRange, setXRange] = useState<number>(8);
const [xType, setXType] = useState<string>('premium');
const [currencyCode, setCurrencyCode] = useState<number>(1);
const [center, setCenter] = useState<number>(0);
useEffect(() => {
if (Object.keys(limits).length === 0) {
fetch('/api/limits/')
.then((response) => response.json())
.then((data) => {
setAppState({ limits: data })
})
setAppState({ limits: data });
});
}
}, [])
}, []);
useEffect(() => {
setCurrencyCode(currency === 0 ? 1 : currency)
}, [currency])
setCurrencyCode(currency === 0 ? 1 : currency);
}, [currency]);
useEffect(() => {
if (Object.keys(limits).length > 0) {
@ -57,172 +79,176 @@ const DepthChart: React.FC<DepthChartProps> = ({
// We need to transform all currencies to the same base (ex. USD), we don't have the exchange rate
// for EUR -> USD, but we know the rate of both to BTC, so we get advantage of it and apply a
// simple rule of three
order.base_amount = (order.price * limits[currencyCode].price) / limits[order.currency].price
return order
})
setEnrichedOrders(enriched)
order.base_amount =
(order.price * limits[currencyCode].price) / limits[order.currency].price;
return order;
});
setEnrichedOrders(enriched);
}
}, [limits, orders, currencyCode])
}, [limits, orders, currencyCode]);
useEffect(() => {
if (enrichedOrders.length > 0) {
generateSeries()
if (enrichedOrders.length > 0) {
generateSeries();
}
}, [enrichedOrders, xRange])
}, [enrichedOrders, xRange]);
useEffect(() => {
if (xType === 'base_amount') {
const prices: number[] = enrichedOrders.map((order) => order?.base_amount || 0)
setCenter(~~median(prices))
setXRange(1500)
if (xType === 'base_amount') {
const prices: number[] = enrichedOrders.map((order) => order?.base_amount || 0);
setCenter(~~median(prices));
setXRange(1500);
} else if (lastDayPremium) {
setCenter(lastDayPremium)
setXRange(8)
setCenter(lastDayPremium);
setXRange(8);
}
}, [enrichedOrders, xType, lastDayPremium, currencyCode])
}, [enrichedOrders, xType, lastDayPremium, currencyCode]);
const calculateBtc = (order: Order): number => {
const amount = parseInt(order.amount) || order.max_amount
return amount / order.price
}
const amount = parseInt(order.amount) || order.max_amount;
return amount / order.price;
};
const generateSeries:() => void = () => {
let sortedOrders: Order[] = xType === 'base_amount' ?
enrichedOrders.sort((order1, order2) => (order1?.base_amount || 0) - (order2?.base_amount || 0) )
: enrichedOrders.sort((order1, order2) => order1.premium - order2.premium )
const generateSeries: () => void = () => {
let sortedOrders: Order[] =
xType === 'base_amount'
? enrichedOrders.sort(
(order1, order2) => (order1?.base_amount || 0) - (order2?.base_amount || 0),
)
: enrichedOrders.sort((order1, order2) => order1.premium - order2.premium);
const sortedBuyOrders: Order[] = sortedOrders.filter((order) => order.type == 0).reverse()
const sortedSellOrders: Order[] = sortedOrders.filter((order) => order.type == 1)
const sortedBuyOrders: Order[] = sortedOrders.filter((order) => order.type == 0).reverse();
const sortedSellOrders: Order[] = sortedOrders.filter((order) => order.type == 1);
const buySerie: Datum[] = generateSerie(sortedBuyOrders)
const sellSerie: Datum[] = generateSerie(sortedSellOrders)
const buySerie: Datum[] = generateSerie(sortedBuyOrders);
const sellSerie: Datum[] = generateSerie(sortedSellOrders);
const maxX: number = center + xRange
const minX: number = center - xRange
const maxX: number = center + xRange;
const minX: number = center - xRange;
setSeries([
{
id: "buy",
data: closeSerie(buySerie, maxX, minX)
},
id: 'buy',
data: closeSerie(buySerie, maxX, minX),
},
{
id: "sell",
data: closeSerie(sellSerie, minX, maxX)
}
])
}
id: 'sell',
data: closeSerie(sellSerie, minX, maxX),
},
]);
};
const generateSerie = (orders: Order[]): Datum[] => {
if (!center) { return [] }
if (!center) {
return [];
}
let sumOrders: number = 0
let serie: Datum[] = []
let sumOrders: number = 0;
let serie: Datum[] = [];
orders.forEach((order) => {
const lastSumOrders = sumOrders
sumOrders += calculateBtc(order)
const lastSumOrders = sumOrders;
sumOrders += calculateBtc(order);
const datum: Datum[] = [
{ // Vertical Line
x: xType === 'base_amount' ? order.base_amount : order.premium,
y: lastSumOrders
{
// Vertical Line
x: xType === 'base_amount' ? order.base_amount : order.premium,
y: lastSumOrders,
},
{ // Order Point
x: xType === 'base_amount' ? order.base_amount : order.premium,
{
// Order Point
x: xType === 'base_amount' ? order.base_amount : order.premium,
y: sumOrders,
order: order
}
]
order: order,
},
];
serie = [...serie, ...datum]
})
const inlineSerie = serie.filter((datum: Datum) => {
return (Number(datum.x) > center - xRange) &&
(Number(datum.x) < center + xRange)
})
serie = [...serie, ...datum];
});
const inlineSerie = serie.filter((datum: Datum) => {
return Number(datum.x) > center - xRange && Number(datum.x) < center + xRange;
});
return inlineSerie
}
return inlineSerie;
};
const closeSerie = (serie: Datum[], limitBottom: number, limitTop: number): Datum[] =>{
if (serie.length == 0) { return [] }
const closeSerie = (serie: Datum[], limitBottom: number, limitTop: number): Datum[] => {
if (serie.length == 0) {
return [];
}
// If the bottom is not 0, exdens the horizontal bottom line
if (serie[0].y !== 0) {
const startingPoint: Datum = {
x: limitBottom,
y: serie[0].y
}
serie.unshift(startingPoint)
y: serie[0].y,
};
serie.unshift(startingPoint);
}
// exdens the horizontal top line
const endingPoint: Datum = {
x: limitTop,
y: serie[serie.length - 1].y
}
y: serie[serie.length - 1].y,
};
return [...serie, endingPoint]
}
return [...serie, endingPoint];
};
const centerLine: CustomLayer = (props) => (
<path
key="center-line"
key='center-line'
d={props.lineGenerator([
{
y: 0,
x: props.xScale(center)
x: props.xScale(center),
},
{
y: props.innerHeight,
x: props.xScale(center)
x: props.xScale(center),
},
])}
fill="none"
fill='none'
stroke={getNivoScheme(theme).markers?.lineColor}
strokeWidth={getNivoScheme(theme).markers?.lineStrokeWidth}
/>
)
);
const generateTooltip: React.FunctionComponent<PointTooltipProps> = (pointTooltip: PointTooltipProps) => {
const order: Order = pointTooltip.point.data.order
const generateTooltip: React.FunctionComponent<PointTooltipProps> = (
pointTooltip: PointTooltipProps,
) => {
const order: Order = pointTooltip.point.data.order;
return order ? (
<Paper elevation={12} style={{ padding: 10, width: 250 }}>
<Grid container justifyContent="space-between">
<Grid container justifyContent='space-between'>
<Grid item xs={3}>
<Grid
container
justifyContent="center"
alignItems="center"
>
<Grid container justifyContent='center' alignItems='center'>
<RobotAvatar order={order} />
</Grid>
</Grid>
<Grid item xs={8}>
<Grid
container
direction="column"
justifyContent="center"
alignItems="flex-start"
>
<Box>
{order.maker_nick}
</Box>
<Grid container direction='column' justifyContent='center' alignItems='flex-start'>
<Box>{order.maker_nick}</Box>
<Box>
<Grid
container
direction="column"
justifyContent="flex-start"
alignItems="flex-start"
direction='column'
justifyContent='flex-start'
alignItems='flex-start'
>
<Grid item xs={12}>
{amountToString(order.amount, order.has_range, order.min_amount, order.max_amount)}
{' '}
{amountToString(
order.amount,
order.has_range,
order.min_amount,
order.max_amount,
)}{' '}
{currencyDict[order.currency]}
</Grid>
<Grid item xs={12}>
<PaymentText
othersText={t("Others")}
verbose={true}
size={20}
<PaymentText
othersText={t('Others')}
verbose={true}
size={20}
text={order.payment_method}
/>
</Grid>
@ -232,69 +258,67 @@ const DepthChart: React.FC<DepthChartProps> = ({
</Grid>
</Grid>
</Paper>
) : <></>
}
) : (
<></>
);
};
const formatAxisX = (value: number): string => {
if (xType === 'base_amount') {
return value.toString()
return value.toString();
}
return `${value}%`
}
const formatAxisY = (value: number): string => `${value}BTC`
return `${value}%`;
};
const formatAxisY = (value: number): string => `${value}BTC`;
const rangeSteps = xType === 'base_amount' ? 200 : 0.5
const rangeSteps = xType === 'base_amount' ? 200 : 0.5;
const handleOnClick: PointMouseHandler = (point: Point) => {
history.push('/order/' + point.data?.order?.id);
}
};
return bookLoading || !center || enrichedOrders.length < 1 ? (
<div style={{display: "flex", justifyContent: "center", paddingTop: 200, height: 420 }}>
<div style={{ display: 'flex', justifyContent: 'center', paddingTop: 200, height: 420 }}>
<CircularProgress />
</div>
) : (
<Grid container style={{ paddingTop: 15 }}>
<Grid
container
direction="row"
justifyContent="space-around"
alignItems="flex-start"
style={{ position: "absolute" }}
<Grid
container
direction='row'
justifyContent='space-around'
alignItems='flex-start'
style={{ position: 'absolute' }}
>
<Grid container justifyContent="flex-start" alignItems="flex-start" style={{ paddingLeft: 20 }}>
<Select
variant="standard"
value={xType}
onChange={(e) => setXType(e.target.value)}
>
<MenuItem value={"premium"}>
<div style={{display:'flex',alignItems:'center', flexWrap:'wrap'}}>
{t("Premium")}
<Grid
container
justifyContent='flex-start'
alignItems='flex-start'
style={{ paddingLeft: 20 }}
>
<Select variant='standard' value={xType} onChange={(e) => setXType(e.target.value)}>
<MenuItem value={'premium'}>
<div style={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap' }}>
{t('Premium')}
</div>
</MenuItem>
<MenuItem value={"base_amount"}>
<div style={{display:'flex',alignItems:'center', flexWrap:'wrap'}}>
{t("Price")}
<MenuItem value={'base_amount'}>
<div style={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap' }}>
{t('Price')}
</div>
</MenuItem>
</Select>
</Grid>
</Grid>
<Grid
container
direction="row"
justifyContent="center"
alignItems="center"
>
<Grid container justifyContent="center" alignItems="center">
<Grid container direction='row' justifyContent='center' alignItems='center'>
<Grid container justifyContent='center' alignItems='center'>
<Grid item>
<IconButton onClick={() => setXRange(xRange + rangeSteps)}>
<RemoveCircleOutline />
</IconButton>
</Grid>
<Grid item>
<Box justifyContent="center">
<Box justifyContent='center'>
{xType === 'base_amount' ? `${center} ${currencyDict[currencyCode]}` : `${center}%`}
</Box>
</Grid>
@ -306,42 +330,42 @@ const DepthChart: React.FC<DepthChartProps> = ({
</Grid>
</Grid>
<Grid container style={{ height: 357, padding: 15 }}>
<ResponsiveLine
data={series}
<ResponsiveLine
data={series}
enableArea={true}
useMesh={true}
animate={false}
crosshairType="cross"
crosshairType='cross'
tooltip={generateTooltip}
onClick={handleOnClick}
axisRight={{
tickSize: 5,
format: formatAxisY
format: formatAxisY,
}}
axisLeft={{
tickSize: 5,
format: formatAxisY
format: formatAxisY,
}}
axisBottom={{
tickSize: 5,
tickRotation: xType === 'base_amount' && compact ? 45 : 0,
format: formatAxisX
format: formatAxisX,
}}
margin={{ left: 65, right: 60, bottom: compact ? 36 : 25, top: 10 }}
xFormat={(value) => Number(value).toFixed(0)}
lineWidth={3}
theme={getNivoScheme(theme)}
colors={[theme.palette.secondary.main,theme.palette.primary.main]}
colors={[theme.palette.secondary.main, theme.palette.primary.main]}
xScale={{
type: 'linear',
min: center - xRange,
max: center + xRange
max: center + xRange,
}}
layers={['axes', 'areas', 'crosshair', 'lines', centerLine, 'slices', 'mesh']}
/>
</Grid>
</Grid>
)
}
);
};
export default DepthChart
export default DepthChart;

View File

@ -1,61 +1,61 @@
import { light } from "@mui/material/styles/createPalette"
import { palette } from "@mui/system"
import { Theme as NivoTheme } from "@nivo/core"
import { Theme as MuiTheme } from './createTheme'
import { light } from '@mui/material/styles/createPalette';
import { palette } from '@mui/system';
import { Theme as NivoTheme } from '@nivo/core';
import { Theme as MuiTheme } from './createTheme';
export const getNivoScheme: (theme: MuiTheme) => NivoTheme = (theme) => {
const lightMode = {
markers: {
lineColor: "rgb(0, 0, 0)",
lineStrokeWidth: 1
lineColor: 'rgb(0, 0, 0)',
lineStrokeWidth: 1,
},
axis: {
ticks: {
line: {
strokeWidth: "1",
stroke: "rgb(0, 0, 0)"
}
strokeWidth: '1',
stroke: 'rgb(0, 0, 0)',
},
},
domain: {
line: {
strokeWidth: "1",
stroke: "rgb(0, 0, 0)"
}
}
}
}
strokeWidth: '1',
stroke: 'rgb(0, 0, 0)',
},
},
},
};
const darkMode = {
markers: {
lineColor: "rgb(255, 255, 255)",
lineStrokeWidth: 1
lineColor: 'rgb(255, 255, 255)',
lineStrokeWidth: 1,
},
axis: {
ticks: {
text: {
fill: "rgb(255, 255, 255)"
fill: 'rgb(255, 255, 255)',
},
line: {
strokeWidth: "1",
stroke: "rgb(255, 255, 255)"
}
strokeWidth: '1',
stroke: 'rgb(255, 255, 255)',
},
},
domain: {
line: {
strokeWidth: "1",
stroke: "rgb(255, 255, 255)"
}
}
strokeWidth: '1',
stroke: 'rgb(255, 255, 255)',
},
},
},
crosshair: {
line: {
strokeWidth: 1,
stroke: "rgb(255, 255, 255)"
}
}
}
stroke: 'rgb(255, 255, 255)',
},
},
};
return theme.palette.mode === 'dark' ? darkMode : lightMode
}
return theme.palette.mode === 'dark' ? darkMode : lightMode;
};
export default getNivoScheme
export default getNivoScheme;

View File

@ -1,51 +1,52 @@
import React from "react";
import { useTranslation } from "react-i18next";
import React from 'react';
import { useTranslation } from 'react-i18next';
import {
Dialog,
DialogTitle,
Tooltip,
Tooltip,
IconButton,
TextField,
DialogActions,
DialogActions,
DialogContent,
DialogContentText,
Button,
Button,
Grid,
Link,
} from "@mui/material"
} from '@mui/material';
import { saveAsJson } from "../../utils/saveFile";
import { copyToClipboard } from "../../utils/clipboard";
import { saveAsJson } from '../../utils/saveFile';
import { copyToClipboard } from '../../utils/clipboard';
// Icons
import KeyIcon from '@mui/icons-material/Key';
import ContentCopy from "@mui/icons-material/ContentCopy";
import ContentCopy from '@mui/icons-material/ContentCopy';
import ForumIcon from '@mui/icons-material/Forum';
import { ExportIcon, NewTabIcon } from '../Icons';
function CredentialTextfield(props){
return(
<Grid item align="center" xs={12}>
<Tooltip placement="top" enterTouchDelay={200} enterDelay={200} title={props.tooltipTitle}>
function CredentialTextfield(props) {
return (
<Grid item align='center' xs={12}>
<Tooltip placement='top' enterTouchDelay={200} enterDelay={200} title={props.tooltipTitle}>
<TextField
sx={{width:"100%", maxWidth:"550px"}}
sx={{ width: '100%', maxWidth: '550px' }}
disabled
label={<b>{props.label}</b>}
value={props.value}
variant='filled'
size='small'
InputProps={{
endAdornment:
endAdornment: (
<Tooltip disableHoverListener enterTouchDelay={0} title={props.copiedTitle}>
<IconButton onClick={()=> copyToClipboard(props.value)}>
<ContentCopy/>
<IconButton onClick={() => copyToClipboard(props.value)}>
<ContentCopy />
</IconButton>
</Tooltip>,
}}
/>
</Tooltip>
),
}}
/>
</Tooltip>
</Grid>
)
);
}
type Props = {
@ -53,15 +54,15 @@ type Props = {
onClose: () => void;
orderId: number;
messages: array;
own_pub_key: string;
own_pub_key: string;
own_enc_priv_key: string;
peer_pub_key: string;
passphrase: string;
onClickBack: () => void;
}
};
const AuditPGPDialog = ({
open,
open,
onClose,
orderId,
messages,
@ -74,103 +75,129 @@ const AuditPGPDialog = ({
const { t } = useTranslation();
return (
<Dialog
open={open}
onClose={onClose}
>
<DialogTitle >
{t("Don't trust, verify")}
</DialogTitle>
<Dialog open={open} onClose={onClose}>
<DialogTitle>{t("Don't trust, verify")}</DialogTitle>
<DialogContent>
<DialogContentText>
{t("Your communication is end-to-end encrypted with OpenPGP. You can verify the privacy of this chat using any tool based on the OpenPGP standard.")}
{t(
'Your communication is end-to-end encrypted with OpenPGP. You can verify the privacy of this chat using any tool based on the OpenPGP standard.',
)}
</DialogContentText>
<Grid container spacing={1} align="center">
<Grid item align="center" xs={12}>
<Button component={Link} target="_blank" href="https://learn.robosats.com/docs/pgp-encryption">{t("Learn how to verify")} <NewTabIcon sx={{width:16,height:16}}/></Button>
<Grid container spacing={1} align='center'>
<Grid item align='center' xs={12}>
<Button
component={Link}
target='_blank'
href='https://learn.robosats.com/docs/pgp-encryption'
>
{t('Learn how to verify')} <NewTabIcon sx={{ width: 16, height: 16 }} />
</Button>
</Grid>
<CredentialTextfield
tooltipTitle={t("Your PGP public key. Your peer uses it to encrypt messages only you can read.")}
label={t("Your public key")}
<CredentialTextfield
tooltipTitle={t(
'Your PGP public key. Your peer uses it to encrypt messages only you can read.',
)}
label={t('Your public key')}
value={own_pub_key}
copiedTitle={t("Copied!")}/>
copiedTitle={t('Copied!')}
/>
<CredentialTextfield
tooltipTitle={t("Your peer PGP public key. You use it to encrypt messages only he can read and to verify your peer signed the incoming messages.")}
label={t("Peer public key")}
<CredentialTextfield
tooltipTitle={t(
'Your peer PGP public key. You use it to encrypt messages only he can read and to verify your peer signed the incoming messages.',
)}
label={t('Peer public key')}
value={peer_pub_key}
copiedTitle={t("Copied!")}/>
copiedTitle={t('Copied!')}
/>
<CredentialTextfield
tooltipTitle={t("Your encrypted private key. You use it to decrypt the messages that your peer encrypted for you. You also use it to sign the messages you send.")}
label={t("Your encrypted private key")}
<CredentialTextfield
tooltipTitle={t(
'Your encrypted private key. You use it to decrypt the messages that your peer encrypted for you. You also use it to sign the messages you send.',
)}
label={t('Your encrypted private key')}
value={own_enc_priv_key}
copiedTitle={t("Copied!")}/>
copiedTitle={t('Copied!')}
/>
<CredentialTextfield
tooltipTitle={t("The passphrase to decrypt your private key. Only you know it! Do not share. It is also your robot token.")}
label={t("Your private key passphrase (keep secure!)")}
<CredentialTextfield
tooltipTitle={t(
'The passphrase to decrypt your private key. Only you know it! Do not share. It is also your robot token.',
)}
label={t('Your private key passphrase (keep secure!)')}
value={passphrase}
copiedTitle={t("Copied!")}/>
<br/>
<Grid item xs={6}>
<Tooltip placement="top" enterTouchDelay={0} enterDelay={1000} enterNextDelay={2000} title={t("Save credentials as a JSON file")}>
<Button
size="small"
color="primary"
variant="contained"
onClick={()=>saveAsJson(
'keys_'+orderId+'.json',
{"own_public_key": own_pub_key,
"peer_public_key":peer_pub_key,
"encrypted_private_key":own_enc_priv_key,
"passphrase":passphrase
})}>
<div style={{width:26,height:18}}>
<ExportIcon sx={{width:18,height:18}}/>
</div>
{t("Keys")}
<div style={{width:26,height:20}}>
<KeyIcon sx={{width:20,height:20}}/>
</div>
</Button>
</Tooltip>
</Grid>
<Grid item xs={6}>
<Tooltip placement="top" enterTouchDelay={0} enterDelay={1000} enterNextDelay={2000} title={t("Save messages as a JSON file")}>
<Button
size="small"
color="primary"
variant="contained"
onClick={()=>saveAsJson(
'messages_'+orderId+'.json',
messages)}>
<div style={{width:28,height:20}}>
<ExportIcon sx={{width:18,height:18}}/>
</div>
{t("Messages")}
<div style={{width:26,height:20}}>
<ForumIcon sx={{width:20,height:20}}/>
</div>
</Button>
</Tooltip>
copiedTitle={t('Copied!')}
/>
<br />
<Grid item xs={6}>
<Tooltip
placement='top'
enterTouchDelay={0}
enterDelay={1000}
enterNextDelay={2000}
title={t('Save credentials as a JSON file')}
>
<Button
size='small'
color='primary'
variant='contained'
onClick={() =>
saveAsJson('keys_' + orderId + '.json', {
own_public_key: own_pub_key,
peer_public_key: peer_pub_key,
encrypted_private_key: own_enc_priv_key,
passphrase: passphrase,
})
}
>
<div style={{ width: 26, height: 18 }}>
<ExportIcon sx={{ width: 18, height: 18 }} />
</div>
{t('Keys')}
<div style={{ width: 26, height: 20 }}>
<KeyIcon sx={{ width: 20, height: 20 }} />
</div>
</Button>
</Tooltip>
</Grid>
<Grid item xs={6}>
<Tooltip
placement='top'
enterTouchDelay={0}
enterDelay={1000}
enterNextDelay={2000}
title={t('Save messages as a JSON file')}
>
<Button
size='small'
color='primary'
variant='contained'
onClick={() => saveAsJson('messages_' + orderId + '.json', messages)}
>
<div style={{ width: 28, height: 20 }}>
<ExportIcon sx={{ width: 18, height: 18 }} />
</div>
{t('Messages')}
<div style={{ width: 26, height: 20 }}>
<ForumIcon sx={{ width: 20, height: 20 }} />
</div>
</Button>
</Tooltip>
</Grid>
</Grid>
</DialogContent>
<DialogActions>
<Button onClick={onClickBack} autoFocus>{t("Go back")}</Button>
<Button onClick={onClickBack} autoFocus>
{t('Go back')}
</Button>
</DialogActions>
</Dialog>
)
}
);
};
export default AuditPGPDialog;

View File

@ -1,5 +1,5 @@
import React from "react";
import { useTranslation } from "react-i18next";
import React from 'react';
import { useTranslation } from 'react-i18next';
import {
Dialog,
DialogContent,
@ -12,22 +12,19 @@ import {
ListItemButton,
Tooltip,
Typography,
} from "@mui/material";
} from '@mui/material';
import SendIcon from '@mui/icons-material/Send';
import GitHubIcon from '@mui/icons-material/GitHub';
import TwitterIcon from '@mui/icons-material/Twitter';
import RedditIcon from '@mui/icons-material/Reddit';
import Flags from 'country-flag-icons/react/3x2'
import Flags from 'country-flag-icons/react/3x2';
type Props = {
isOpen: boolean;
handleClickCloseCommunity: () => void;
}
};
const CommunityDialog = ({
isOpen,
handleClickCloseCommunity,
}: Props): JSX.Element => {
const CommunityDialog = ({ isOpen, handleClickCloseCommunity }: Props): JSX.Element => {
const { t } = useTranslation();
const flagProps = {
@ -35,7 +32,7 @@ const CommunityDialog = ({
height: 30,
opacity: 0.85,
style: {
filter: "drop-shadow(2px 2px 2px #444444)",
filter: 'drop-shadow(2px 2px 2px #444444)',
},
};
@ -43,136 +40,139 @@ const CommunityDialog = ({
<Dialog
open={isOpen}
onClose={handleClickCloseCommunity}
aria-labelledby="community-dialog-title"
aria-describedby="community-description"
aria-labelledby='community-dialog-title'
aria-describedby='community-description'
>
<DialogContent>
<Typography component="h5" variant="h5">
{t("Community")}
<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 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 dense>
<Divider/>
<Divider />
<ListItemButton
component="a"
target="_blank"
href="https://twitter.com/robosats"
rel="noreferrer"
component='a'
target='_blank'
href='https://twitter.com/robosats'
rel='noreferrer'
>
<ListItemIcon>
<TwitterIcon color="primary" sx={{height:32,width:32}}/>
<TwitterIcon color='primary' sx={{ height: 32, width: 32 }} />
</ListItemIcon>
<ListItemText
primary={t("Follow RoboSats in Twitter")}
secondary={t("Twitter Official Account")}
primary={t('Follow RoboSats in Twitter')}
secondary={t('Twitter Official Account')}
/>
</ListItemButton>
<Divider/>
<Divider />
<ListItemButton
component="a"
target="_blank"
href="https://reddit.com/r/robosats"
rel="noreferrer"
component='a'
target='_blank'
href='https://reddit.com/r/robosats'
rel='noreferrer'
>
<ListItemIcon>
<RedditIcon color="primary" sx={{height:35,width:35}}/>
<RedditIcon color='primary' sx={{ height: 35, width: 35 }} />
</ListItemIcon>
<ListItemText
primary={t("Join RoboSats' Subreddit")}
secondary={t("RoboSats in Reddit")}
secondary={t('RoboSats in Reddit')}
/>
</ListItemButton>
<Divider/>
<Divider />
<ListItem>
<ListItemIcon>
<SendIcon color="primary" sx={{height:32,width:32}}/>
<SendIcon color='primary' sx={{ height: 32, width: 32 }} />
</ListItemIcon>
<ListItemText secondary={t("RoboSats Telegram Communities")}>
<Tooltip title={t("Join RoboSats Spanish speaking community!") || ""}>
<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"
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!") || ""}>
<Tooltip title={t('Join RoboSats Russian speaking community!') || ''}>
<IconButton
component="a"
target="_blank"
href="https://t.me/robosats_ru"
rel="noreferrer"
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!") || ""}>
<Tooltip title={t('Join RoboSats Chinese speaking community!') || ''}>
<IconButton
component="a"
target="_blank"
href="https://t.me/robosats_cn"
rel="noreferrer"
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!") || ""}>
<Tooltip title={t('Join RoboSats English speaking community!') || ''}>
<IconButton
component="a"
target="_blank"
href="https://t.me/robosats"
rel="noreferrer"
component='a'
target='_blank'
href='https://t.me/robosats'
rel='noreferrer'
>
<Flags.US {...flagProps} />
</IconButton>
</Tooltip>
<Tooltip title={t("Join RoboSats Portuguese speaking community!") || ""}>
<Tooltip title={t('Join RoboSats Portuguese speaking community!') || ''}>
<IconButton
component="a"
target="_blank"
href="https://t.me/robosats_pt"
rel="noreferrer"
component='a'
target='_blank'
href='https://t.me/robosats_pt'
rel='noreferrer'
>
<Flags.BR {...flagProps} />
</IconButton>
</Tooltip>
</ListItemText>
</ListItem>
<Divider/>
<Divider />
<ListItemButton
component="a"
target="_blank"
href="https://github.com/Reckless-Satoshi/robosats/issues"
rel="noreferrer"
>
component='a'
target='_blank'
href='https://github.com/Reckless-Satoshi/robosats/issues'
rel='noreferrer'
>
<ListItemIcon>
<GitHubIcon color="primary" sx={{height:32,width:32}}/>
<GitHubIcon color='primary' sx={{ height: 32, width: 32 }} />
</ListItemIcon>
<ListItemText
primary={t("Tell us about a new feature or a bug")}
secondary={t("Github Issues - The Robotic Satoshis Open Source Project")}
primary={t('Tell us about a new feature or a bug')}
secondary={t('Github Issues - The Robotic Satoshis Open Source Project')}
/>
</ListItemButton>
</List>

View File

@ -1,5 +1,5 @@
import React from "react";
import { useTranslation } from "react-i18next";
import React from 'react';
import { useTranslation } from 'react-i18next';
import {
Dialog,
@ -11,7 +11,7 @@ import {
ListItem,
ListItemIcon,
Typography,
} from "@mui/material";
} from '@mui/material';
import InventoryIcon from '@mui/icons-material/Inventory';
import SellIcon from '@mui/icons-material/Sell';
@ -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/prettyNumbers';
type Props = {
isOpen: boolean;
@ -34,7 +34,7 @@ type Props = {
makerFee: number;
takerFee: number;
swapFeeRate: number;
}
};
const ExchangeSummaryDialog = ({
isOpen,
@ -50,106 +50,108 @@ const ExchangeSummaryDialog = ({
}: Props): JSX.Element => {
const { t } = useTranslation();
if (swapFeeRate === null || swapFeeRate === undefined) {
swapFeeRate = 0
swapFeeRate = 0;
}
return (
<Dialog
open={isOpen}
onClose={handleClickCloseExchangeSummary}
aria-labelledby="exchange-summary-title"
aria-describedby="exchange-summary-description"
aria-labelledby='exchange-summary-title'
aria-describedby='exchange-summary-description'
>
<DialogContent>
<Typography component="h5" variant="h5">{t("Exchange Summary")}</Typography>
<Typography component='h5' variant='h5'>
{t('Exchange Summary')}
</Typography>
<List dense>
<ListItem >
<ListItem>
<ListItemIcon>
<InventoryIcon />
</ListItemIcon>
<ListItemText
primaryTypographyProps={{fontSize: '14px'}}
secondaryTypographyProps={{fontSize: '12px'}}
primaryTypographyProps={{ fontSize: '14px' }}
secondaryTypographyProps={{ fontSize: '12px' }}
primary={numPublicBuyOrders}
secondary={t("Public buy orders")}
secondary={t('Public buy orders')}
/>
</ListItem>
<Divider />
<ListItem >
<ListItem>
<ListItemIcon>
<SellIcon />
</ListItemIcon>
<ListItemText
primaryTypographyProps={{fontSize: '14px'}}
secondaryTypographyProps={{fontSize: '12px'}}
primaryTypographyProps={{ fontSize: '14px' }}
secondaryTypographyProps={{ fontSize: '12px' }}
primary={numPublicSellOrders}
secondary={t("Public sell orders")}
secondary={t('Public sell orders')}
/>
</ListItem>
<Divider/>
<Divider />
<ListItem >
<ListItem>
<ListItemIcon>
<BookIcon />
</ListItemIcon>
<ListItemText
primaryTypographyProps={{fontSize: '14px'}}
secondaryTypographyProps={{fontSize: '12px'}}
primaryTypographyProps={{ fontSize: '14px' }}
secondaryTypographyProps={{ fontSize: '12px' }}
primary={`${pn(bookLiquidity)} Sats`}
secondary={t("Book liquidity")}
secondary={t('Book liquidity')}
/>
</ListItem>
<Divider/>
<Divider />
<ListItem >
<ListItem>
<ListItemIcon>
<SmartToyIcon />
</ListItemIcon>
<ListItemText
primaryTypographyProps={{fontSize: '14px'}}
secondaryTypographyProps={{fontSize: '12px'}}
primaryTypographyProps={{ fontSize: '14px' }}
secondaryTypographyProps={{ fontSize: '12px' }}
primary={activeRobotsToday}
secondary={t("Today active robots")}
secondary={t('Today active robots')}
/>
</ListItem>
<Divider/>
<Divider />
<ListItem >
<ListItem>
<ListItemIcon>
<PriceChangeIcon />
</ListItemIcon>
<ListItemText
primaryTypographyProps={{fontSize: '14px'}}
secondaryTypographyProps={{fontSize: '12px'}}
primaryTypographyProps={{ fontSize: '14px' }}
secondaryTypographyProps={{ fontSize: '12px' }}
primary={`${lastDayNonkycBtcPremium}%`}
secondary={t("24h non-KYC bitcoin premium")}
secondary={t('24h non-KYC bitcoin premium')}
/>
</ListItem>
<Divider/>
<Divider />
<ListItem >
<ListItem>
<ListItemIcon>
<PercentIcon />
</ListItemIcon>
<Grid container >
<Grid container>
<Grid item xs={6}>
<ListItemText
primaryTypographyProps={{fontSize: '14px'}}
secondaryTypographyProps={{fontSize: '12px'}}
secondary={t("Maker fee")}
primaryTypographyProps={{ fontSize: '14px' }}
secondaryTypographyProps={{ fontSize: '12px' }}
secondary={t('Maker fee')}
>
{(makerFee * 100).toFixed(3)}%
</ListItemText>
@ -157,9 +159,9 @@ const ExchangeSummaryDialog = ({
<Grid item xs={6}>
<ListItemText
primaryTypographyProps={{fontSize: '14px'}}
secondaryTypographyProps={{fontSize: '12px'}}
secondary={t("Taker fee")}
primaryTypographyProps={{ fontSize: '14px' }}
secondaryTypographyProps={{ fontSize: '12px' }}
secondary={t('Taker fee')}
>
{(takerFee * 100).toFixed(3)}%
</ListItemText>
@ -169,19 +171,18 @@ const ExchangeSummaryDialog = ({
<Divider />
<ListItem >
<ListItem>
<ListItemIcon>
<LinkIcon />
</ListItemIcon>
<ListItemText
primaryTypographyProps={{fontSize: '14px'}}
secondaryTypographyProps={{fontSize: '12px'}}
primaryTypographyProps={{ fontSize: '14px' }}
secondaryTypographyProps={{ fontSize: '12px' }}
primary={`${swapFeeRate.toPrecision(3)}%`}
secondary={t("Current onchain payout fee")}
secondary={t('Current onchain payout fee')}
/>
</ListItem>
</List>
</DialogContent>
</Dialog>

View File

@ -1,19 +1,19 @@
import React from "react";
import { useTranslation } from "react-i18next";
import React from 'react';
import { useTranslation } from 'react-i18next';
import {
Dialog,
Typography,
Link,
DialogActions,
DialogContent,
Button,
Typography,
Link,
DialogActions,
DialogContent,
Button,
Grid,
Accordion,
AccordionDetails,
AccordionSummary,
} from "@mui/material"
import SmoothImage from 'react-smooth-image'
import MediaQuery from 'react-responsive'
} from '@mui/material';
import SmoothImage from 'react-smooth-image';
import MediaQuery from 'react-responsive';
// Icons
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
@ -22,213 +22,292 @@ type Props = {
maxAmount: string;
open: boolean;
onClose: () => void;
}
};
const InfoDialog = ({
maxAmount,
open,
onClose,
}: Props): JSX.Element => {
const InfoDialog = ({ maxAmount, 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">
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>
<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>
<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}>
<SmoothImage
src={window.location.origin + '/static/assets/images/v0.1.2-04.png'}
imageStyles={{
borderRadius: '50%',
border: '2px solid #555',
filter: 'drop-shadow(1px 1px 1px #000000)',
height: '170px',
width: '170px',
}}
/>
</Grid>
</Grid>
<Grid item xs={4}>
<SmoothImage
src={window.location.origin +'/static/assets/images/v0.1.2-04.png'}
imageStyles={{borderRadius: "50%",
border: "2px solid #555",
filter: "drop-shadow(1px 1px 1px #000000)",
height: "170px",
width: "170px"}}
/>
</Grid>
</Grid>
<div style={{height:15}}/>
<div style={{ height: 15 }} />
</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>
<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'}
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>
<p>
{t('RoboSats is an open source project ')}{' '}
<Link href='https://github.com/reckless-satoshi/robosats'>{t('(GitHub).')}</Link>
</p>
</Typography>
</MediaQuery>
<Accordion disableGutters={true}>
<AccordionSummary expandIcon={<ExpandMoreIcon/>}>
<Typography>
{t("How does it work?")}
</Typography>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography>{t('How does it work?')}</Typography>
</AccordionSummary>
<AccordionDetails>
<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 target="_blank" href='https://learn.robosats.com/docs/trade-pipeline/'>{t("How it works")}</Link>.
{" "+t("You can also check the full guide in ")}
<Link target="_blank" href='https://learn.robosats.com/read/en'>{t("How to use")}</Link>.</p>
<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 target='_blank' href='https://learn.robosats.com/docs/trade-pipeline/'>
{t('How it works')}
</Link>
.{' ' + t('You can also check the full guide in ')}
<Link target='_blank' href='https://learn.robosats.com/read/en'>
{t('How to use')}
</Link>
.
</p>
</Typography>
</AccordionDetails>
</Accordion>
<Accordion disableGutters={true}>
<AccordionSummary expandIcon={<ExpandMoreIcon/>}>
<Typography>
{t("What payment methods are accepted?")
}</Typography>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography>{t('What payment methods are accepted?')}</Typography>
</AccordionSummary>
<AccordionDetails>
<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 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>
</AccordionDetails>
</Accordion>
<Accordion disableGutters={true}>
<AccordionSummary expandIcon={<ExpandMoreIcon/>}>
<Typography>
{t("Are there trade limits?")}
</Typography>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography>{t('Are there trade limits?')}</Typography>
</AccordionSummary>
<AccordionDetails>
<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: maxAmount})} </p>
<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: maxAmount },
)}{' '}
</p>
</Typography>
</AccordionDetails>
</Accordion>
<Accordion disableGutters={true}>
<AccordionSummary expandIcon={<ExpandMoreIcon/>}>
<Typography>
{t("What are the fees?")}
</Typography>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography>{t('What are the fees?')}</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography component="div" variant="body2">
<p>{t("RoboSats total fee for an order is {{tradeFee}}%. This fee is split to be covered by both: the order maker ({{makerFee}}%) and the order taker ({{takerFee}}%). In case an onchain address is used to received the Sats a variable swap fee applies. Check the exchange details by tapping on the bottom bar icon to see the current swap fee.",{tradeFee:"0.2", makerFee:"0.025", takerFee: "0.175"})} </p>
<p>{t("Be aware your fiat payment provider might charge extra fees. In any case, the buyer bears the costs of sending fiat. That includes banking charges, transfer fees and foreign exchange spreads. The seller must receive exactly the amount stated in the order details.")} </p>
<Typography component='div' variant='body2'>
<p>
{t(
'RoboSats total fee for an order is {{tradeFee}}%. This fee is split to be covered by both: the order maker ({{makerFee}}%) and the order taker ({{takerFee}}%). In case an onchain address is used to received the Sats a variable swap fee applies. Check the exchange details by tapping on the bottom bar icon to see the current swap fee.',
{ tradeFee: '0.2', makerFee: '0.025', takerFee: '0.175' },
)}{' '}
</p>
<p>
{t(
'Be aware your fiat payment provider might charge extra fees. In any case, the buyer bears the costs of sending fiat. That includes banking charges, transfer fees and foreign exchange spreads. The seller must receive exactly the amount stated in the order details.',
)}{' '}
</p>
</Typography>
</AccordionDetails>
</Accordion>
<Accordion disableGutters={true}>
<AccordionSummary expandIcon={<ExpandMoreIcon/>}>
<Typography>
{t("Is RoboSats private?")}
</Typography>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography>{t('Is RoboSats private?')}</Typography>
</AccordionSummary>
<AccordionDetails>
<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 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>
</AccordionDetails>
</Accordion>
<Accordion disableGutters={true}>
<AccordionSummary expandIcon={<ExpandMoreIcon/>}>
<Typography>
{t("What are the risks?")}
</Typography>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography>{t('What are the risks?')}</Typography>
</AccordionSummary>
<AccordionDetails>
<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 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>
</AccordionDetails>
</Accordion>
<Accordion disableGutters={true}>
<AccordionSummary expandIcon={<ExpandMoreIcon/>}>
<Typography>
{t("What is the trust model?")}
</Typography>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography>{t('What is the trust model?')}</Typography>
</AccordionSummary>
<AccordionDetails>
<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 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>
</AccordionDetails>
</Accordion>
<Accordion disableGutters={true}>
<AccordionSummary expandIcon={<ExpandMoreIcon/>}>
<Typography>
{t("What happens if RoboSats suddenly disappears?")}
</Typography>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography>{t('What happens if RoboSats suddenly disappears?')}</Typography>
</AccordionSummary>
<AccordionDetails>
<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 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>
</AccordionDetails>
</Accordion>
<Accordion disableGutters={true}>
<AccordionSummary expandIcon={<ExpandMoreIcon/>}>
<Typography>
{t("Is RoboSats legal in my country?")}
</Typography>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography>{t('Is RoboSats legal in my country?')}</Typography>
</AccordionSummary>
<AccordionDetails>
<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 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>
</AccordionDetails>
</Accordion>
<Accordion disableGutters={true}>
<AccordionSummary expandIcon={<ExpandMoreIcon/>}>
<Typography>
{t("Disclaimer")}
</Typography>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography>{t('Disclaimer')}</Typography>
</AccordionSummary>
<AccordionDetails>
<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 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>
</AccordionDetails>
</Accordion>
<DialogActions>
<Button onClick={onClose}>{t("Close")}</Button>
<Button onClick={onClose}>{t('Close')}</Button>
</DialogActions>
</DialogContent>
</Dialog>
)
}
);
};
export default InfoDialog;

View File

@ -1,50 +1,49 @@
import React from "react";
import { useTranslation } from "react-i18next";
import React from 'react';
import { useTranslation } from 'react-i18next';
import {
Dialog,
DialogTitle,
DialogActions,
DialogActions,
DialogContent,
DialogContentText,
Button,
Link,
} from "@mui/material"
Link,
} from '@mui/material';
type Props = {
open: boolean;
onClose: () => void;
}
};
const LearnDialog = ({
open,
onClose,
}: Props): JSX.Element => {
const LearnDialog = ({ open, onClose }: Props): JSX.Element => {
const { t } = useTranslation();
return (
<Dialog
open={open}
onClose={onClose}
>
<DialogTitle>
{t("Learn RoboSats")}
</DialogTitle>
<Dialog open={open} onClose={onClose}>
<DialogTitle>{t('Learn RoboSats')}</DialogTitle>
<DialogContent>
<DialogContentText>
{t("You are about to visit Learn RoboSats. It hosts tutorials and documentation to help you learn how to use RoboSats and understand how it works.")}
{t(
'You are about to visit Learn RoboSats. It hosts tutorials and documentation to help you learn how to use RoboSats and understand how it works.',
)}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={onClose}>{t("Back")}</Button>
<Button onClick={onClose} autoFocus component={Link} href="https://learn.robosats.com" target="_blank">
<Button onClick={onClose}>{t('Back')}</Button>
<Button
onClick={onClose}
autoFocus
component={Link}
href='https://learn.robosats.com'
target='_blank'
>
{t("Let's go!")}
</Button>
</DialogActions>
</Dialog>
)
}
);
};
export default LearnDialog;

View File

@ -1,48 +1,43 @@
import React from "react";
import { useTranslation } from "react-i18next";
import React from 'react';
import { useTranslation } from 'react-i18next';
import {
Dialog,
DialogTitle,
DialogActions,
DialogActions,
DialogContent,
DialogContentText,
Button,
} from "@mui/material"
import { Link } from 'react-router-dom'
Button,
} from '@mui/material';
import { Link } from 'react-router-dom';
type Props = {
open: boolean;
onClose: () => void;
}
};
const NoRobotDialog = ({
open,
onClose,
}: Props): JSX.Element => {
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>
<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")}
{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>
<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

@ -1,6 +1,6 @@
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { Link as LinkRouter } from "react-router-dom";
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Link as LinkRouter } from 'react-router-dom';
import {
Avatar,
@ -23,19 +23,19 @@ import {
TextField,
Tooltip,
Typography,
} from "@mui/material";
} from '@mui/material';
import BoltIcon from "@mui/icons-material/Bolt";
import NumbersIcon from "@mui/icons-material/Numbers";
import PasswordIcon from "@mui/icons-material/Password";
import ContentCopy from "@mui/icons-material/ContentCopy";
import PersonAddAltIcon from "@mui/icons-material/PersonAddAlt";
import EmojiEventsIcon from "@mui/icons-material/EmojiEvents";
import { UserNinjaIcon, BitcoinIcon } from "../Icons";
import BoltIcon from '@mui/icons-material/Bolt';
import NumbersIcon from '@mui/icons-material/Numbers';
import PasswordIcon from '@mui/icons-material/Password';
import ContentCopy from '@mui/icons-material/ContentCopy';
import PersonAddAltIcon from '@mui/icons-material/PersonAddAlt';
import EmojiEventsIcon from '@mui/icons-material/EmojiEvents';
import { UserNinjaIcon, BitcoinIcon } from '../Icons';
import { getCookie } from "../../utils/cookies";
import { copyToClipboard } from "../../utils/clipboard";
import { getWebln } from "../../utils/webln";
import { getCookie } from '../../utils/cookies';
import { copyToClipboard } from '../../utils/clipboard';
import { getWebln } from '../../utils/webln';
type Props = {
isOpen: boolean;
@ -44,7 +44,7 @@ type Props = {
activeOrderId: string | number;
lastOrderId: string | number;
referralCode: string;
handleSubmitInvoiceClicked: (e:any, invoice: string) => void;
handleSubmitInvoiceClicked: (e: any, invoice: string) => void;
host: string;
showRewardsSpinner: boolean;
withdrawn: boolean;
@ -53,7 +53,7 @@ type Props = {
stealthInvoices: boolean;
handleSetStealthInvoice: (stealth: boolean) => void;
setAppState: (state: any) => void; // TODO: move to a ContextProvider
}
};
const ProfileDialog = ({
isOpen,
@ -74,24 +74,23 @@ const ProfileDialog = ({
}: Props): JSX.Element => {
const { t } = useTranslation();
const [rewardInvoice, setRewardInvoice] = useState<string>("");
const [rewardInvoice, setRewardInvoice] = useState<string>('');
const [showRewards, setShowRewards] = useState<boolean>(false);
const [openClaimRewards, setOpenClaimRewards] = useState<boolean>(false);
const [weblnEnabled, setWeblnEnabled] = useState<boolean>(false)
const [weblnEnabled, setWeblnEnabled] = useState<boolean>(false);
useEffect(() => {
getWebln()
.then((webln) => {
setWeblnEnabled(webln !== undefined)
})
}, [showRewards])
getWebln().then((webln) => {
setWeblnEnabled(webln !== undefined);
});
}, [showRewards]);
const copyTokenHandler = () => {
const robotToken = getCookie("robot_token");
const robotToken = getCookie('robot_token');
if (robotToken) {
copyToClipboard(robotToken);
setAppState({copiedToken:true});
setAppState({ copiedToken: true });
}
};
@ -99,59 +98,71 @@ const ProfileDialog = ({
copyToClipboard(`http://${host}/ref/${referralCode}`);
};
const handleWeblnInvoiceClicked = async (e: any) =>{
const handleWeblnInvoiceClicked = async (e: any) => {
e.preventDefault();
if (earnedRewards) {
const webln = await getWebln();
const invoice = webln.makeInvoice(earnedRewards).then(() => {
if (invoice) {
handleSubmitInvoiceClicked(e, invoice.paymentRequest)
handleSubmitInvoiceClicked(e, invoice.paymentRequest);
}
})
});
}
}
};
return (
<Dialog
open={isOpen}
onClose={handleClickCloseProfile}
aria-labelledby="profile-title"
aria-describedby="profile-description"
aria-labelledby='profile-title'
aria-describedby='profile-description'
>
<DialogContent>
<Typography component="h5" variant="h5">{t("Your Profile")}</Typography>
<Typography component='h5' variant='h5'>
{t('Your Profile')}
</Typography>
<List>
<Divider/>
<Divider />
<ListItem className="profileNickname">
<ListItemText secondary={t("Your robot")}>
<Typography component="h6" variant="h6">
<ListItem className='profileNickname'>
<ListItemText secondary={t('Your robot')}>
<Typography component='h6' variant='h6'>
{nickname ? (
<div style={{position:"relative", left:"-7px"}}>
<div style={{display:"flex", alignItems:"center", justifyContent:"left", flexWrap:"wrap", width:300}}>
<BoltIcon sx={{ color: "#fcba03", height: "28px", width: "24px"}} />
<div style={{ position: 'relative', left: '-7px' }}>
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'left',
flexWrap: 'wrap',
width: 300,
}}
>
<BoltIcon sx={{ color: '#fcba03', height: '28px', width: '24px' }} />
<a>{nickname}</a>
<BoltIcon sx={{ color: "#fcba03", height: "28px", width: "24px"}} />
<BoltIcon sx={{ color: '#fcba03', height: '28px', width: '24px' }} />
</div>
</div>
)
: null}
) : null}
</Typography>
</ListItemText>
<ListItemAvatar>
<Avatar className="profileAvatar"
sx={{ width: 65, height:65 }}
<Avatar
className='profileAvatar'
sx={{ width: 65, height: 65 }}
alt={nickname}
src={nickname ? `${window.location.origin}/static/assets/avatars/${nickname}.png` : ""}
src={
nickname ? `${window.location.origin}/static/assets/avatars/${nickname}.png` : ''
}
/>
</ListItemAvatar>
</ListItem>
<Divider/>
<Divider />
{activeOrderId ? (
<ListItemButton
@ -160,94 +171,94 @@ const ProfileDialog = ({
component={LinkRouter}
>
<ListItemIcon>
<Badge badgeContent="" color="primary">
<NumbersIcon color="primary" />
<Badge badgeContent='' color='primary'>
<NumbersIcon color='primary' />
</Badge>
</ListItemIcon>
<ListItemText
primary={t("One active order #{{orderID}}", { orderID: activeOrderId })}
secondary={t("Your current order")}
primary={t('One active order #{{orderID}}', { orderID: activeOrderId })}
secondary={t('Your current order')}
/>
</ListItemButton>
) :
lastOrderId ? (
<ListItemButton
onClick={handleClickCloseProfile}
to={`/order/${lastOrderId}`}
component={LinkRouter}
>
<ListItemIcon>
<NumbersIcon color="primary" />
</ListItemIcon>
<ListItemText
primary={t("Your last order #{{orderID}}", { orderID: lastOrderId })}
secondary={t("Inactive order")}
/>
</ListItemButton>
) : (
<ListItem>
<ListItemIcon>
<NumbersIcon />
</ListItemIcon>
<ListItemText
primary={t("No active orders")}
secondary={t("You do not have previous orders")}
/>
</ListItem>
)
}
) : lastOrderId ? (
<ListItemButton
onClick={handleClickCloseProfile}
to={`/order/${lastOrderId}`}
component={LinkRouter}
>
<ListItemIcon>
<NumbersIcon color='primary' />
</ListItemIcon>
<ListItemText
primary={t('Your last order #{{orderID}}', { orderID: lastOrderId })}
secondary={t('Inactive order')}
/>
</ListItemButton>
) : (
<ListItem>
<ListItemIcon>
<NumbersIcon />
</ListItemIcon>
<ListItemText
primary={t('No active orders')}
secondary={t('You do not have previous orders')}
/>
</ListItem>
)}
<ListItem>
<ListItemIcon>
<PasswordIcon />
</ListItemIcon>
<ListItemText secondary={t("Your token (will not remain here)")}>
{getCookie("robot_token") ? (
<ListItemText secondary={t('Your token (will not remain here)')}>
{getCookie('robot_token') ? (
<TextField
disabled
sx={{width:"100%", maxWidth:"450px"}}
label={t("Back it up!")}
value={getCookie("robot_token") }
variant="filled"
size="small"
sx={{ width: '100%', maxWidth: '450px' }}
label={t('Back it up!')}
value={getCookie('robot_token')}
variant='filled'
size='small'
InputProps={{
endAdornment:
<Tooltip
disableHoverListener
enterTouchDelay={0}
title={t("Copied!") || ""}
>
<IconButton onClick={copyTokenHandler}>
<ContentCopy color="inherit" />
</IconButton>
</Tooltip>,
}}
endAdornment: (
<Tooltip disableHoverListener enterTouchDelay={0} title={t('Copied!') || ''}>
<IconButton onClick={copyTokenHandler}>
<ContentCopy color='inherit' />
</IconButton>
</Tooltip>
),
}}
/>
) :
t("Cannot remember")
}
) : (
t('Cannot remember')
)}
</ListItemText>
</ListItem>
<Divider/>
<Divider />
<ListItem>
<ListItemIcon>
<UserNinjaIcon/>
<UserNinjaIcon />
</ListItemIcon>
<ListItemText>
<Tooltip placement="top" enterTouchDelay={0} title={t("Stealth lightning invoices do not contain details about the trade except an order reference. Enable this setting if you don't want to disclose details to a custodial lightning wallet.")}>
<Tooltip
placement='top'
enterTouchDelay={0}
title={t(
"Stealth lightning invoices do not contain details about the trade except an order reference. Enable this setting if you don't want to disclose details to a custodial lightning wallet.",
)}
>
<Grid item>
<FormControlLabel
labelPlacement="end"
label={t("Use stealth invoices")}
labelPlacement='end'
label={t('Use stealth invoices')}
control={
<Switch
checked={stealthInvoices}
onChange={() => handleSetStealthInvoice(!stealthInvoices)
}
onChange={() => handleSetStealthInvoice(!stealthInvoices)}
/>
}
/>
@ -258,21 +269,19 @@ const ProfileDialog = ({
<ListItem>
<ListItemIcon>
<BitcoinIcon/>
<BitcoinIcon />
</ListItemIcon>
<ListItemText>
<FormControlLabel
labelPlacement="end"
labelPlacement='end'
label={
<div style={{display:'flex', alignItems:'center'}}>
{t("Rewards and compensations")}
</div>}
<div style={{ display: 'flex', alignItems: 'center' }}>
{t('Rewards and compensations')}
</div>
}
control={
<Switch
checked={showRewards}
onChange={()=> setShowRewards(!showRewards)}
/>
<Switch checked={showRewards} onChange={() => setShowRewards(!showRewards)} />
}
/>
</ListItemText>
@ -285,24 +294,25 @@ const ProfileDialog = ({
<PersonAddAltIcon />
</ListItemIcon>
<ListItemText secondary={t("Share to earn 100 Sats per trade")}>
<ListItemText secondary={t('Share to earn 100 Sats per trade')}>
<TextField
label={t("Your referral link")}
value={host+'/ref/'+referralCode}
size="small"
label={t('Your referral link')}
value={host + '/ref/' + referralCode}
size='small'
InputProps={{
endAdornment:
<Tooltip
disableHoverListener
enterTouchDelay={0}
title={t("Copied!") || ""}
>
<IconButton onClick={copyReferralCodeHandler}>
<ContentCopy />
</IconButton>
</Tooltip>,
}}
/>
endAdornment: (
<Tooltip
disableHoverListener
enterTouchDelay={0}
title={t('Copied!') || ''}
>
<IconButton onClick={copyReferralCodeHandler}>
<ContentCopy />
</IconButton>
</Tooltip>
),
}}
/>
</ListItemText>
</ListItem>
@ -312,7 +322,7 @@ const ProfileDialog = ({
</ListItemIcon>
{!openClaimRewards ? (
<ListItemText secondary={t("Your earned rewards")}>
<ListItemText secondary={t('Your earned rewards')}>
<Grid container>
<Grid item xs={9}>
<Typography>{`${earnedRewards} Sats`}</Typography>
@ -322,54 +332,56 @@ const ProfileDialog = ({
<Button
disabled={earnedRewards === 0 ? true : false}
onClick={() => setOpenClaimRewards(true)}
variant="contained"
size="small"
variant='contained'
size='small'
>
{t("Claim")}
{t('Claim')}
</Button>
</Grid>
</Grid>
</ListItemText>
) : (
<form noValidate style={{maxWidth: 270}}>
<Grid container style={{ display: "flex", alignItems: "stretch"}}>
<Grid item style={{ display: "flex", maxWidth:160}} >
<form noValidate style={{ maxWidth: 270 }}>
<Grid container style={{ display: 'flex', alignItems: 'stretch' }}>
<Grid item style={{ display: 'flex', maxWidth: 160 }}>
<TextField
error={badInvoice ? true : false}
helperText={badInvoice ? badInvoice : "" }
label={t("Invoice for {{amountSats}} Sats", { amountSats: earnedRewards })}
size="small"
helperText={badInvoice ? badInvoice : ''}
label={t('Invoice for {{amountSats}} Sats', {
amountSats: earnedRewards,
})}
size='small'
value={rewardInvoice}
onChange={e => {
onChange={(e) => {
setRewardInvoice(e.target.value);
}}
/>
</Grid>
<Grid item alignItems="stretch" style={{ display: "flex", maxWidth:80}}>
<Grid item alignItems='stretch' style={{ display: 'flex', maxWidth: 80 }}>
<Button
sx={{maxHeight:38}}
sx={{ maxHeight: 38 }}
onClick={(e) => handleSubmitInvoiceClicked(e, rewardInvoice)}
variant="contained"
color="primary"
size="small"
type="submit"
variant='contained'
color='primary'
size='small'
type='submit'
>
{t("Submit")}
{t('Submit')}
</Button>
</Grid>
</Grid>
{weblnEnabled && (
<Grid container style={{ display: "flex", alignItems: "stretch"}}>
<Grid item alignItems="stretch" style={{ display: "flex", maxWidth:240}}>
<Grid container style={{ display: 'flex', alignItems: 'stretch' }}>
<Grid item alignItems='stretch' style={{ display: 'flex', maxWidth: 240 }}>
<Button
sx={{maxHeight:38, minWidth: 230}}
sx={{ maxHeight: 38, minWidth: 230 }}
onClick={(e) => handleWeblnInvoiceClicked(e)}
variant="contained"
color="primary"
size="small"
type="submit"
variant='contained'
color='primary'
size='small'
type='submit'
>
{t("Generate with Webln")}
{t('Generate with Webln')}
</Button>
</Grid>
</Grid>
@ -379,15 +391,15 @@ const ProfileDialog = ({
</ListItem>
{showRewardsSpinner && (
<div style={{display: "flex", justifyContent: "center"}}>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<CircularProgress />
</div>
)}
{withdrawn && (
<div style={{display: "flex", justifyContent: "center"}}>
<Typography color="primary" variant="body2">
<b>{t("There it goes, thank you!🥇")}</b>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<Typography color='primary' variant='body2'>
<b>{t('There it goes, thank you!🥇')}</b>
</Typography>
</div>
)}

View File

@ -1,5 +1,5 @@
import React from "react";
import { useTranslation } from "react-i18next";
import React from 'react';
import { useTranslation } from 'react-i18next';
import {
Dialog,
@ -11,19 +11,19 @@ import {
ListItem,
ListItemIcon,
Typography,
} from "@mui/material";
} from '@mui/material';
import BoltIcon from "@mui/icons-material/Bolt";
import PublicIcon from "@mui/icons-material/Public";
import DnsIcon from "@mui/icons-material/Dns";
import WebIcon from "@mui/icons-material/Web";
import FavoriteIcon from "@mui/icons-material/Favorite";
import GitHubIcon from "@mui/icons-material/GitHub";
import EqualizerIcon from "@mui/icons-material/Equalizer";
import BoltIcon from '@mui/icons-material/Bolt';
import PublicIcon from '@mui/icons-material/Public';
import DnsIcon from '@mui/icons-material/Dns';
import WebIcon from '@mui/icons-material/Web';
import FavoriteIcon from '@mui/icons-material/Favorite';
import GitHubIcon from '@mui/icons-material/GitHub';
import EqualizerIcon from '@mui/icons-material/Equalizer';
import { AmbossIcon, BitcoinSignIcon } from "../Icons";
import { AmbossIcon, BitcoinSignIcon } from '../Icons';
import { pn } from "../../utils/prettyNumbers";
import { pn } from '../../utils/prettyNumbers';
type Props = {
isOpen: boolean;
@ -37,7 +37,7 @@ type Props = {
robosatsRunningCommitHash: string;
lastDayVolume: number;
lifetimeVolume: number;
}
};
const StatsDialog = ({
isOpen,
@ -58,11 +58,13 @@ const StatsDialog = ({
<Dialog
open={isOpen}
onClose={handleClickCloseStatsForNerds}
aria-labelledby="stats-for-nerds-dialog-title"
aria-describedby="stats-for-nerds-description"
aria-labelledby='stats-for-nerds-dialog-title'
aria-describedby='stats-for-nerds-description'
>
<DialogContent>
<Typography component="h5" variant="h5">{t("Stats For Nerds")}</Typography>
<Typography component='h5' variant='h5'>
{t('Stats For Nerds')}
</Typography>
<List dense>
<Divider />
@ -71,21 +73,21 @@ const StatsDialog = ({
<ListItemIcon>
<BoltIcon />
</ListItemIcon>
<ListItemText primary={lndVersion} secondary={t("LND version")} />
<ListItemText primary={lndVersion} secondary={t('LND version')} />
</ListItem>
<Divider />
{network === "testnet" ? (
{network === 'testnet' ? (
<ListItem>
<ListItemIcon>
<DnsIcon />
</ListItemIcon>
<ListItemText secondary={nodeAlias}>
<Link
target="_blank"
target='_blank'
href={`https://1ml.com/testnet/node/${nodeId}`}
rel="noreferrer"
rel='noreferrer'
>
{`${nodeId.slice(0, 12)}... (1ML)`}
</Link>
@ -97,11 +99,7 @@ const StatsDialog = ({
<AmbossIcon />
</ListItemIcon>
<ListItemText secondary={nodeAlias}>
<Link
target="_blank"
href={`https://amboss.space/node/${nodeId}`}
rel="noreferrer"
>
<Link target='_blank' href={`https://amboss.space/node/${nodeId}`} rel='noreferrer'>
{`${nodeId.slice(0, 12)}... (AMBOSS)`}
</Link>
</ListItemText>
@ -115,11 +113,7 @@ const StatsDialog = ({
<WebIcon />
</ListItemIcon>
<ListItemText secondary={alternativeName}>
<Link
target="_blank"
href={`http://${alternativeSite}`}
rel="noreferrer"
>
<Link target='_blank' href={`http://${alternativeSite}`} rel='noreferrer'>
{`${alternativeSite.slice(0, 12)}...onion`}
</Link>
</ListItemText>
@ -131,11 +125,11 @@ const StatsDialog = ({
<ListItemIcon>
<GitHubIcon />
</ListItemIcon>
<ListItemText secondary={t("Currently running commit hash")}>
<ListItemText secondary={t('Currently running commit hash')}>
<Link
target="_blank"
target='_blank'
href={`https://github.com/Reckless-Satoshi/robosats/tree/${robosatsRunningCommitHash}`}
rel="noreferrer"
rel='noreferrer'
>
{`${robosatsRunningCommitHash.slice(0, 12)}...`}
</Link>
@ -148,10 +142,17 @@ const StatsDialog = ({
<ListItemIcon>
<EqualizerIcon />
</ListItemIcon>
<ListItemText secondary={t("24h contracted volume")}>
<div style={{ cursor: "pointer", display: "flex", alignItems: "center", flexWrap: "wrap" }}>
<ListItemText secondary={t('24h contracted volume')}>
<div
style={{
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
flexWrap: 'wrap',
}}
>
{pn(lastDayVolume)}
<BitcoinSignIcon sx={{ width: 14,height: 14 }} color={"text.secondary"} />
<BitcoinSignIcon sx={{ width: 14, height: 14 }} color={'text.secondary'} />
</div>
</ListItemText>
</ListItem>
@ -162,10 +163,17 @@ const StatsDialog = ({
<ListItemIcon>
<EqualizerIcon />
</ListItemIcon>
<ListItemText secondary={t("Lifetime contracted volume")}>
<div style={{ cursor: "pointer", display: "flex",alignItems: "center", flexWrap: "wrap" }}>
<ListItemText secondary={t('Lifetime contracted volume')}>
<div
style={{
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
flexWrap: 'wrap',
}}
>
{pn(lifetimeVolume)}
<BitcoinSignIcon sx={{ width: 14, height: 14 }} color={"text.secondary"} />
<BitcoinSignIcon sx={{ width: 14, height: 14 }} color={'text.secondary'} />
</div>
</ListItemText>
</ListItem>
@ -177,14 +185,21 @@ const StatsDialog = ({
</ListItemIcon>
<ListItemText
primary={
<div style={{ display: "flex", alignItems: "center", justifyContent: "left", flexWrap: "wrap" }}>
<span>{`${t("Made with")} `}</span>
<FavoriteIcon sx={{ color: "#ff0000", height: "22px", width: "22px" }} />
<span>{` ${t("and")} `}</span>
<BoltIcon sx={{ color: "#fcba03", height: "23px",width: "23px" }} />
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'left',
flexWrap: 'wrap',
}}
>
<span>{`${t('Made with')} `}</span>
<FavoriteIcon sx={{ color: '#ff0000', height: '22px', width: '22px' }} />
<span>{` ${t('and')} `}</span>
<BoltIcon sx={{ color: '#fcba03', height: '23px', width: '23px' }} />
</div>
}
secondary={t("... somewhere on Earth!")}
secondary={t('... somewhere on Earth!')}
/>
</ListItem>
</List>

View File

@ -1,19 +1,19 @@
import React from "react";
import { useTranslation } from "react-i18next";
import React from 'react';
import { useTranslation } from 'react-i18next';
import {
Dialog,
DialogTitle,
Tooltip,
Tooltip,
IconButton,
TextField,
DialogActions,
DialogActions,
DialogContent,
DialogContentText,
Button,
Button,
Grid,
} from "@mui/material"
import { getCookie } from "../../utils/cookies";
import ContentCopy from "@mui/icons-material/ContentCopy";
} from '@mui/material';
import { getCookie } from '../../utils/cookies';
import ContentCopy from '@mui/icons-material/ContentCopy';
type Props = {
open: boolean;
@ -22,10 +22,10 @@ type Props = {
onClickCopy: () => void;
onClickBack: () => void;
onClickDone: () => void;
}
};
const StoreTokenDialog = ({
open,
open,
onClose,
copyIconColor,
onClickCopy,
@ -35,46 +35,45 @@ const StoreTokenDialog = ({
const { t } = useTranslation();
return (
<Dialog
open={open}
onClose={onClose}
>
<DialogTitle >
{t("Store your robot token")}
</DialogTitle>
<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.")}
{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">
<br />
<Grid align='center'>
<TextField
sx={{width:"100%", maxWidth:"550px"}}
sx={{ width: '100%', maxWidth: '550px' }}
disabled
label={t("Back it up!")}
value={getCookie("robot_token")}
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>,
}}
/>
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>
<Button onClick={onClickBack} autoFocus>
{t('Go back')}
</Button>
<Button onClick={onClickDone}>{t('Done')}</Button>
</DialogActions>
</Dialog>
)
}
);
};
export default StoreTokenDialog;

View File

@ -1,10 +1,9 @@
export { default as AuditPGPDialog } from "./AuditPGP";
export { default as CommunityDialog } from "./Community";
export { default as InfoDialog } from "./Info";
export { default as LearnDialog } from "./Learn";
export { default as NoRobotDialog } from "./NoRobot";
export { default as StoreTokenDialog } from "./StoreToken";
export { default as ExchangeSummaryDialog } from "./ExchangeSummary";
export { default as ProfileDialog } from "./Profile";
export { default as StatsDialog } from "./Stats";
export { default as AuditPGPDialog } from './AuditPGP';
export { default as CommunityDialog } from './Community';
export { default as InfoDialog } from './Info';
export { default as LearnDialog } from './Learn';
export { default as NoRobotDialog } from './NoRobot';
export { default as StoreTokenDialog } from './StoreToken';
export { default as ExchangeSummaryDialog } from './ExchangeSummary';
export { default as ProfileDialog } from './Profile';
export { default as StatsDialog } from './Stats';

View File

@ -1,17 +1,30 @@
import React, { Component } from 'react';
import { withTranslation } from "react-i18next";
import {Button, IconButton, Badge, Tooltip, TextField, Grid, Container, Card, CardHeader, Paper, Avatar, Typography} from "@mui/material";
import { withTranslation } from 'react-i18next';
import {
Button,
IconButton,
Badge,
Tooltip,
TextField,
Grid,
Container,
Card,
CardHeader,
Paper,
Avatar,
Typography,
} from '@mui/material';
import ReconnectingWebSocket from 'reconnecting-websocket';
import { encryptMessage , decryptMessage} from "../utils/pgp";
import { getCookie } from "../utils/cookies";
import { saveAsJson } from "../utils/saveFile";
import { copyToClipboard } from "../utils/clipboard";
import { AuditPGPDialog } from "./Dialogs"
import { encryptMessage, decryptMessage } from '../utils/pgp';
import { getCookie } from '../utils/cookies';
import { saveAsJson } from '../utils/saveFile';
import { copyToClipboard } from '../utils/clipboard';
import { AuditPGPDialog } from './Dialogs';
// Icons
import CheckIcon from '@mui/icons-material/Check';
import CloseIcon from '@mui/icons-material/Close';
import ContentCopy from "@mui/icons-material/ContentCopy";
import ContentCopy from '@mui/icons-material/ContentCopy';
import VisibilityIcon from '@mui/icons-material/Visibility';
import CircularProgress from '@mui/material/CircularProgress';
import KeyIcon from '@mui/icons-material/Key';
@ -23,120 +36,157 @@ class Chat extends Component {
}
state = {
own_pub_key: getCookie('pub_key').split('\\').join('\n'),
own_pub_key: getCookie('pub_key').split('\\').join('\n'),
own_enc_priv_key: getCookie('enc_priv_key').split('\\').join('\n'),
peer_pub_key: null,
token: getCookie('robot_token'),
messages: [],
value:'',
value: '',
connected: false,
peer_connected: false,
audit: false,
showPGP: new Array,
showPGP: new Array(),
waitingEcho: false,
lastSent: '---BLANK---',
latestIndex: 0,
scrollNow:false,
scrollNow: false,
};
rws = new ReconnectingWebSocket('ws://' + window.location.host + '/ws/chat/' + this.props.orderId + '/', [], {connectionTimeout: 15000});
rws = new ReconnectingWebSocket(
'ws://' + window.location.host + '/ws/chat/' + this.props.orderId + '/',
[],
{ connectionTimeout: 15000 },
);
componentDidMount() {
this.rws.addEventListener('open', () => {
console.log('Connected!');
this.setState({connected: true});
this.rws.send(JSON.stringify({
type: "message",
message: this.state.own_pub_key,
nick: this.props.ur_nick,
}));
this.setState({ connected: true });
this.rws.send(
JSON.stringify({
type: 'message',
message: this.state.own_pub_key,
nick: this.props.ur_nick,
}),
);
});
this.rws.addEventListener('message', (message) => {
const dataFromServer = JSON.parse(message.data);
console.log('Got reply!', dataFromServer.type);
console.log('PGP message index', dataFromServer.index, ' latestIndex ',this.state.latestIndex);
if (dataFromServer){
console.log(dataFromServer)
this.setState({peer_connected: dataFromServer.peer_connected})
console.log(
'PGP message index',
dataFromServer.index,
' latestIndex ',
this.state.latestIndex,
);
if (dataFromServer) {
console.log(dataFromServer);
this.setState({ peer_connected: dataFromServer.peer_connected });
// If we receive our own key on a message
if (dataFromServer.message == this.state.own_pub_key){console.log("OWN PUB KEY RECEIVED!!")}
if (dataFromServer.message == this.state.own_pub_key) {
console.log('OWN PUB KEY RECEIVED!!');
}
// If we receive a public key other than ours (our peer key!)
if (dataFromServer.message.substring(0,36) == `-----BEGIN PGP PUBLIC KEY BLOCK-----` && dataFromServer.message != this.state.own_pub_key) {
if (dataFromServer.message == this.state.peer_pub_key){
console.log("PEER HAS RECONNECTED USING HIS PREVIOUSLY KNOWN PUBKEY")
} else if (dataFromServer.message != this.state.peer_pub_key & this.state.peer_pub_key != null){
console.log("PEER PUBKEY HAS CHANGED")
if (
dataFromServer.message.substring(0, 36) == `-----BEGIN PGP PUBLIC KEY BLOCK-----` &&
dataFromServer.message != this.state.own_pub_key
) {
if (dataFromServer.message == this.state.peer_pub_key) {
console.log('PEER HAS RECONNECTED USING HIS PREVIOUSLY KNOWN PUBKEY');
} else if (
(dataFromServer.message != this.state.peer_pub_key) &
(this.state.peer_pub_key != null)
) {
console.log('PEER PUBKEY HAS CHANGED');
}
console.log("PEER PUBKEY RECEIVED!!")
this.setState({peer_pub_key:dataFromServer.message})
console.log('PEER PUBKEY RECEIVED!!');
this.setState({ peer_pub_key: dataFromServer.message });
// After receiving the peer pubkey we ask the server for the historic messages if any
this.rws.send(JSON.stringify({
type: "message",
this.rws.send(
JSON.stringify({
type: 'message',
message: `-----SERVE HISTORY-----`,
nick: this.props.ur_nick,
}))
} else
}),
);
}
// If we receive an encrypted message
if (dataFromServer.message.substring(0,27) == `-----BEGIN PGP MESSAGE-----` && dataFromServer.index > this.state.latestIndex){
else if (
dataFromServer.message.substring(0, 27) == `-----BEGIN PGP MESSAGE-----` &&
dataFromServer.index > this.state.latestIndex
) {
decryptMessage(
dataFromServer.message.split('\\').join('\n'),
dataFromServer.user_nick == this.props.ur_nick ? this.state.own_pub_key : this.state.peer_pub_key,
this.state.own_enc_priv_key,
this.state.token)
.then((decryptedData) =>
this.setState((state) =>
({
dataFromServer.message.split('\\').join('\n'),
dataFromServer.user_nick == this.props.ur_nick
? this.state.own_pub_key
: this.state.peer_pub_key,
this.state.own_enc_priv_key,
this.state.token,
).then((decryptedData) =>
this.setState((state) => ({
scrollNow: true,
waitingEcho: this.state.waitingEcho == true ? (decryptedData.decryptedMessage == this.state.lastSent ? false: true ) : false,
lastSent: decryptedData.decryptedMessage == this.state.lastSent ? '----BLANK----': this.state.lastSent,
latestIndex: dataFromServer.index > this.state.latestIndex ? dataFromServer.index : this.state.latestIndex,
messages: [...state.messages,
{
index: dataFromServer.index,
encryptedMessage: dataFromServer.message.split('\\').join('\n'),
plainTextMessage: decryptedData.decryptedMessage,
validSignature: decryptedData.validSignature,
userNick: dataFromServer.user_nick,
time: dataFromServer.time
}].sort(function(a,b) {
waitingEcho:
this.state.waitingEcho == true
? decryptedData.decryptedMessage == this.state.lastSent
? false
: true
: false,
lastSent:
decryptedData.decryptedMessage == this.state.lastSent
? '----BLANK----'
: this.state.lastSent,
latestIndex:
dataFromServer.index > this.state.latestIndex
? dataFromServer.index
: this.state.latestIndex,
messages: [
...state.messages,
{
index: dataFromServer.index,
encryptedMessage: dataFromServer.message.split('\\').join('\n'),
plainTextMessage: decryptedData.decryptedMessage,
validSignature: decryptedData.validSignature,
userNick: dataFromServer.user_nick,
time: dataFromServer.time,
},
].sort(function (a, b) {
// order the message array by their index (increasing)
return a.index - b.index
return a.index - b.index;
}),
})
));
} else
})),
);
}
// We allow plaintext communication. The user must write # to start
// If we receive an plaintext message
if (dataFromServer.message.substring(0,1) == "#"){
console.log("Got plaintext message", dataFromServer.message)
this.setState((state) =>
({
scrollNow: true,
messages: [...state.messages,
{
else if (dataFromServer.message.substring(0, 1) == '#') {
console.log('Got plaintext message', dataFromServer.message);
this.setState((state) => ({
scrollNow: true,
messages: [
...state.messages,
{
index: this.state.latestIndex + 0.001,
encryptedMessage: dataFromServer.message,
plainTextMessage: dataFromServer.message,
validSignature: false,
validSignature: false,
userNick: dataFromServer.user_nick,
time: (new Date).toString(),
}]}));
}
time: new Date().toString(),
},
],
}));
}
}
});
this.rws.addEventListener('close', () => {
console.log('Socket is closed. Reconnect will be attempted');
this.setState({connected: false});
this.setState({ connected: false });
});
this.rws.addEventListener('error', () => {
@ -145,216 +195,401 @@ class Chat extends Component {
}
componentDidUpdate() {
// Only fire the scroll and audio when the reason for Update is a new message
if (this.state.scrollNow){
const audio = new Audio(`/static/assets/sounds/chat-open.mp3`)
if (this.state.scrollNow) {
const audio = new Audio(`/static/assets/sounds/chat-open.mp3`);
audio.play();
this.scrollToBottom();
this.setState({scrollNow:false});
this.setState({ scrollNow: false });
}
}
scrollToBottom = () => {
this.messagesEnd.scrollIntoView({ behavior: "smooth" });
}
this.messagesEnd.scrollIntoView({ behavior: 'smooth' });
};
onButtonClicked = (e) => {
// If input string contains token. Do not set message
if(this.state.value.indexOf(this.state.token) !== -1){
alert(`Aye! You just sent your own robot token to your peer in chat, that's a catastrophic idea! So bad your message was blocked.`)
this.setState({value: ""});
if (this.state.value.indexOf(this.state.token) !== -1) {
alert(
`Aye! You just sent your own robot token to your peer in chat, that's a catastrophic idea! So bad your message was blocked.`,
);
this.setState({ value: '' });
}
// If input string contains '#' send unencrypted and unlogged message
else if(this.state.value.substring(0,1)=='#'){
this.rws.send(JSON.stringify({
type: "message",
else if (this.state.value.substring(0, 1) == '#') {
this.rws.send(
JSON.stringify({
type: 'message',
message: this.state.value,
nick: this.props.ur_nick,
}));
this.setState({value: ""});
}),
);
this.setState({ value: '' });
}
// Else if message is not empty send message
else if(this.state.value!=''){
this.setState({value: "", waitingEcho: true, lastSent:this.state.value})
encryptMessage(this.state.value, this.state.own_pub_key, this.state.peer_pub_key, this.state.own_enc_priv_key, this.state.token)
.then((encryptedMessage) =>
console.log("Sending Encrypted MESSAGE", encryptedMessage) &
this.rws.send(JSON.stringify({
type: "message",
message: encryptedMessage.split('\n').join('\\'),
nick: this.props.ur_nick,
})
)
// Else if message is not empty send message
else if (this.state.value != '') {
this.setState({ value: '', waitingEcho: true, lastSent: this.state.value });
encryptMessage(
this.state.value,
this.state.own_pub_key,
this.state.peer_pub_key,
this.state.own_enc_priv_key,
this.state.token,
).then(
(encryptedMessage) =>
console.log('Sending Encrypted MESSAGE', encryptedMessage) &
this.rws.send(
JSON.stringify({
type: 'message',
message: encryptedMessage.split('\n').join('\\'),
nick: this.props.ur_nick,
}),
),
);
}
e.preventDefault();
}
};
createJsonFile = () => {
return ({
"credentials": {
"own_public_key": this.state.own_pub_key,
"peer_public_key":this.state.peer_pub_key,
"encrypted_private_key":this.state.own_enc_priv_key,
"passphrase":this.state.token},
"messages": this.state.messages,
})
}
return {
credentials: {
own_public_key: this.state.own_pub_key,
peer_public_key: this.state.peer_pub_key,
encrypted_private_key: this.state.own_enc_priv_key,
passphrase: this.state.token,
},
messages: this.state.messages,
};
};
messageCard = (props) => {
const { t } = this.props;
return(
<Card elevation={5} align="left" >
<CardHeader sx={{color: '#333333'}}
return (
<Card elevation={5} align='left'>
<CardHeader
sx={{ color: '#333333' }}
avatar={
<Badge variant="dot" overlap="circular" badgeContent="" color={props.userConnected ? "success" : "error"}>
<Avatar className="flippedSmallAvatar"
<Badge
variant='dot'
overlap='circular'
badgeContent=''
color={props.userConnected ? 'success' : 'error'}
>
<Avatar
className='flippedSmallAvatar'
alt={props.message.userNick}
src={window.location.origin +'/static/assets/avatars/' + props.message.userNick + '.png'}
/>
src={
window.location.origin +
'/static/assets/avatars/' +
props.message.userNick +
'.png'
}
/>
</Badge>
}
style={{backgroundColor: props.cardColor}}
style={{ backgroundColor: props.cardColor }}
title={
<Tooltip placement="top" enterTouchDelay={0} enterDelay={500} enterNextDelay={2000} title={t(props.message.validSignature ? "Verified signature by {{nickname}}": "Cannot verify signature of {{nickname}}",{"nickname": props.message.userNick})}>
<div style={{display:'flex',alignItems:'center', flexWrap:'wrap', position:'relative',left:-5, width:240}}>
<div style={{width:168,display:'flex',alignItems:'center', flexWrap:'wrap'}}>
<Tooltip
placement='top'
enterTouchDelay={0}
enterDelay={500}
enterNextDelay={2000}
title={t(
props.message.validSignature
? 'Verified signature by {{nickname}}'
: 'Cannot verify signature of {{nickname}}',
{ nickname: props.message.userNick },
)}
>
<div
style={{
display: 'flex',
alignItems: 'center',
flexWrap: 'wrap',
position: 'relative',
left: -5,
width: 240,
}}
>
<div
style={{ width: 168, display: 'flex', alignItems: 'center', flexWrap: 'wrap' }}
>
{props.message.userNick}
{props.message.validSignature ?
<CheckIcon sx={{height:16}} color="success"/>
:
<CloseIcon sx={{height:16}} color="error"/>
}
{props.message.validSignature ? (
<CheckIcon sx={{ height: 16 }} color='success' />
) : (
<CloseIcon sx={{ height: 16 }} color='error' />
)}
</div>
<div style={{width:20}}>
<IconButton sx={{height:18,width:18}}
onClick={()=>
this.setState(prevState => {
const newShowPGP = [...prevState.showPGP];
newShowPGP[props.index] = !newShowPGP[props.index];
return {showPGP: newShowPGP};
})}>
<VisibilityIcon color={this.state.showPGP[props.index]? "primary":"inherit"} sx={{height:16,width:16,color:this.state.showPGP[props.index]? "primary":"#333333"}}/>
<div style={{ width: 20 }}>
<IconButton
sx={{ height: 18, width: 18 }}
onClick={() =>
this.setState((prevState) => {
const newShowPGP = [...prevState.showPGP];
newShowPGP[props.index] = !newShowPGP[props.index];
return { showPGP: newShowPGP };
})
}
>
<VisibilityIcon
color={this.state.showPGP[props.index] ? 'primary' : 'inherit'}
sx={{
height: 16,
width: 16,
color: this.state.showPGP[props.index] ? 'primary' : '#333333',
}}
/>
</IconButton>
</div>
<div style={{width:20}}>
<Tooltip disableHoverListener enterTouchDelay={0} title={t("Copied!")}>
<IconButton sx={{height:18,width:18}}
onClick={()=> copyToClipboard(this.state.showPGP[props.index] ? props.message.encryptedMessage : props.message.plainTextMessage)}>
<ContentCopy sx={{height:16,width:16,color:'#333333'}}/>
<div style={{ width: 20 }}>
<Tooltip disableHoverListener enterTouchDelay={0} title={t('Copied!')}>
<IconButton
sx={{ height: 18, width: 18 }}
onClick={() =>
copyToClipboard(
this.state.showPGP[props.index]
? props.message.encryptedMessage
: props.message.plainTextMessage,
)
}
>
<ContentCopy sx={{ height: 16, width: 16, color: '#333333' }} />
</IconButton>
</Tooltip>
</div>
</div>
</Tooltip>
}
subheader={this.state.showPGP[props.index] ? <a> {props.message.time} <br/> {"Valid signature: " + props.message.validSignature} <br/> {props.message.encryptedMessage} </a> : props.message.plainTextMessage}
subheaderTypographyProps={{sx: {wordWrap: "break-word", width: '200px', color: '#444444', fontSize: this.state.showPGP[props.index]? 11 : null }}}
subheader={
this.state.showPGP[props.index] ? (
<a>
{' '}
{props.message.time} <br /> {'Valid signature: ' + props.message.validSignature}{' '}
<br /> {props.message.encryptedMessage}{' '}
</a>
) : (
props.message.plainTextMessage
)
}
subheaderTypographyProps={{
sx: {
wordWrap: 'break-word',
width: '200px',
color: '#444444',
fontSize: this.state.showPGP[props.index] ? 11 : null,
},
}}
/>
</Card>
)
}
);
};
render() {
const { t } = this.props;
return (
<Container component="main">
<Container component='main'>
<Grid container spacing={0.5}>
<Grid item xs={0.3}/>
<Grid item xs={0.3} />
<Grid item xs={5.5}>
<Paper elevation={1} style={this.state.connected ? {backgroundColor: '#e8ffe6'}: {backgroundColor: '#FFF1C5'}}>
<Typography variant='caption' sx={{color: '#333333'}}>
{t("You")+": "}{this.state.connected ? t("connected"): t("disconnected")}
</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: '#333333'}}>
{t("Peer")+": "}{this.state.peer_connected ? t("connected"): t("disconnected")}
</Typography>
</Paper>
</Grid>
<Grid item xs={0.3}/>
</Grid>
<div style={{position:'relative', left:'-2px', margin:'0 auto', width: '285px'}}>
<Paper elevation={1} style={{height: '300px', maxHeight: '300px' , width: '285px' ,overflow: 'auto', backgroundColor: '#F7F7F7' }}>
{this.state.messages.map((message, index) =>
<li style={{listStyleType:"none"}} key={index}>
{message.userNick == this.props.ur_nick ?
<this.messageCard message={message} index={index} cardColor={'#eeeeee'} userConnected={this.state.connected}/>
:
<this.messageCard message={message} index={index} cardColor={'#fafafa'} userConnected={this.state.peer_connected}/>
<Paper
elevation={1}
style={
this.state.connected
? { backgroundColor: '#e8ffe6' }
: { backgroundColor: '#FFF1C5' }
}
</li>)}
<div style={{ float:"left", clear: "both" }} ref={(el) => { this.messagesEnd = el; }}></div>
>
<Typography variant='caption' sx={{ color: '#333333' }}>
{t('You') + ': '}
{this.state.connected ? t('connected') : t('disconnected')}
</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: '#333333' }}>
{t('Peer') + ': '}
{this.state.peer_connected ? t('connected') : t('disconnected')}
</Typography>
</Paper>
</Grid>
<Grid item xs={0.3} />
</Grid>
<div style={{ position: 'relative', left: '-2px', margin: '0 auto', width: '285px' }}>
<Paper
elevation={1}
style={{
height: '300px',
maxHeight: '300px',
width: '285px',
overflow: 'auto',
backgroundColor: '#F7F7F7',
}}
>
{this.state.messages.map((message, index) => (
<li style={{ listStyleType: 'none' }} key={index}>
{message.userNick == this.props.ur_nick ? (
<this.messageCard
message={message}
index={index}
cardColor={'#eeeeee'}
userConnected={this.state.connected}
/>
) : (
<this.messageCard
message={message}
index={index}
cardColor={'#fafafa'}
userConnected={this.state.peer_connected}
/>
)}
</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"}}>
<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 ? (this.state.peer_pub_key ? null : t("Waiting for peer public key...")) : t("Connecting...")}
label={t('Type a message')}
variant='standard'
size='small'
helperText={
this.state.connected
? this.state.peer_pub_key
? null
: t('Waiting for peer public key...')
: t('Connecting...')
}
value={this.state.value}
onChange={e => {
onChange={(e) => {
this.setState({ value: e.target.value });
this.value = this.state.value;
}}
sx={{width: 219}}
sx={{ width: 219 }}
/>
</Grid>
<Grid item alignItems="stretch" style={{ display: "flex" }}>
<Button sx={{'width':68}} disabled={!this.state.connected || this.state.waitingEcho || this.state.peer_pub_key == null} type="submit" variant="contained" color="primary">
{this.state.waitingEcho ?
<div style={{display:'flex',alignItems:'center', flexWrap:'wrap', minWidth:68, width:68, position:"relative",left:15}}>
<div style={{width:20}}><KeyIcon sx={{width:18}}/></div>
<div style={{width:18}}><CircularProgress size={16} thickness={5}/></div>
<Grid item alignItems='stretch' style={{ display: 'flex' }}>
<Button
sx={{ width: 68 }}
disabled={
!this.state.connected ||
this.state.waitingEcho ||
this.state.peer_pub_key == null
}
type='submit'
variant='contained'
color='primary'
>
{this.state.waitingEcho ? (
<div
style={{
display: 'flex',
alignItems: 'center',
flexWrap: 'wrap',
minWidth: 68,
width: 68,
position: 'relative',
left: 15,
}}
>
<div style={{ width: 20 }}>
<KeyIcon sx={{ width: 18 }} />
</div>
<div style={{ width: 18 }}>
<CircularProgress size={16} thickness={5} />
</div>
</div>
:
t("Send")
}
) : (
t('Send')
)}
</Button>
</Grid>
</Grid>
</form>
</div>
<div style={{height:4}}/>
<div style={{ height: 4 }} />
<Grid container spacing={0}>
<AuditPGPDialog
open={this.state.audit}
onClose={() => this.setState({audit:false})}
onClose={() => this.setState({ audit: false })}
orderId={Number(this.props.orderId)}
messages={this.state.messages}
own_pub_key={this.state.own_pub_key}
own_enc_priv_key={this.state.own_enc_priv_key}
peer_pub_key={this.state.peer_pub_key ? this.state.peer_pub_key : "Not received yet"}
peer_pub_key={this.state.peer_pub_key ? this.state.peer_pub_key : 'Not received yet'}
passphrase={this.state.token}
onClickBack={() => this.setState({audit:false})}
onClickBack={() => this.setState({ audit: false })}
/>
<Grid item xs={6}>
<Tooltip placement="bottom" enterTouchDelay={0} enterDelay={500} enterNextDelay={2000} title={t("Verify your privacy")}>
<Button size="small" color="primary" variant="outlined" onClick={()=>this.setState({audit:!this.state.audit})}><KeyIcon/>{t("Audit PGP")} </Button>
<Tooltip
placement='bottom'
enterTouchDelay={0}
enterDelay={500}
enterNextDelay={2000}
title={t('Verify your privacy')}
>
<Button
size='small'
color='primary'
variant='outlined'
onClick={() => this.setState({ audit: !this.state.audit })}
>
<KeyIcon />
{t('Audit PGP')}{' '}
</Button>
</Tooltip>
</Grid>
<Grid item xs={6}>
<Tooltip placement="bottom" enterTouchDelay={0} enterDelay={500} enterNextDelay={2000} title={t("Save full log as a JSON file (messages and credentials)")}>
<Button size="small" color="primary" variant="outlined" onClick={()=>saveAsJson('complete_log_chat_'+this.props.orderId+'.json', this.createJsonFile())}><div style={{width:28,height:20}}><ExportIcon sx={{width:20,height:20}}/></div> {t("Export")} </Button>
<Tooltip
placement='bottom'
enterTouchDelay={0}
enterDelay={500}
enterNextDelay={2000}
title={t('Save full log as a JSON file (messages and credentials)')}
>
<Button
size='small'
color='primary'
variant='outlined'
onClick={() =>
saveAsJson(
'complete_log_chat_' + this.props.orderId + '.json',
this.createJsonFile(),
)
}
>
<div style={{ width: 28, height: 20 }}>
<ExportIcon sx={{ width: 20, height: 20 }} />
</div>{' '}
{t('Export')}{' '}
</Button>
</Tooltip>
</Grid>
</Grid>
</Container>
)
);
}
}

View File

@ -1,11 +1,11 @@
import React from "react";
import Flags from 'country-flag-icons/react/3x2'
import React from 'react';
import Flags from 'country-flag-icons/react/3x2';
import SwapCallsIcon from '@mui/icons-material/SwapCalls';
import { GoldIcon, EarthIcon } from "../Icons";
import { GoldIcon, EarthIcon } from '../Icons';
type Props = {
code: string;
}
};
const FlagWithProps = ({ code }: Props): JSX.Element => {
const defaultProps = {
@ -15,82 +15,80 @@ const FlagWithProps = ({ code }: Props): JSX.Element => {
let flag: JSX.Element | null = null;
if(code === 'AED') flag = <Flags.AE {...defaultProps}/>;
if(code === 'AUD') flag = <Flags.AU {...defaultProps}/>;
if(code === 'ARS') flag = <Flags.AR {...defaultProps}/>;
if(code === 'BRL') flag = <Flags.BR {...defaultProps}/>;
if(code === 'BYN') flag = <Flags.BY {...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 === 'EGP') flag = <Flags.EG {...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 === 'LKR') flag = <Flags.LK {...defaultProps}/>;
if(code === 'MAD') flag = <Flags.MA {...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 === 'TND') flag = <Flags.TN {...defaultProps}/>;
if(code === 'ANY') flag = <EarthIcon {...defaultProps}/>;
if(code === 'XAU') flag = <GoldIcon {...defaultProps}/>;
if(code === 'BTC') flag = <SwapCallsIcon color="primary"/>;
if (code === 'AED') flag = <Flags.AE {...defaultProps} />;
if (code === 'AUD') flag = <Flags.AU {...defaultProps} />;
if (code === 'ARS') flag = <Flags.AR {...defaultProps} />;
if (code === 'BRL') flag = <Flags.BR {...defaultProps} />;
if (code === 'BYN') flag = <Flags.BY {...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 === 'EGP') flag = <Flags.EG {...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 === 'LKR') flag = <Flags.LK {...defaultProps} />;
if (code === 'MAD') flag = <Flags.MA {...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 === 'TND') flag = <Flags.TN {...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>
);
return <div style={{ width: 28, height: 20 }}>{flag}</div>;
};
export default FlagWithProps;

View File

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

View File

@ -1,62 +1,128 @@
import React, { Component } from "react";
import { BrowserRouter as Router, Switch, Route, Link, Redirect,useHistory } from "react-router-dom";
import React, { Component } from 'react';
import {
BrowserRouter as Router,
Switch,
Route,
Link,
Redirect,
useHistory,
} from 'react-router-dom';
import UserGenPage from "./UserGenPage";
import MakerPage from "./MakerPage";
import BookPage from "./BookPage";
import OrderPage from "./OrderPage";
import BottomBar from "./BottomBar";
import UserGenPage from './UserGenPage';
import MakerPage from './MakerPage';
import BookPage from './BookPage';
import OrderPage from './OrderPage';
import BottomBar from './BottomBar';
export default class HomePage extends Component {
constructor(props) {
super(props);
this.state = {
nickname: null,
token: null,
copiedToken: false,
avatarLoaded: false,
buyChecked: false,
sellChecked: false,
type:2,
currency:0,
bookCurrencyCode:'ANY',
bookOrders:new Array(),
bookLoading: true,
activeOrderId: null,
lastOrderId: null,
earnedRewards: 0,
referralCode:'',
lastDayPremium: 0,
limits: {}
}
}
constructor(props) {
super(props);
this.state = {
nickname: null,
token: null,
copiedToken: false,
avatarLoaded: false,
buyChecked: false,
sellChecked: false,
type: 2,
currency: 0,
bookCurrencyCode: 'ANY',
bookOrders: new Array(),
bookLoading: true,
activeOrderId: null,
lastOrderId: null,
earnedRewards: 0,
referralCode: '',
lastDayPremium: 0,
limits: {},
};
}
setAppState=(newState)=>{
this.setState(newState)
}
setAppState = (newState) => {
this.setState(newState);
};
redirectTo(location) {
redirectTo(location) {
this.props.history.push(location);
}
}
render() {
const fontSize = this.props.theme.typography.fontSize;
const fontSizeFactor = fontSize / 14; // default fontSize is 14
return (
<Router >
<div className='appCenter'>
<Switch>
<Route exact path='/' render={(props) => <UserGenPage {...props} {...this.state} {...this.props} setAppState={this.setAppState}/>}/>
<Route path='/ref/:refCode' render={(props) => <UserGenPage {...props} {...this.state} {...this.props} setAppState={this.setAppState}/>}/>
<Route path='/make' render={(props) => <MakerPage {...props} {...this.state} {...this.props} setAppState={this.setAppState}/>}/>
<Route path='/book' render={(props) => <BookPage {...props} {...this.state} {...this.props} setAppState={this.setAppState} />}/>
<Route path="/order/:orderId" render={(props) => <OrderPage {...props} {...this.state} {...this.props} setAppState={this.setAppState}/>}/>
</Switch>
</div>
<div className='bottomBar' style={{height: `${40*fontSizeFactor}px`, width: window.innerWidth}}>
<BottomBar redirectTo={this.redirectTo} {...this.state} {...this.props} setAppState={this.setAppState} />
</div>
</Router>
);
}
render() {
const fontSize = this.props.theme.typography.fontSize;
const fontSizeFactor = fontSize / 14; // default fontSize is 14
return (
<Router>
<div className='appCenter'>
<Switch>
<Route
exact
path='/'
render={(props) => (
<UserGenPage
{...props}
{...this.state}
{...this.props}
setAppState={this.setAppState}
/>
)}
/>
<Route
path='/ref/:refCode'
render={(props) => (
<UserGenPage
{...props}
{...this.state}
{...this.props}
setAppState={this.setAppState}
/>
)}
/>
<Route
path='/make'
render={(props) => (
<MakerPage
{...props}
{...this.state}
{...this.props}
setAppState={this.setAppState}
/>
)}
/>
<Route
path='/book'
render={(props) => (
<BookPage
{...props}
{...this.state}
{...this.props}
setAppState={this.setAppState}
/>
)}
/>
<Route
path='/order/:orderId'
render={(props) => (
<OrderPage
{...props}
{...this.state}
{...this.props}
setAppState={this.setAppState}
/>
)}
/>
</Switch>
</div>
<div
className='bottomBar'
style={{ height: `${40 * fontSizeFactor}px`, width: window.innerWidth }}
>
<BottomBar
redirectTo={this.redirectTo}
{...this.state}
{...this.props}
setAppState={this.setAppState}
/>
</div>
</Router>
);
}
}

View File

@ -1,18 +1,28 @@
import React, { Component } from "react";
import { SvgIcon } from "@mui/material"
import React, { Component } from 'react';
import { SvgIcon } from '@mui/material';
export default function AmbossIcon(props) {
return (
<SvgIcon {...props} x="0px" y="0px" viewBox="0 0 95.7 84.9">
<g id="Layer_2_00000052094167160547307180000012226084410257483709_">
<g id="Layer_1-2">
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="0" y1="42.45" x2="95.7" y2="42.45">
<stop offset="0" style={{stopColor:'#925bc9'}}/>
<stop offset="1" style={{stopColor:'#ff59ac'}}/>
</linearGradient>
<path className={"amboss"} d="M55.3,84.9V61.3h-15v23.6H0V0h95.7v84.9H55.3z M55.3,28.1h-15v17.1h15V28.1z"/>
</g>
return (
<SvgIcon {...props} x='0px' y='0px' viewBox='0 0 95.7 84.9'>
<g id='Layer_2_00000052094167160547307180000012226084410257483709_'>
<g id='Layer_1-2'>
<linearGradient
id='SVGID_1_'
gradientUnits='userSpaceOnUse'
x1='0'
y1='42.45'
x2='95.7'
y2='42.45'
>
<stop offset='0' style={{ stopColor: '#925bc9' }} />
<stop offset='1' style={{ stopColor: '#ff59ac' }} />
</linearGradient>
<path
className={'amboss'}
d='M55.3,84.9V61.3h-15v23.6H0V0h95.7v84.9H55.3z M55.3,28.1h-15v17.1h15V28.1z'
/>
</g>
</SvgIcon>
);
}
</g>
</SvgIcon>
);
}

View File

@ -1,12 +1,12 @@
import React, { Component } from "react";
import { SvgIcon } from "@mui/material"
import React, { Component } from 'react';
import { SvgIcon } from '@mui/material';
export default function BasqueCountryFlag(props) {
return (
<SvgIcon {...props} x="0px" y="0px" viewBox="0 0 50 28">
<path d="M0,0 v28 h50 v-28 z" fill="#D52B1E"/>
<path d="M0,0 L50,28 M50,0 L0,28" stroke="#009B48" strokeWidth="4.3"/>
<path d="M25,0 v28 M0,14 h50" stroke="#fff" strokeWidth="4.3"/>
</SvgIcon>
);
}
return (
<SvgIcon {...props} x='0px' y='0px' viewBox='0 0 50 28'>
<path d='M0,0 v28 h50 v-28 z' fill='#D52B1E' />
<path d='M0,0 L50,28 M50,0 L0,28' stroke='#009B48' strokeWidth='4.3' />
<path d='M25,0 v28 M0,14 h50' stroke='#fff' strokeWidth='4.3' />
</SvgIcon>
);
}

View File

@ -1,10 +1,10 @@
import React, { Component } from "react";
import { SvgIcon } from "@mui/material"
import React, { Component } from 'react';
import { SvgIcon } from '@mui/material';
export default function BitcoinIcon(props) {
return (
<SvgIcon sx={props.sx} color={props.color} viewBox="0 0 512 512">
<path d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zm-141.651-35.33c4.937-32.999-20.191-50.739-54.55-62.573l11.146-44.702-27.213-6.781-10.851 43.524c-7.154-1.783-14.502-3.464-21.803-5.13l10.929-43.81-27.198-6.781-11.153 44.686c-5.922-1.349-11.735-2.682-17.377-4.084l.031-.14-37.53-9.37-7.239 29.062s20.191 4.627 19.765 4.913c11.022 2.751 13.014 10.044 12.68 15.825l-12.696 50.925c.76.194 1.744.473 2.829.907-.907-.225-1.876-.473-2.876-.713l-17.796 71.338c-1.349 3.348-4.767 8.37-12.471 6.464.271.395-19.78-4.937-19.78-4.937l-13.51 31.147 35.414 8.827c6.588 1.651 13.045 3.379 19.4 5.006l-11.262 45.213 27.182 6.781 11.153-44.733a1038.209 1038.209 0 0 0 21.687 5.627l-11.115 44.523 27.213 6.781 11.262-45.128c46.404 8.781 81.299 5.239 95.986-36.727 11.836-33.79-.589-53.281-25.004-65.991 17.78-4.098 31.174-15.792 34.747-39.949zm-62.177 87.179c-8.41 33.79-65.308 15.523-83.755 10.943l14.944-59.899c18.446 4.603 77.6 13.717 68.811 48.956zm8.417-87.667c-7.673 30.736-55.031 15.12-70.393 11.292l13.548-54.327c15.363 3.828 64.836 10.973 56.845 43.035z"/>
<SvgIcon sx={props.sx} color={props.color} viewBox='0 0 512 512'>
<path d='M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zm-141.651-35.33c4.937-32.999-20.191-50.739-54.55-62.573l11.146-44.702-27.213-6.781-10.851 43.524c-7.154-1.783-14.502-3.464-21.803-5.13l10.929-43.81-27.198-6.781-11.153 44.686c-5.922-1.349-11.735-2.682-17.377-4.084l.031-.14-37.53-9.37-7.239 29.062s20.191 4.627 19.765 4.913c11.022 2.751 13.014 10.044 12.68 15.825l-12.696 50.925c.76.194 1.744.473 2.829.907-.907-.225-1.876-.473-2.876-.713l-17.796 71.338c-1.349 3.348-4.767 8.37-12.471 6.464.271.395-19.78-4.937-19.78-4.937l-13.51 31.147 35.414 8.827c6.588 1.651 13.045 3.379 19.4 5.006l-11.262 45.213 27.182 6.781 11.153-44.733a1038.209 1038.209 0 0 0 21.687 5.627l-11.115 44.523 27.213 6.781 11.262-45.128c46.404 8.781 81.299 5.239 95.986-36.727 11.836-33.79-.589-53.281-25.004-65.991 17.78-4.098 31.174-15.792 34.747-39.949zm-62.177 87.179c-8.41 33.79-65.308 15.523-83.755 10.943l14.944-59.899c18.446 4.603 77.6 13.717 68.811 48.956zm8.417-87.667c-7.673 30.736-55.031 15.12-70.393 11.292l13.548-54.327c15.363 3.828 64.836 10.973 56.845 43.035z' />
</SvgIcon>
);
}
}

View File

@ -1,10 +1,10 @@
import React, { Component } from "react";
import { SvgIcon } from "@mui/material"
import React, { Component } from 'react';
import { SvgIcon } from '@mui/material';
export default function BitcoinSignIcon(props) {
return (
<SvgIcon sx={props.sx} color={props.color} viewBox="0 0 320 512">
<path d="M48 32C48 14.33 62.33 0 80 0C97.67 0 112 14.33 112 32V64H144V32C144 14.33 158.3 0 176 0C193.7 0 208 14.33 208 32V64C208 65.54 207.9 67.06 207.7 68.54C254.1 82.21 288 125.1 288 176C288 200.2 280.3 222.6 267.3 240.9C298.9 260.7 320 295.9 320 336C320 397.9 269.9 448 208 448V480C208 497.7 193.7 512 176 512C158.3 512 144 497.7 144 480V448H112V480C112 497.7 97.67 512 80 512C62.33 512 48 497.7 48 480V448H41.74C18.69 448 0 429.3 0 406.3V101.6C0 80.82 16.82 64 37.57 64H48V32zM176 224C202.5 224 224 202.5 224 176C224 149.5 202.5 128 176 128H64V224H176zM64 288V384H208C234.5 384 256 362.5 256 336C256 309.5 234.5 288 208 288H64z"/>
<SvgIcon sx={props.sx} color={props.color} viewBox='0 0 320 512'>
<path d='M48 32C48 14.33 62.33 0 80 0C97.67 0 112 14.33 112 32V64H144V32C144 14.33 158.3 0 176 0C193.7 0 208 14.33 208 32V64C208 65.54 207.9 67.06 207.7 68.54C254.1 82.21 288 125.1 288 176C288 200.2 280.3 222.6 267.3 240.9C298.9 260.7 320 295.9 320 336C320 397.9 269.9 448 208 448V480C208 497.7 193.7 512 176 512C158.3 512 144 497.7 144 480V448H112V480C112 497.7 97.67 512 80 512C62.33 512 48 497.7 48 480V448H41.74C18.69 448 0 429.3 0 406.3V101.6C0 80.82 16.82 64 37.57 64H48V32zM176 224C202.5 224 224 202.5 224 176C224 149.5 202.5 128 176 128H64V224H176zM64 288V384H208C234.5 384 256 362.5 256 336C256 309.5 234.5 288 208 288H64z' />
</SvgIcon>
);
}
}

View File

@ -1,49 +1,111 @@
import React, { Component } from "react";
import { SvgIcon } from "@mui/material"
import React, { Component } from 'react';
import { SvgIcon } from '@mui/material';
export default function BuySatsIcon(props) {
return (
<SvgIcon sx={props.sx} color={props.color} x="0px" y="0px" viewBox="0 0 300 300">
<g>
<polygon fill={props.color} points="178.391,189.96 174.934,186.875 169.489,182.018 166.596,179.436 161.154,174.579 158.645,172.34
return (
<SvgIcon sx={props.sx} color={props.color} x='0px' y='0px' viewBox='0 0 300 300'>
<g>
<polygon
fill={props.color}
points='178.391,189.96 174.934,186.875 169.489,182.018 166.596,179.436 161.154,174.579 158.645,172.34
157.717,171.51 152.893,176.918 155.712,179.436 161.154,184.291 164.049,186.875 169.491,191.73 172.947,194.816
177.943,199.271 180.826,197.5 184.389,195.311 183.834,194.816 "/>
<polygon fill={props.color} points="166.034,194.816 160.588,189.96 157.695,187.377 152.256,182.521 150.52,180.975 149.592,180.145
177.943,199.271 180.826,197.5 184.389,195.311 183.834,194.816 '
/>
<polygon
fill={props.color}
points='166.034,194.816 160.588,189.96 157.695,187.377 152.256,182.521 150.52,180.975 149.592,180.145
144.766,185.551 146.813,187.377 152.256,192.232 155.146,194.814 160.59,199.672 164.049,202.756 167.4,205.748 173.85,201.787
169.491,197.9 "/>
<polygon fill={props.color} points="157.695,202.255 152.253,197.398 149.358,194.816 143.916,189.96 142.014,188.26 137.189,193.668
169.491,197.9 '
/>
<polygon
fill={props.color}
points='157.695,202.255 152.253,197.398 149.358,194.816 143.916,189.96 142.014,188.26 137.189,193.668
138.476,194.816 143.92,199.672 146.813,202.254 152.256,207.109 155.712,210.195 157.527,211.816 163.973,207.854
161.154,205.34 "/>
<rect x="173.431" y="172.686" transform="matrix(0.7405 0.672 -0.672 0.7405 165.1439 -72.7109)" fill={props.color} width="6.611" height="9.654"/>
<polygon fill={props.color} points="141.129,211.877 147.035,215.203 150.877,210.975 145.981,206.533 "/>
<polygon fill={props.color} points="186.961,57.038 189.855,59.621 195.298,64.477 198.756,67.562 204.199,72.418 205.449,73.534
161.154,205.34 '
/>
<rect
x='173.431'
y='172.686'
transform='matrix(0.7405 0.672 -0.672 0.7405 165.1439 -72.7109)'
fill={props.color}
width='6.611'
height='9.654'
/>
<polygon
fill={props.color}
points='141.129,211.877 147.035,215.203 150.877,210.975 145.981,206.533 '
/>
<polygon
fill={props.color}
points='186.961,57.038 189.855,59.621 195.298,64.477 198.756,67.562 204.199,72.418 205.449,73.534
210.271,68.126 209.643,67.562 204.199,62.705 200.742,59.621 195.298,54.764 192.402,52.182 186.963,47.326 184.453,45.086
183.525,44.256 178.695,49.665 181.52,52.182 "/>
<polygon fill={props.color} points="178.062,64.979 180.954,67.562 186.398,72.418 189.855,75.503 195.298,80.359 196.396,81.34
183.525,44.256 178.695,49.665 181.52,52.182 '
/>
<polygon
fill={props.color}
points='178.062,64.979 180.954,67.562 186.398,72.418 189.855,75.503 195.298,80.359 196.396,81.34
197.325,82.166 202.146,76.76 200.742,75.503 195.298,70.648 191.841,67.562 186.398,62.705 183.505,60.124 178.062,55.268
176.328,53.72 175.396,52.89 170.572,58.299 172.618,60.124 "/>
<polygon fill={props.color} points="169.723,72.418 172.618,75 178.062,79.856 181.52,82.941 186.962,87.798 188.818,89.456
176.328,53.72 175.396,52.89 170.572,58.299 172.618,60.124 '
/>
<polygon
fill={props.color}
points='169.723,72.418 172.618,75 178.062,79.856 181.52,82.941 186.962,87.798 188.818,89.456
189.747,90.282 194.57,84.875 192.402,82.942 186.962,78.085 183.505,75 178.062,70.144 175.167,67.562 169.723,62.705
167.82,61.006 162.998,66.415 164.282,67.562 "/>
<rect x="199.243" y="45.425" transform="matrix(0.7403 0.6723 -0.6723 0.7403 86.3919 -123.1221)" fill={props.color} width="6.609" height="9.656"/>
<polygon fill={props.color} points="165.298,86.425 170.193,90.867 176.684,83.72 171.789,79.28 "/>
<polygon fill={props.color} points="88.684,102.6 87.755,101.77 82.927,107.177 105.743,127.533 109.679,131.046 114.503,125.64
112.599,123.938 "/>
<polygon fill={props.color} points="80.557,111.232 79.631,110.403 74.803,115.811 94.669,133.537 100.627,138.853 101.553,139.68
106.378,134.272 101.42,129.848 "/>
<polygon fill={props.color} points="72.049,118.52 67.228,123.928 84.469,139.311 92.184,146.195 93.051,146.968 93.978,147.795
98.659,142.548 98.802,142.389 91.107,135.525 "/>
<rect x="103.471" y="102.94" transform="matrix(0.7405 0.6721 -0.6721 0.7405 100.1373 -43.7934)" fill={props.color} width="6.609" height="9.654"/>
<polygon fill={props.color} points="69.529,143.938 72.38,146.525 74.422,148.381 80.622,141.554 80.914,141.234 76.019,136.792 "/>
<path fill={props.color} d="M187.466,112.718c7.511,0,14.995-1.878,21.647-5.43c10.848-5.793,18.796-15.48,22.379-27.277
167.82,61.006 162.998,66.415 164.282,67.562 '
/>
<rect
x='199.243'
y='45.425'
transform='matrix(0.7403 0.6723 -0.6723 0.7403 86.3919 -123.1221)'
fill={props.color}
width='6.609'
height='9.656'
/>
<polygon
fill={props.color}
points='165.298,86.425 170.193,90.867 176.684,83.72 171.789,79.28 '
/>
<polygon
fill={props.color}
points='88.684,102.6 87.755,101.77 82.927,107.177 105.743,127.533 109.679,131.046 114.503,125.64
112.599,123.938 '
/>
<polygon
fill={props.color}
points='80.557,111.232 79.631,110.403 74.803,115.811 94.669,133.537 100.627,138.853 101.553,139.68
106.378,134.272 101.42,129.848 '
/>
<polygon
fill={props.color}
points='72.049,118.52 67.228,123.928 84.469,139.311 92.184,146.195 93.051,146.968 93.978,147.795
98.659,142.548 98.802,142.389 91.107,135.525 '
/>
<rect
x='103.471'
y='102.94'
transform='matrix(0.7405 0.6721 -0.6721 0.7405 100.1373 -43.7934)'
fill={props.color}
width='6.609'
height='9.654'
/>
<polygon
fill={props.color}
points='69.529,143.938 72.38,146.525 74.422,148.381 80.622,141.554 80.914,141.234 76.019,136.792 '
/>
<path
fill={props.color}
d='M187.466,112.718c7.511,0,14.995-1.878,21.647-5.43c10.848-5.793,18.796-15.48,22.379-27.277
c3.583-11.796,2.365-24.266-3.426-35.113c-8.033-15.037-23.645-24.379-40.742-24.379c-7.51,0-14.996,1.878-21.648,5.431
c-10.847,5.793-18.794,15.479-22.377,27.275s-2.366,24.266,3.426,35.113C154.755,103.376,170.365,112.718,187.466,112.718z
M171.232,36.355c16.688-8.912,37.514-2.587,46.43,14.099c8.908,16.688,2.584,37.516-14.104,46.428
c-16.688,8.913-37.518,2.586-46.431-14.1C148.219,66.092,154.544,45.268,171.232,36.355z"/>
<path fill={props.color} d="M247.809,155.361c-6.326-5.757-17.865-13.456-34.295-22.881c-18.544-10.639-43.998-23.456-59.836-27.527
c-16.688,8.913-37.518,2.586-46.431-14.1C148.219,66.092,154.544,45.268,171.232,36.355z'
/>
<path
fill={props.color}
d='M247.809,155.361c-6.326-5.757-17.865-13.456-34.295-22.881c-18.544-10.639-43.998-23.456-59.836-27.527
c-0.785-0.199-1.621-0.3-2.484-0.3c-1.917,0-5.62,0.546-15.812,4.946c-0.823-2.478-1.856-4.885-3.086-7.188
c-8.032-15.037-23.644-24.379-40.743-24.379c-7.509,0-14.995,1.878-21.646,5.43c-10.847,5.792-18.795,15.478-22.378,27.275
c-3.583,11.797-2.365,24.267,3.428,35.113c1.488,2.787,3.262,5.414,5.295,7.841c-3.117,2.348-5.28,4.3-6.706,6.043
@ -60,12 +122,16 @@ export default function BuySatsIcon(props) {
c-8.912-16.687-2.588-37.511,14.1-46.422c16.689-8.914,37.516-2.588,46.43,14.101c1.647,3.086,2.772,6.316,3.407,9.585
C125.997,121.143,126.1,124.779,125.656,128.328z M59.715,174.898l34.511,20.586l-34.511,60.934V174.898z M102.657,200.516
l47.237,28.182l46.69-28.232l37.43,66.088c-0.445,0.067-0.896,0.114-1.358,0.114H68.718c-1.125,0-2.196-0.216-3.19-0.595
L102.657,200.516z"/>
<path fill={props.color} d="M269.921,0H30.08C13.493,0,0,13.494,0,30.08v239.84C0,286.506,13.493,300,30.08,300h239.841
L102.657,200.516z'
/>
<path
fill={props.color}
d='M269.921,0H30.08C13.493,0,0,13.494,0,30.08v239.84C0,286.506,13.493,300,30.08,300h239.841
C286.506,300,300,286.506,300,269.92V30.08C300,13.494,286.506,0,269.921,0z M284.413,269.92c0,7.992-6.501,14.494-14.492,14.494
H30.08c-7.992,0-14.494-6.502-14.494-14.494V30.08c0-7.992,6.502-14.494,14.494-14.494h239.841
c7.991,0,14.492,6.502,14.492,14.494V269.92z"/>
</g>
</SvgIcon>
);
}
c7.991,0,14.492,6.502,14.492,14.494V269.92z'
/>
</g>
</SvgIcon>
);
}

View File

@ -1,11 +1,13 @@
import React, { Component } from "react";
import { SvgIcon } from "@mui/material"
import React, { Component } from 'react';
import { SvgIcon } from '@mui/material';
export default function BuySatsCheckedIcon(props) {
return (
<SvgIcon sx={props.sx} color={props.color} x="0px" y="0px" viewBox="0 0 300 300">
<g>
<path fill={props.color} d="M269.921,0H30.08C13.493,0,0,13.494,0,30.08v239.84C0,286.506,13.493,300,30.08,300h239.841
return (
<SvgIcon sx={props.sx} color={props.color} x='0px' y='0px' viewBox='0 0 300 300'>
<g>
<path
fill={props.color}
d='M269.921,0H30.08C13.493,0,0,13.494,0,30.08v239.84C0,286.506,13.493,300,30.08,300h239.841
C286.506,300,300,286.506,300,269.92V30.08C300,13.494,286.506,0,269.921,0z M143.299,53.225
c3.583-11.796,11.53-21.482,22.377-27.275c6.652-3.553,14.139-5.431,21.648-5.431c15.026,0,28.906,7.216,37.501,19.173
c0.573,0.797,1.123,1.615,1.647,2.454c0.56,0.895,1.092,1.812,1.594,2.752c5.791,10.847,7.009,23.317,3.426,35.113
@ -16,8 +18,11 @@ export default function BuySatsCheckedIcon(props) {
c0.334-2.09,0.804-4.173,1.43-6.234C51.112,98.94,59.06,89.254,69.907,83.462c6.651-3.552,14.137-5.43,21.646-5.43
c17.099,0,32.711,9.342,40.743,24.379c1.23,2.303,2.263,4.71,3.086,7.188c10.192-4.4,13.895-4.946,15.812-4.946
c0.863,0,1.699,0.101,2.484,0.3c15.838,4.071,41.292,16.888,59.836,27.527c16.43,9.425,27.969,17.124,34.295,22.881
C253.963,160.965,254.477,164.032,254.473,166.466L254.473,166.466z"/>
<path fill={props.color} d="M111.93,162.154c-14.119,7.541-30.589,6.39-43.265-1.563c-2.595,1.757-4.684,3.269-6.238,4.499
C253.963,160.965,254.477,164.032,254.473,166.466L254.473,166.466z'
/>
<path
fill={props.color}
d='M111.93,162.154c-14.119,7.541-30.589,6.39-43.265-1.563c-2.595,1.757-4.684,3.269-6.238,4.499
l56.336,33.61v-0.001c-0.178-1.596-0.265-3.194-0.264-4.787c0.006-8.962,2.806-17.76,8.005-25.075
c0.347-0.488,0.704-0.969,1.072-1.443c0.654-0.843,1.341-1.664,2.061-2.461c0.72-0.796,1.473-1.568,2.257-2.314
c2.746-2.611,5.884-4.899,9.387-6.769c20.966-11.197,47.131-3.25,58.332,17.717c1.73,3.242,3.001,6.613,3.84,10.029l-7.986,4.907
@ -31,8 +36,11 @@ export default function BuySatsCheckedIcon(props) {
z M167.4,205.748l-3.352-2.992l-3.459-3.084l-5.443-4.857l-2.891-2.582l-5.443-4.855l-2.047-1.826l0.58-0.649l4.246-4.757
l0.928,0.83l1.736,1.547l5.439,4.855l2.893,2.583l5.446,4.856l3.457,3.084l4.358,3.887L167.4,205.748z M180.826,197.5l-2.883,1.771
l-4.996-4.455l-3.456-3.086l-5.442-4.855l-2.895-2.584l-5.442-4.855l-2.819-2.518l4.824-5.408l0.928,0.83l2.51,2.239l5.441,4.856
l2.894,2.582l5.444,4.857l3.457,3.085l5.443,4.856l0.554,0.493l0.001,0.001L180.826,197.5z"/>
<path fill={props.color} d="M187.19,100.922c4.427,0.034,8.908-0.79,13.194-2.546c1.071-0.439,2.131-0.937,3.174-1.494
l2.894,2.582l5.444,4.857l3.457,3.085l5.443,4.856l0.554,0.493l0.001,0.001L180.826,197.5z'
/>
<path
fill={props.color}
d='M187.19,100.922c4.427,0.034,8.908-0.79,13.194-2.546c1.071-0.439,2.131-0.937,3.174-1.494
c16.688-8.912,23.012-29.74,14.104-46.428c-6.13-11.472-17.889-18.046-30.062-18.139c-5.533-0.042-11.152,1.255-16.367,4.04
c-16.688,8.913-23.014,29.737-14.105,46.427C163.255,94.253,175.016,100.829,187.19,100.922z M165.298,86.425l6.491-7.145v0
l4.895,4.44l-6.49,7.147L165.298,86.425z M189.747,90.282l-0.929-0.826l-1.856-1.658l-5.442-4.857l-3.457-3.085L172.618,75
@ -41,12 +49,21 @@ export default function BuySatsCheckedIcon(props) {
M183.525,44.256l0.928,0.83l2.51,2.24l5.439,4.856l2.896,2.582l5.444,4.857l3.457,3.084l5.443,4.857l0.629,0.564l-4.822,5.408
l-1.25-1.116l-5.443-4.856l-3.458-3.085l-5.442-4.856l-2.895-2.583l-5.441-4.856l-2.824-2.517L183.525,44.256z M176.328,53.72
l1.734,1.548l5.442,4.856l2.894,2.581l5.442,4.857l3.457,3.086l5.444,4.855l1.404,1.257l-4.821,5.406l-0.001,0l-0.928-0.826
l-1.099-0.981l-5.442-4.856l-3.457-3.085l-5.444-4.856l-2.892-2.583l-5.444-4.855l-2.046-1.825l4.824-5.409L176.328,53.72z"/>
<polygon fill={props.color} points="59.715,174.898 59.715,256.418 94.226,195.484 "/>
<path fill={props.color} d="M241.417,259.706c0.151-0.657,0.24-1.34,0.24-2.042v-84.453l-36.673,22.176L241.417,259.706z"/>
<path fill={props.color} d="M196.584,200.466l-46.69,28.232l-47.237-28.183l-37.129,65.557l0,0.001c0.994,0.379,2.065,0.595,3.19,0.595
h163.938c0.462,0,0.912-0.047,1.357-0.113l-0.001-0.002L196.584,200.466z"/>
<path fill={props.color} d="M68.179,149.165c2.65,2.481,5.651,4.502,8.876,6.015c9.464,4.428,20.827,4.506,30.732-0.785
l-1.099-0.981l-5.442-4.856l-3.457-3.085l-5.444-4.856l-2.892-2.583l-5.444-4.855l-2.046-1.825l4.824-5.409L176.328,53.72z'
/>
<polygon fill={props.color} points='59.715,174.898 59.715,256.418 94.226,195.484 ' />
<path
fill={props.color}
d='M241.417,259.706c0.151-0.657,0.24-1.34,0.24-2.042v-84.453l-36.673,22.176L241.417,259.706z'
/>
<path
fill={props.color}
d='M196.584,200.466l-46.69,28.232l-47.237-28.183l-37.129,65.557l0,0.001c0.994,0.379,2.065,0.595,3.19,0.595
h163.938c0.462,0,0.912-0.047,1.357-0.113l-0.001-0.002L196.584,200.466z'
/>
<path
fill={props.color}
d='M68.179,149.165c2.65,2.481,5.651,4.502,8.876,6.015c9.464,4.428,20.827,4.506,30.732-0.785
c1.276-0.682,2.492-1.434,3.646-2.247c8.07-5.697,13.056-14.459,14.224-23.819c0.222-1.774,0.307-3.57,0.25-5.371
c-0.058-1.802-0.258-3.608-0.607-5.403c-0.635-3.269-1.76-6.499-3.407-9.585c-8.914-16.688-29.741-23.015-46.43-14.101
c-11.474,6.126-18.048,17.884-18.14,30.057c-0.042,5.533,1.255,11.151,4.04,16.365C63.182,143.697,65.503,146.663,68.179,149.165z
@ -54,8 +71,9 @@ export default function BuySatsCheckedIcon(props) {
l-0.927-0.827l-0.867-0.773l-7.715-6.884l-17.241-15.383l0,0l4.82-5.408l19.058,17.005l7.695,6.864L98.659,142.548z
M101.553,139.68l-0.926-0.827l-5.958-5.316l-19.866-17.726l4.828-5.408l0.926,0.829l20.863,18.616l4.958,4.424L101.553,139.68z
M109.679,131.046l-3.936-3.513l-22.816-20.356l4.828-5.407l0.929,0.83l23.915,21.338l1.904,1.702L109.679,131.046z
M101.086,109.122l6.486-7.146l4.892,4.44l0.002,0.002l-6.489,7.149l-4.894-4.442L101.086,109.122z"/>
</g>
</SvgIcon>
);
}
M101.086,109.122l6.486-7.146l4.892,4.44l0.002,0.002l-6.489,7.149l-4.894-4.442L101.086,109.122z'
/>
</g>
</SvgIcon>
);
}

View File

@ -1,11 +1,11 @@
import React, { Component } from "react";
import { SvgIcon } from "@mui/material"
import React, { Component } from 'react';
import { SvgIcon } from '@mui/material';
export default function CataloniaFlag(props) {
return (
<SvgIcon {...props} x="0px" y="0px" viewBox="0 0 810 540">
<rect width="810" height="540" fill="#FCDD09"/>
<path stroke="#DA121A" strokeWidth="60" d="M0,90H810m0,120H0m0,120H810m0,120H0"/>
</SvgIcon>
);
}
return (
<SvgIcon {...props} x='0px' y='0px' viewBox='0 0 810 540'>
<rect width='810' height='540' fill='#FCDD09' />
<path stroke='#DA121A' strokeWidth='60' d='M0,90H810m0,120H0m0,120H810m0,120H0' />
</SvgIcon>
);
}

View File

@ -1,16 +1,21 @@
import React, { Component } from "react";
import { SvgIcon } from "@mui/material"
import React, { Component } from 'react';
import { SvgIcon } from '@mui/material';
export default function EarthIcon(props) {
return (
<SvgIcon {...props} x="0px" y="0px" viewBox="0 0 440.45 440.45" >
<g id="XMLID_34_">
<g>
<path style={{fill:"#1EA6C6"}} d="M102.62,211.975c0.02,0.08,0.04,0.15,0.06,0.23c1.91,7.94,3.42,19.8,1.63,35.19
return (
<SvgIcon {...props} x='0px' y='0px' viewBox='0 0 440.45 440.45'>
<g id='XMLID_34_'>
<g>
<path
style={{ fill: '#1EA6C6' }}
d='M102.62,211.975c0.02,0.08,0.04,0.15,0.06,0.23c1.91,7.94,3.42,19.8,1.63,35.19
c-1.91,16.48,5.5,32.66,19.11,42.15l18.56,12.95c0,0-6.37,48.72,11.31,74.36v0.01C65.91,359.665,0,282.645,0,190.225
c0-53.81,22.35-102.41,58.27-137c-14.15,28.48-28.54,72.45-8.29,109.27c9.27,16.85,23.9,27.78,40.15,34.8
C96.36,199.975,101.02,205.395,102.62,211.975z"/>
<path style={{fill:"#1EA6C6"}} d="M190.23,0.005c41.46,0,79.82,13.26,111.07,35.79c0,0-16.37,25.11-15.71,50.17
C96.36,199.975,101.02,205.395,102.62,211.975z'
/>
<path
style={{ fill: '#1EA6C6' }}
d='M190.23,0.005c41.46,0,79.82,13.26,111.07,35.79c0,0-16.37,25.11-15.71,50.17
c0.15,5.99,4.84,10.89,10.81,11.52c0.42,0.04,0.85,0.07,1.27,0.07c4.55,0,8.75-2.58,10.81-6.73c4.38-8.78,15.91-19.42,45.02,1.77
l0.01-0.01c2.17,3.61,4.22,7.3,6.14,11.07c0,0-10.16,2.28-23.77,0.6c-3.78-0.47-7.6,0.45-10.69,2.67
c-2.15,1.55-4.62,3.78-7.36,6.53c-13.89,13.96-16.19,43-16.19,43c-0.27,6.06,0,12.76,0.97,20.16
@ -20,28 +25,47 @@ export default function EarthIcon(props) {
c-16.59-10.07-36.14-23.07-48.73-35.16c-6.33-6.07-15.04-8.98-23.76-8.04c-11.2,1.21-27.07,3.65-42.8,8.55
c2.7-26.79-43.7-50.71-31.7-71.71c6.5-11.39,17.42-11.6,26.05-9.41c6.87,1.74,14.08-0.76,18.54-6.27
c10.56-13.03,33.63-25.24,50.49-32.96c11.05-5.05,14.62-18.98,7.36-28.71c-22.27-29.9-53.82-49.59-53.82-49.59
C141.44,4.815,165.27,0.005,190.23,0.005z M197.98,61.495l23-30c0,0-10-9-46-12C167.79,18.895,194.98,38.495,197.98,61.495z"/>
<path style={{fill:"#F7CF52"}} d="M353.51,92.585l-0.01,0.01c-29.11-21.19-40.64-10.55-45.02-1.77c-2.06,4.15-6.26,6.73-10.81,6.73
C141.44,4.815,165.27,0.005,190.23,0.005z M197.98,61.495l23-30c0,0-10-9-46-12C167.79,18.895,194.98,38.495,197.98,61.495z'
/>
<path
style={{ fill: '#F7CF52' }}
d='M353.51,92.585l-0.01,0.01c-29.11-21.19-40.64-10.55-45.02-1.77c-2.06,4.15-6.26,6.73-10.81,6.73
c-0.42,0-0.85-0.03-1.27-0.07c-5.97-0.63-10.66-5.53-10.81-11.52c-0.66-25.06,15.71-50.17,15.71-50.17
C322.33,50.935,340.14,70.275,353.51,92.585z"/>
<path style={{fill:"#F7CF52"}} d="M380.45,190.225c0,22.57-3.93,44.23-11.15,64.32h-0.01c-6.07-10.32-8.37-22.55-8.98-33.34
C322.33,50.935,340.14,70.275,353.51,92.585z'
/>
<path
style={{ fill: '#F7CF52' }}
d='M380.45,190.225c0,22.57-3.93,44.23-11.15,64.32h-0.01c-6.07-10.32-8.37-22.55-8.98-33.34
c-0.68-11.96-11.49-20.89-23.33-19.05c-0.24,0.03-0.48,0.07-0.72,0.1c-16.41,2.4-31.48-9.2-33.65-25.64
c-0.97-7.4-1.24-14.1-0.97-20.16c0,0,2.3-29.04,16.19-43c2.74-2.75,5.21-4.98,7.36-6.53c3.09-2.22,6.91-3.14,10.69-2.67
c13.61,1.68,23.77-0.6,23.77-0.6C372.95,129.625,380.45,159.045,380.45,190.225z"/>
<path style={{fill:"#F7CF52"}} d="M220.98,31.495l-23,30c-3-23-30.19-42.6-23-42C210.98,22.495,220.98,31.495,220.98,31.495z"/>
<path style={{fill:"#F7CF52"}} d="M173.42,63.145c7.26,9.73,3.69,23.66-7.36,28.71c-16.86,7.72-39.93,19.93-50.49,32.96
c13.61,1.68,23.77-0.6,23.77-0.6C372.95,129.625,380.45,159.045,380.45,190.225z'
/>
<path
style={{ fill: '#F7CF52' }}
d='M220.98,31.495l-23,30c-3-23-30.19-42.6-23-42C210.98,22.495,220.98,31.495,220.98,31.495z'
/>
<path
style={{ fill: '#F7CF52' }}
d='M173.42,63.145c7.26,9.73,3.69,23.66-7.36,28.71c-16.86,7.72-39.93,19.93-50.49,32.96
c-4.46,5.51-11.67,8.01-18.54,6.27c-8.63-2.19-19.55-1.98-26.05,9.41c-12,21,34.4,44.92,31.7,71.71
c-0.02-0.08-0.04-0.15-0.06-0.23c-1.6-6.58-6.26-12-12.49-14.68c-16.25-7.02-30.88-17.95-40.15-34.8
c-20.25-36.82-5.86-80.79,8.29-109.27c17.54-16.91,38.33-30.47,61.33-39.67C119.6,13.555,151.15,33.245,173.42,63.145z"/>
<path style={{fill:"#F7CF52"}} d="M217.97,246.855c17.77,10.79,21.93,34.83,8.76,50.92c-7.41,9.05-15.93,18.74-24.47,28.12
c-20.25-36.82-5.86-80.79,8.29-109.27c17.54-16.91,38.33-30.47,61.33-39.67C119.6,13.555,151.15,33.245,173.42,63.145z'
/>
<path
style={{ fill: '#F7CF52' }}
d='M217.97,246.855c17.77,10.79,21.93,34.83,8.76,50.92c-7.41,9.05-15.93,18.74-24.47,28.12
l-29.58,31.37c-14.04,14.58-19.39,19.59-19.39,19.59c-17.68-25.64-11.31-74.36-11.31-74.36l-18.56-12.95
c-13.61-9.49-21.02-25.67-19.11-42.15c1.79-15.39,0.28-27.25-1.63-35.19c15.73-4.9,31.6-7.34,42.8-8.55
c8.72-0.94,17.43,1.97,23.76,8.04C181.83,223.785,201.38,236.785,217.97,246.855z"/>
</g>
c8.72-0.94,17.43,1.97,23.76,8.04C181.83,223.785,201.38,236.785,217.97,246.855z'
/>
</g>
<path style={{opacity:"0.3",fill:"#808080",enableBackground:"new"}} d="M190.23,0.005c5.8,0,11.54,0.26,17.2,0.77
</g>
<path
style={{ opacity: '0.3', fill: '#808080', enableBackground: 'new' }}
d='M190.23,0.005c5.8,0,11.54,0.26,17.2,0.77
C110.48,9.515,34.5,90.995,34.5,190.225c0,99.25,76.01,180.74,172.99,189.45c-5.68,0.51-11.44,0.77-17.26,0.77
C85.17,380.445,0,295.285,0,190.225S85.17,0.005,190.23,0.005z"/>
</SvgIcon>
);
}
C85.17,380.445,0,295.285,0,190.225S85.17,0.005,190.23,0.005z'
/>
</SvgIcon>
);
}

View File

@ -1,10 +1,10 @@
import React, { Component } from "react";
import { SvgIcon } from "@mui/material"
import React, { Component } from 'react';
import { SvgIcon } from '@mui/material';
export default function ExportIcon(props) {
return (
<SvgIcon sx={props.sx} color={props.color} viewBox="0 0 576 512">
<path d="M192 312C192 298.8 202.8 288 216 288H384V160H256c-17.67 0-32-14.33-32-32L224 0H48C21.49 0 0 21.49 0 48v416C0 490.5 21.49 512 48 512h288c26.51 0 48-21.49 48-48v-128H216C202.8 336 192 325.3 192 312zM256 0v128h128L256 0zM568.1 295l-80-80c-9.375-9.375-24.56-9.375-33.94 0s-9.375 24.56 0 33.94L494.1 288H384v48h110.1l-39.03 39.03C450.3 379.7 448 385.8 448 392s2.344 12.28 7.031 16.97c9.375 9.375 24.56 9.375 33.94 0l80-80C578.3 319.6 578.3 304.4 568.1 295z"/>
<SvgIcon sx={props.sx} color={props.color} viewBox='0 0 576 512'>
<path d='M192 312C192 298.8 202.8 288 216 288H384V160H256c-17.67 0-32-14.33-32-32L224 0H48C21.49 0 0 21.49 0 48v416C0 490.5 21.49 512 48 512h288c26.51 0 48-21.49 48-48v-128H216C202.8 336 192 325.3 192 312zM256 0v128h128L256 0zM568.1 295l-80-80c-9.375-9.375-24.56-9.375-33.94 0s-9.375 24.56 0 33.94L494.1 288H384v48h110.1l-39.03 39.03C450.3 379.7 448 385.8 448 392s2.344 12.28 7.031 16.97c9.375 9.375 24.56 9.375 33.94 0l80-80C578.3 319.6 578.3 304.4 568.1 295z' />
</SvgIcon>
);
}
}

View File

@ -1,21 +1,48 @@
import React, { Component } from "react";
import { SvgIcon } from "@mui/material"
import React, { Component } from 'react';
import { SvgIcon } from '@mui/material';
export default function GoldIcon(props) {
return (
<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:"#FFCE54"}} points="350.216,176.572 107.756,284.345 125.753,374.324 386.13,258.531 "/>
<polygon style={{fill:"#E8AA3D"}} points="107.756,284.345 37.038,264.123 0.015,338.207 125.753,374.324 "/>
<polygon style={{fill:"#F6BB42"}} points="475.969,212.682 404.127,194.717 162.791,300.232 125.753,374.324 251.504,410.41
511.882,294.625 "/>
<polygon style={{fill:"#FFCE54"}} points="475.969,212.682 233.508,320.431 251.504,410.41 511.882,294.625 "/>
<polygon style={{fill:"#E8AA3D"}} points="233.508,320.431 162.791,300.232 125.753,374.324 251.504,410.41 "/>
<polygon style={{fill:"#F6BB42"}} points="396.316,119.429 324.488,101.473 103.867,198.435 66.843,272.519 192.596,308.621
432.245,201.379 "/>
<polygon style={{fill:"#FFCE54"}} points="396.316,119.429 174.6,218.641 192.596,308.621 432.245,201.379 "/>
<polygon style={{fill:"#E8AA3D"}} points="174.6,218.641 103.867,198.435 66.843,272.519 192.596,308.621 "/>
</SvgIcon>
);
}
return (
<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: '#FFCE54' }}
points='350.216,176.572 107.756,284.345 125.753,374.324 386.13,258.531 '
/>
<polygon
style={{ fill: '#E8AA3D' }}
points='107.756,284.345 37.038,264.123 0.015,338.207 125.753,374.324 '
/>
<polygon
style={{ fill: '#F6BB42' }}
points='475.969,212.682 404.127,194.717 162.791,300.232 125.753,374.324 251.504,410.41
511.882,294.625 '
/>
<polygon
style={{ fill: '#FFCE54' }}
points='475.969,212.682 233.508,320.431 251.504,410.41 511.882,294.625 '
/>
<polygon
style={{ fill: '#E8AA3D' }}
points='233.508,320.431 162.791,300.232 125.753,374.324 251.504,410.41 '
/>
<polygon
style={{ fill: '#F6BB42' }}
points='396.316,119.429 324.488,101.473 103.867,198.435 66.843,272.519 192.596,308.621
432.245,201.379 '
/>
<polygon
style={{ fill: '#FFCE54' }}
points='396.316,119.429 174.6,218.641 192.596,308.621 432.245,201.379 '
/>
<polygon
style={{ fill: '#E8AA3D' }}
points='174.6,218.641 103.867,198.435 66.843,272.519 192.596,308.621 '
/>
</SvgIcon>
);
}

View File

@ -1,10 +1,10 @@
import React, { Component } from "react";
import { SvgIcon } from "@mui/material"
import React, { Component } from 'react';
import { SvgIcon } from '@mui/material';
export default function NewTabIcon(props) {
return (
<SvgIcon sx={props.sx} color={props.color} viewBox="0 0 448 512">
<path d="M256 64C256 46.33 270.3 32 288 32H415.1C415.1 32 415.1 32 415.1 32C420.3 32 424.5 32.86 428.2 34.43C431.1 35.98 435.5 38.27 438.6 41.3C438.6 41.35 438.6 41.4 438.7 41.44C444.9 47.66 447.1 55.78 448 63.9C448 63.94 448 63.97 448 64V192C448 209.7 433.7 224 416 224C398.3 224 384 209.7 384 192V141.3L214.6 310.6C202.1 323.1 181.9 323.1 169.4 310.6C156.9 298.1 156.9 277.9 169.4 265.4L338.7 96H288C270.3 96 256 81.67 256 64V64zM0 128C0 92.65 28.65 64 64 64H160C177.7 64 192 78.33 192 96C192 113.7 177.7 128 160 128H64V416H352V320C352 302.3 366.3 288 384 288C401.7 288 416 302.3 416 320V416C416 451.3 387.3 480 352 480H64C28.65 480 0 451.3 0 416V128z"/>
<SvgIcon sx={props.sx} color={props.color} viewBox='0 0 448 512'>
<path d='M256 64C256 46.33 270.3 32 288 32H415.1C415.1 32 415.1 32 415.1 32C420.3 32 424.5 32.86 428.2 34.43C431.1 35.98 435.5 38.27 438.6 41.3C438.6 41.35 438.6 41.4 438.7 41.44C444.9 47.66 447.1 55.78 448 63.9C448 63.94 448 63.97 448 64V192C448 209.7 433.7 224 416 224C398.3 224 384 209.7 384 192V141.3L214.6 310.6C202.1 323.1 181.9 323.1 169.4 310.6C156.9 298.1 156.9 277.9 169.4 265.4L338.7 96H288C270.3 96 256 81.67 256 64V64zM0 128C0 92.65 28.65 64 64 64H160C177.7 64 192 78.33 192 96C192 113.7 177.7 128 160 128H64V416H352V320C352 302.3 366.3 288 384 288C401.7 288 416 302.3 416 320V416C416 451.3 387.3 480 352 480H64C28.65 480 0 451.3 0 416V128z' />
</SvgIcon>
);
}
}

View File

@ -1,11 +1,12 @@
import React, { Component } from "react";
import { SvgIcon } from "@mui/material"
import React, { Component } from 'react';
import { SvgIcon } from '@mui/material';
export default function RoboSatsIcon(props) {
return (
<SvgIcon {...props} x="0px" y="0px" width="1000px" height="1000px" viewBox="0 0 1000 900">
<g>
<path d="M602.336,731.51c16.747-16.94,29.249-35.524,37.504-56.694c18.792-48.193,16.967-94.996-10.46-139.81
return (
<SvgIcon {...props} x='0px' y='0px' width='1000px' height='1000px' viewBox='0 0 1000 900'>
<g>
<path
d='M602.336,731.51c16.747-16.94,29.249-35.524,37.504-56.694c18.792-48.193,16.967-94.996-10.46-139.81
c-10.255-16.756-24.983-29.293-39.461-42.103c-67.731-59.932-135.412-119.919-203.104-179.895
c-0.368-0.326-0.644-0.755-1.331-1.579c18.529-12.477,36.983-24.903,55.872-37.62c-9.61-6.799-18.917-13.385-28.648-20.27
c11.763-14.483,23.273-28.656,34.738-42.773c13.313,7.081,24.784,5.523,32.075-4.132c6.395-8.467,5.794-20.59-1.412-28.52
@ -15,46 +16,64 @@ export default function RoboSatsIcon(props) {
c37.661,0.119,75.351,1.898,112.093,11.01c52.81,13.096,95.741,40.904,125.379,87.462c13.802,21.681,20.643,45.764,23.136,71.039
c3.595,36.436,1.313,72.517-8.858,107.873c-11.943,41.515-37.09,74.011-69.641,101.357c-16.133,13.552-33.803,24.811-52.581,34.343
c-1.3,0.659-2.533,1.445-4.148,2.375c80.735,102.152,161.255,204.034,242.318,306.6C761.843,731.51,682.637,731.51,602.336,731.51z
"/>
<path d="M282.877,389.186c25.706-0.109,46.42,20.376,46.55,46.038c0.131,25.994-20.404,46.852-46.238,46.96
c-25.588,0.108-46.928-21.172-46.758-46.627C236.602,409.95,257.291,389.295,282.877,389.186z"/>
<path d="M445.93,607.736c0.705-26.031,21.515-46.381,46.915-45.881c26.295,0.52,46.657,21.756,45.918,47.887
c-0.721,25.455-21.862,45.67-47.178,45.104C465.779,654.273,445.244,633.082,445.93,607.736z"/>
<path d="M175.223,550.758c23.365,20.689,46.15,40.865,69.337,61.396c-4.974,5.619-9.792,11.063-14.91,16.846
'
/>
<path
d='M282.877,389.186c25.706-0.109,46.42,20.376,46.55,46.038c0.131,25.994-20.404,46.852-46.238,46.96
c-25.588,0.108-46.928-21.172-46.758-46.627C236.602,409.95,257.291,389.295,282.877,389.186z'
/>
<path
d='M445.93,607.736c0.705-26.031,21.515-46.381,46.915-45.881c26.295,0.52,46.657,21.756,45.918,47.887
c-0.721,25.455-21.862,45.67-47.178,45.104C465.779,654.273,445.244,633.082,445.93,607.736z'
/>
<path
d='M175.223,550.758c23.365,20.689,46.15,40.865,69.337,61.396c-4.974,5.619-9.792,11.063-14.91,16.846
c-5.634-4.988-11.167-9.738-16.519-14.684c-3.131-2.896-5.343-2.492-8.415,0.467c-9.944,9.58-20.234,18.801-29.493,27.332
C175.223,613.414,175.223,582.512,175.223,550.758z"/>
<path d="M379.124,731.533c-30.045,0-59.057,0-89.151,0c8.955-9.23,17.236-17.769,25.724-26.519
C175.223,613.414,175.223,582.512,175.223,550.758z'
/>
<path
d='M379.124,731.533c-30.045,0-59.057,0-89.151,0c8.955-9.23,17.236-17.769,25.724-26.519
c-6.368-5.709-12.409-11.127-18.739-16.803c4.904-5.559,9.594-10.877,14.65-16.608C334.013,691.492,356.2,711.186,379.124,731.533z
"/>
</g>
<g>
<path d="M208.875,819.362h-15.495v34.557h-19.45v-94.397h35.075c11.151,0,19.752,2.485,25.804,7.455
'
/>
</g>
<g>
<path
d='M208.875,819.362h-15.495v34.557h-19.45v-94.397h35.075c11.151,0,19.752,2.485,25.804,7.455
c6.051,4.972,9.077,11.995,9.077,21.071c0,6.44-1.394,11.811-4.182,16.111s-7.013,7.727-12.675,10.276l20.422,38.576v0.907h-20.876
L208.875,819.362z M193.379,803.608h15.69c4.884,0,8.666-1.242,11.346-3.729c2.68-2.484,4.02-5.91,4.02-10.276
c0-4.451-1.264-7.952-3.792-10.503c-2.529-2.55-6.409-3.825-11.638-3.825h-15.625V803.608z"/>
<path d="M336.208,808.859c0,9.294-1.643,17.44-4.927,24.442c-3.285,7.002-7.985,12.405-14.101,16.209
c0-4.451-1.264-7.952-3.792-10.503c-2.529-2.55-6.409-3.825-11.638-3.825h-15.625V803.608z'
/>
<path
d='M336.208,808.859c0,9.294-1.643,17.44-4.927,24.442c-3.285,7.002-7.985,12.405-14.101,16.209
c-6.117,3.804-13.129,5.705-21.039,5.705c-7.824,0-14.805-1.881-20.941-5.641c-6.138-3.761-10.892-9.131-14.263-16.111
c-3.372-6.979-5.08-15.009-5.122-24.086v-4.668c0-9.292,1.674-17.473,5.024-24.539c3.349-7.067,8.082-12.491,14.199-16.273
c6.116-3.781,13.106-5.673,20.974-5.673c7.866,0,14.857,1.892,20.974,5.673c6.116,3.782,10.849,9.206,14.199,16.273
c3.349,7.066,5.024,15.226,5.024,24.475V808.859z M316.499,804.58c0-9.896-1.773-17.418-5.316-22.562
c-3.545-5.144-8.602-7.716-15.171-7.716c-6.527,0-11.563,2.54-15.106,7.618c-3.545,5.079-5.339,12.524-5.381,22.335v4.604
c0,9.639,1.772,17.116,5.316,22.433c3.543,5.316,8.644,7.975,15.301,7.975c6.526,0,11.541-2.561,15.042-7.683
s5.272-12.588,5.316-22.4V804.58z"/>
<path d="M350.342,853.919v-94.397h33.065c11.453,0,20.141,2.193,26.063,6.58c5.921,4.388,8.882,10.817,8.882,19.288
s5.272-12.588,5.316-22.4V804.58z'
/>
<path
d='M350.342,853.919v-94.397h33.065c11.453,0,20.141,2.193,26.063,6.58c5.921,4.388,8.882,10.817,8.882,19.288
c0,4.626-1.189,8.699-3.566,12.222c-2.377,3.522-5.684,6.105-9.919,7.747c4.84,1.211,8.655,3.653,11.443,7.326
c2.788,3.675,4.182,8.169,4.182,13.485c0,9.077-2.896,15.949-8.688,20.617c-5.792,4.668-14.047,7.046-24.767,7.132H350.342z
M369.792,799.069h14.393c9.811-0.172,14.717-4.084,14.717-11.734c0-4.279-1.243-7.358-3.728-9.239
c-2.486-1.88-6.408-2.82-11.767-2.82h-13.615V799.069z M369.792,812.814v25.479h16.662c4.581,0,8.158-1.091,10.73-3.274
c2.571-2.182,3.858-5.196,3.858-9.044c0-8.645-4.474-13.031-13.421-13.161H369.792z"/>
<path d="M512.621,808.859c0,9.294-1.645,17.44-4.928,24.442c-3.285,7.002-7.986,12.405-14.102,16.209
c2.571-2.182,3.858-5.196,3.858-9.044c0-8.645-4.474-13.031-13.421-13.161H369.792z'
/>
<path
d='M512.621,808.859c0,9.294-1.645,17.44-4.928,24.442c-3.285,7.002-7.986,12.405-14.102,16.209
c-6.117,3.804-13.129,5.705-21.039,5.705c-7.824,0-14.805-1.881-20.941-5.641c-6.138-3.761-10.892-9.131-14.263-16.111
c-3.372-6.979-5.08-15.009-5.122-24.086v-4.668c0-9.292,1.674-17.473,5.024-24.539c3.349-7.067,8.082-12.491,14.199-16.273
c6.116-3.781,13.106-5.673,20.974-5.673c7.866,0,14.857,1.892,20.974,5.673c6.116,3.782,10.849,9.206,14.199,16.273
c3.35,7.066,5.025,15.226,5.025,24.475V808.859z M492.911,804.58c0-9.896-1.773-17.418-5.316-22.562
c-3.545-5.144-8.602-7.716-15.171-7.716c-6.527,0-11.563,2.54-15.106,7.618c-3.545,5.079-5.339,12.524-5.381,22.335v4.604
c0,9.639,1.772,17.116,5.316,22.433c3.543,5.316,8.644,7.975,15.301,7.975c6.526,0,11.541-2.561,15.042-7.683
s5.272-12.588,5.316-22.4V804.58z"/>
<path d="M575.704,829.152c0-3.673-1.297-6.493-3.891-8.461c-2.593-1.966-7.261-4.041-14.004-6.224
s5.272-12.588,5.316-22.4V804.58z'
/>
<path
d='M575.704,829.152c0-3.673-1.297-6.493-3.891-8.461c-2.593-1.966-7.261-4.041-14.004-6.224
c-6.742-2.183-12.081-4.333-16.014-6.451c-10.72-5.791-16.079-13.593-16.079-23.405c0-5.1,1.437-9.648,4.312-13.647
c2.874-3.997,7.002-7.12,12.384-9.368c5.381-2.247,11.421-3.371,18.121-3.371c6.742,0,12.75,1.222,18.023,3.663
c5.272,2.442,9.368,5.89,12.286,10.341c2.917,4.452,4.376,9.509,4.376,15.171h-19.45c0-4.321-1.361-7.683-4.084-10.081
@ -62,11 +81,15 @@ export default function RoboSatsIcon(props) {
c0,3.069,1.545,5.641,4.636,7.715c3.09,2.075,7.64,4.02,13.647,5.835c11.064,3.329,19.126,7.456,24.184,12.384
c5.057,4.927,7.585,11.065,7.585,18.412c0,8.169-3.091,14.578-9.271,19.224c-6.181,4.646-14.501,6.97-24.961,6.97
c-7.261,0-13.874-1.329-19.839-3.987s-10.514-6.299-13.647-10.925c-3.134-4.624-4.7-9.984-4.7-16.078h19.515
c0,10.416,6.225,15.624,18.672,15.624c4.625,0,8.234-0.939,10.827-2.82C574.407,835.149,575.704,832.523,575.704,829.152z"/>
<path d="M661.673,834.469H627.57l-6.483,19.45h-20.682l35.14-94.397h18.023l35.335,94.397h-20.683L661.673,834.469z
M632.822,818.714h23.599l-11.864-35.334L632.822,818.714z"/>
<path d="M760.999,775.275h-28.916v78.644h-19.45v-78.644h-28.526v-15.754h76.893V775.275z"/>
<path d="M819.997,829.152c0-3.673-1.297-6.493-3.891-8.461c-2.593-1.966-7.261-4.041-14.004-6.224
c0,10.416,6.225,15.624,18.672,15.624c4.625,0,8.234-0.939,10.827-2.82C574.407,835.149,575.704,832.523,575.704,829.152z'
/>
<path
d='M661.673,834.469H627.57l-6.483,19.45h-20.682l35.14-94.397h18.023l35.335,94.397h-20.683L661.673,834.469z
M632.822,818.714h23.599l-11.864-35.334L632.822,818.714z'
/>
<path d='M760.999,775.275h-28.916v78.644h-19.45v-78.644h-28.526v-15.754h76.893V775.275z' />
<path
d='M819.997,829.152c0-3.673-1.297-6.493-3.891-8.461c-2.593-1.966-7.261-4.041-14.004-6.224
c-6.742-2.183-12.081-4.333-16.014-6.451c-10.72-5.791-16.079-13.593-16.079-23.405c0-5.1,1.437-9.648,4.312-13.647
c2.874-3.997,7.002-7.12,12.384-9.368c5.381-2.247,11.421-3.371,18.121-3.371c6.742,0,12.75,1.222,18.023,3.663
c5.272,2.442,9.368,5.89,12.286,10.341c2.917,4.452,4.376,9.509,4.376,15.171h-19.45c0-4.321-1.361-7.683-4.084-10.081
@ -74,8 +97,9 @@ export default function RoboSatsIcon(props) {
c0,3.069,1.545,5.641,4.636,7.715c3.09,2.075,7.64,4.02,13.647,5.835c11.064,3.329,19.126,7.456,24.184,12.384
c5.057,4.927,7.585,11.065,7.585,18.412c0,8.169-3.091,14.578-9.271,19.224c-6.181,4.646-14.501,6.97-24.961,6.97
c-7.261,0-13.874-1.329-19.839-3.987s-10.514-6.299-13.647-10.925c-3.134-4.624-4.7-9.984-4.7-16.078h19.515
c0,10.416,6.225,15.624,18.672,15.624c4.625,0,8.234-0.939,10.827-2.82C818.7,835.149,819.997,832.523,819.997,829.152z"/>
</g>
</SvgIcon>
);
}
c0,10.416,6.225,15.624,18.672,15.624c4.625,0,8.234-0.939,10.827-2.82C818.7,835.149,819.997,832.523,819.997,829.152z'
/>
</g>
</SvgIcon>
);
}

View File

@ -1,11 +1,12 @@
import React, { Component } from "react";
import { SvgIcon } from "@mui/material"
import React, { Component } from 'react';
import { SvgIcon } from '@mui/material';
export default function RoboSatsNoTextIcon(props) {
return (
<SvgIcon {...props} x="0px" y="0px" width="1000px" height="1000px" viewBox="0 0 1000 800">
<g>
<path d="M602.336,731.51c16.747-16.94,29.249-35.524,37.504-56.694c18.792-48.193,16.967-94.996-10.46-139.81
return (
<SvgIcon {...props} x='0px' y='0px' width='1000px' height='1000px' viewBox='0 0 1000 800'>
<g>
<path
d='M602.336,731.51c16.747-16.94,29.249-35.524,37.504-56.694c18.792-48.193,16.967-94.996-10.46-139.81
c-10.255-16.756-24.983-29.293-39.461-42.103c-67.731-59.932-135.412-119.919-203.104-179.895
c-0.368-0.326-0.644-0.755-1.331-1.579c18.529-12.477,36.983-24.903,55.872-37.62c-9.61-6.799-18.917-13.385-28.648-20.27
c11.763-14.483,23.273-28.656,34.738-42.773c13.313,7.081,24.784,5.523,32.075-4.132c6.395-8.467,5.794-20.59-1.412-28.52
@ -15,18 +16,27 @@ export default function RoboSatsNoTextIcon(props) {
c37.661,0.119,75.351,1.898,112.093,11.01c52.81,13.096,95.741,40.904,125.379,87.462c13.802,21.681,20.643,45.764,23.136,71.039
c3.595,36.436,1.313,72.517-8.858,107.873c-11.943,41.515-37.09,74.011-69.641,101.357c-16.133,13.552-33.803,24.811-52.581,34.343
c-1.3,0.659-2.533,1.445-4.148,2.375c80.735,102.152,161.255,204.034,242.318,306.6C761.843,731.51,682.637,731.51,602.336,731.51z
"/>
<path d="M282.877,389.186c25.706-0.109,46.42,20.376,46.55,46.038c0.131,25.994-20.404,46.852-46.238,46.96
c-25.588,0.108-46.928-21.172-46.758-46.627C236.602,409.95,257.291,389.295,282.877,389.186z"/>
<path d="M445.93,607.736c0.705-26.031,21.515-46.381,46.915-45.881c26.295,0.52,46.657,21.756,45.918,47.887
c-0.721,25.455-21.862,45.67-47.178,45.104C465.779,654.273,445.244,633.082,445.93,607.736z"/>
<path d="M175.223,550.758c23.365,20.689,46.15,40.865,69.337,61.396c-4.974,5.619-9.792,11.063-14.91,16.846
'
/>
<path
d='M282.877,389.186c25.706-0.109,46.42,20.376,46.55,46.038c0.131,25.994-20.404,46.852-46.238,46.96
c-25.588,0.108-46.928-21.172-46.758-46.627C236.602,409.95,257.291,389.295,282.877,389.186z'
/>
<path
d='M445.93,607.736c0.705-26.031,21.515-46.381,46.915-45.881c26.295,0.52,46.657,21.756,45.918,47.887
c-0.721,25.455-21.862,45.67-47.178,45.104C465.779,654.273,445.244,633.082,445.93,607.736z'
/>
<path
d='M175.223,550.758c23.365,20.689,46.15,40.865,69.337,61.396c-4.974,5.619-9.792,11.063-14.91,16.846
c-5.634-4.988-11.167-9.738-16.519-14.684c-3.131-2.896-5.343-2.492-8.415,0.467c-9.944,9.58-20.234,18.801-29.493,27.332
C175.223,613.414,175.223,582.512,175.223,550.758z"/>
<path d="M379.124,731.533c-30.045,0-59.057,0-89.151,0c8.955-9.23,17.236-17.769,25.724-26.519
C175.223,613.414,175.223,582.512,175.223,550.758z'
/>
<path
d='M379.124,731.533c-30.045,0-59.057,0-89.151,0c8.955-9.23,17.236-17.769,25.724-26.519
c-6.368-5.709-12.409-11.127-18.739-16.803c4.904-5.559,9.594-10.877,14.65-16.608C334.013,691.492,356.2,711.186,379.124,731.533z
"/>
</g>
</SvgIcon>
);
}
'
/>
</g>
</SvgIcon>
);
}

View File

@ -1,11 +1,12 @@
import React, { Component } from "react";
import { SvgIcon } from "@mui/material"
import React, { Component } from 'react';
import { SvgIcon } from '@mui/material';
export default function RoboSatsTextIcon(props) {
return (
<SvgIcon {...props} x="0px" y="0px" width="2000px" height="1000px" viewBox="0 300 2000 150">
<g>
<path d="M455.556,849.519c10.487-10.606,18.315-22.243,23.484-35.499c11.767-30.177,10.624-59.483-6.55-87.546
return (
<SvgIcon {...props} x='0px' y='0px' width='2000px' height='1000px' viewBox='0 300 2000 150'>
<g>
<path
d='M455.556,849.519c10.487-10.606,18.315-22.243,23.484-35.499c11.767-30.177,10.624-59.483-6.55-87.546
c-6.421-10.492-15.644-18.342-24.709-26.363c-42.412-37.528-84.791-75.089-127.178-112.646c-0.23-0.204-0.403-0.473-0.833-0.988
c11.603-7.813,23.158-15.593,34.985-23.557c-6.017-4.258-11.845-8.382-17.938-12.692c7.366-9.069,14.573-17.943,21.752-26.783
c8.336,4.434,15.519,3.458,20.084-2.588c4.005-5.302,3.629-12.893-0.884-17.858c-4.39-4.829-12.207-5.82-17.747-2.248
@ -14,42 +15,58 @@ export default function RoboSatsTextIcon(props) {
c1.134-0.051,2.23-0.144,3.327-0.144c61.359-0.006,122.719-0.134,184.077,0.059c23.582,0.074,47.182,1.188,70.189,6.894
c33.068,8.2,59.95,25.613,78.508,54.766c8.642,13.576,12.927,28.656,14.487,44.482c2.252,22.815,0.823,45.408-5.545,67.547
c-7.479,25.995-23.225,46.343-43.608,63.466c-10.102,8.486-21.167,15.536-32.924,21.505c-0.814,0.413-1.585,0.905-2.597,1.487
c50.553,63.965,100.971,127.76,151.731,191.983C555.434,849.519,505.838,849.519,455.556,849.519z"/>
<path d="M255.521,635.166c16.096-0.067,29.067,12.759,29.148,28.827c0.083,16.276-12.776,29.339-28.953,29.405
c-16.022,0.067-29.385-13.258-29.278-29.196C226.544,648.168,239.5,635.234,255.521,635.166z"/>
<path d="M357.619,772.016c0.441-16.3,13.472-29.043,29.376-28.729c16.465,0.325,29.215,13.623,28.752,29.985
c-0.451,15.939-13.688,28.597-29.541,28.242C370.048,801.155,357.19,787.886,357.619,772.016z"/>
<path d="M188.111,736.337c14.63,12.955,28.898,25.589,43.417,38.445c-3.115,3.519-6.132,6.927-9.336,10.548
c50.553,63.965,100.971,127.76,151.731,191.983C555.434,849.519,505.838,849.519,455.556,849.519z'
/>
<path
d='M255.521,635.166c16.096-0.067,29.067,12.759,29.148,28.827c0.083,16.276-12.776,29.339-28.953,29.405
c-16.022,0.067-29.385-13.258-29.278-29.196C226.544,648.168,239.5,635.234,255.521,635.166z'
/>
<path
d='M357.619,772.016c0.441-16.3,13.472-29.043,29.376-28.729c16.465,0.325,29.215,13.623,28.752,29.985
c-0.451,15.939-13.688,28.597-29.541,28.242C370.048,801.155,357.19,787.886,357.619,772.016z'
/>
<path
d='M188.111,736.337c14.63,12.955,28.898,25.589,43.417,38.445c-3.115,3.519-6.132,6.927-9.336,10.548
c-3.528-3.123-6.993-6.098-10.344-9.194c-1.96-1.813-3.346-1.561-5.269,0.292c-6.227,5.999-12.67,11.772-18.468,17.114
C188.111,775.57,188.111,756.221,188.111,736.337z"/>
<path d="M315.788,849.533c-18.813,0-36.98,0-55.824,0c5.607-5.78,10.793-11.127,16.108-16.606
c-3.987-3.574-7.77-6.967-11.734-10.521c3.071-3.48,6.007-6.811,9.173-10.398C287.54,824.461,301.433,836.792,315.788,849.533z"/>
C188.111,775.57,188.111,756.221,188.111,736.337z'
/>
<path
d='M315.788,849.533c-18.813,0-36.98,0-55.824,0c5.607-5.78,10.793-11.127,16.108-16.606
c-3.987-3.574-7.77-6.967-11.734-10.521c3.071-3.48,6.007-6.811,9.173-10.398C287.54,824.461,301.433,836.792,315.788,849.533z'
/>
</g>
<g>
<path d="M766.812,758.155c0,18.361-3.246,34.457-9.734,48.289c-6.49,13.834-15.776,24.51-27.859,32.022
<path
d='M766.812,758.155c0,18.361-3.246,34.457-9.734,48.289c-6.49,13.834-15.776,24.51-27.859,32.022
c-12.085,7.516-25.938,11.273-41.564,11.273c-15.458,0-29.249-3.715-41.374-11.145c-12.127-7.429-21.519-18.039-28.181-31.831
c-6.66-13.789-10.035-29.653-10.118-47.584v-9.223c0-18.358,3.308-34.521,9.927-48.481c6.617-13.962,15.968-24.678,28.052-32.15
c12.083-7.471,25.894-11.207,41.437-11.207c15.541,0,29.352,3.735,41.437,11.207c12.083,7.473,21.433,18.188,28.052,32.15
c6.616,13.961,9.926,30.081,9.926,48.354V758.155L766.812,758.155z M727.873,749.701c0-19.553-3.503-34.411-10.504-44.574
c-7.003-10.163-16.993-15.243-29.972-15.243c-12.895,0-22.845,5.017-29.846,15.05c-7.003,10.036-10.546,24.744-10.631,44.127v9.093
c0,19.043,3.5,33.817,10.503,44.319c7,10.504,17.079,15.755,30.229,15.755c12.893,0,22.799-5.059,29.715-15.178
c6.917-10.119,10.418-24.868,10.504-44.255L727.873,749.701L727.873,749.701z"/>
<path d="M794.736,847.177V660.678h65.326c22.627,0,39.791,4.336,51.491,13.001c11.699,8.668,17.549,21.372,17.549,38.107
c6.917-10.119,10.418-24.868,10.504-44.255L727.873,749.701L727.873,749.701z'
/>
<path
d='M794.736,847.177V660.678h65.326c22.627,0,39.791,4.336,51.491,13.001c11.699,8.668,17.549,21.372,17.549,38.107
c0,9.138-2.35,17.187-7.045,24.146c-4.697,6.961-11.23,12.062-19.597,15.307c9.562,2.39,17.1,7.216,22.607,14.474
c5.508,7.259,8.263,16.138,8.263,26.642c0,17.934-5.723,31.51-17.165,40.733c-11.446,9.223-27.754,13.919-48.93,14.089
L794.736,847.177L794.736,847.177z M833.162,738.813h28.437c19.383-0.341,29.076-8.069,29.076-23.186
c0-8.453-2.455-14.538-7.364-18.252c-4.913-3.714-12.662-5.572-23.248-5.572h-26.9V738.813L833.162,738.813z M833.162,765.968
v50.341h32.919c9.051,0,16.118-2.155,21.198-6.469c5.08-4.313,7.621-10.269,7.621-17.868c0-17.078-8.838-25.746-26.514-26.003
L833.162,765.968L833.162,765.968z"/>
<path d="M1115.343,758.155c0,18.361-3.245,34.457-9.734,48.289c-6.492,13.834-15.776,24.51-27.858,32.022
L833.162,765.968L833.162,765.968z'
/>
<path
d='M1115.343,758.155c0,18.361-3.245,34.457-9.734,48.289c-6.492,13.834-15.776,24.51-27.858,32.022
c-12.085,7.516-25.94,11.273-41.567,11.273c-15.457,0-29.246-3.715-41.37-11.145c-12.127-7.429-21.521-18.039-28.182-31.831
c-6.66-13.789-10.035-29.653-10.119-47.584v-9.223c0-18.358,3.309-34.521,9.928-48.481c6.616-13.962,15.966-24.678,28.051-32.15
c12.081-7.471,25.894-11.207,41.436-11.207s29.354,3.735,41.439,11.207c12.079,7.473,21.432,18.188,28.05,32.15
c6.616,13.961,9.926,30.081,9.926,48.354v8.325H1115.343z M1076.405,749.701c0-19.553-3.504-34.411-10.505-44.574
s-16.992-15.243-29.973-15.243c-12.895,0-22.844,5.017-29.845,15.05c-7.004,10.036-10.548,24.744-10.632,44.127v9.093
c0,19.043,3.501,33.817,10.503,44.319c7.002,10.504,17.079,15.755,30.229,15.755c12.896,0,22.802-5.059,29.717-15.178
c6.918-10.119,10.419-24.868,10.505-44.255L1076.405,749.701L1076.405,749.701z"/>
<path d="M1239.975,798.248c0-7.258-2.563-12.829-7.686-16.717c-5.123-3.884-14.346-7.982-27.666-12.296
c6.918-10.119,10.419-24.868,10.505-44.255L1076.405,749.701L1076.405,749.701z'
/>
<path
d='M1239.975,798.248c0-7.258-2.563-12.829-7.686-16.717c-5.123-3.884-14.346-7.982-27.666-12.296
c-13.323-4.312-23.869-8.561-31.64-12.744c-21.178-11.443-31.766-26.855-31.766-46.241c0-10.075,2.838-19.064,8.517-26.963
c5.679-7.897,13.835-14.067,24.466-18.508c10.631-4.439,22.563-6.66,35.801-6.66c13.323,0,25.191,2.412,35.609,7.236
c10.417,4.826,18.509,11.636,24.272,20.43c5.765,8.796,8.647,18.787,8.647,29.973h-38.426c0-8.536-2.692-15.177-8.071-19.917
@ -57,11 +74,15 @@ export default function RoboSatsTextIcon(props) {
c0,6.063,3.053,11.143,9.16,15.241c6.104,4.099,15.091,7.941,26.962,11.528c21.859,6.577,37.786,14.73,47.776,24.465
c9.991,9.735,14.987,21.861,14.987,36.377c0,16.139-6.106,28.8-18.317,37.979c-12.212,9.181-28.649,13.771-49.313,13.771
c-14.347,0-27.411-2.626-39.195-7.878s-20.773-12.444-26.964-21.583c-6.192-9.136-9.286-19.725-9.286-31.767h38.556
c0,20.581,12.296,30.87,36.891,30.87c9.136,0,16.269-1.858,21.391-5.573C1237.414,810.097,1239.975,804.907,1239.975,798.248z"/>
<path d="M1409.822,808.75h-67.376l-12.809,38.427h-40.861l69.424-186.498h35.611l69.809,186.498h-40.861L1409.822,808.75z
M1352.822,777.625h46.624l-23.44-69.809L1352.822,777.625z"/>
<path d="M1606.055,691.805h-57.127v155.372h-38.429V691.805h-56.358v-31.126h151.914V691.805L1606.055,691.805z"/>
<path d="M1722.617,798.248c0-7.258-2.563-12.829-7.687-16.717c-5.123-3.884-14.346-7.982-27.666-12.296
c0,20.581,12.296,30.87,36.891,30.87c9.136,0,16.269-1.858,21.391-5.573C1237.414,810.097,1239.975,804.907,1239.975,798.248z'
/>
<path
d='M1409.822,808.75h-67.376l-12.809,38.427h-40.861l69.424-186.498h35.611l69.809,186.498h-40.861L1409.822,808.75z
M1352.822,777.625h46.624l-23.44-69.809L1352.822,777.625z'
/>
<path d='M1606.055,691.805h-57.127v155.372h-38.429V691.805h-56.358v-31.126h151.914V691.805L1606.055,691.805z' />
<path
d='M1722.617,798.248c0-7.258-2.563-12.829-7.687-16.717c-5.123-3.884-14.346-7.982-27.666-12.296
c-13.323-4.312-23.87-8.561-31.639-12.744c-21.179-11.443-31.767-26.855-31.767-46.241c0-10.075,2.837-19.064,8.517-26.963
c5.679-7.897,13.835-14.067,24.466-18.508c10.632-4.439,22.563-6.66,35.801-6.66c13.323,0,25.19,2.412,35.609,7.236
c10.417,4.826,18.509,11.636,24.273,20.43c5.764,8.796,8.646,18.787,8.646,29.973h-38.426c0-8.536-2.692-15.177-8.072-19.917
@ -69,8 +90,9 @@ export default function RoboSatsTextIcon(props) {
c0,6.063,3.053,11.143,9.16,15.241c6.104,4.099,15.091,7.941,26.962,11.528c21.858,6.577,37.786,14.73,47.776,24.465
c9.991,9.735,14.987,21.861,14.987,36.377c0,16.139-6.106,28.8-18.317,37.979c-12.212,9.181-28.648,13.771-49.313,13.771
c-14.346,0-27.411-2.626-39.195-7.878s-20.774-12.444-26.964-21.583c-6.192-9.136-9.286-19.725-9.286-31.767h38.556
c0,20.581,12.296,30.87,36.891,30.87c9.136,0,16.268-1.858,21.391-5.573C1720.055,810.097,1722.617,804.907,1722.617,798.248z"/>
c0,20.581,12.296,30.87,36.891,30.87c9.136,0,16.268-1.858,21.391-5.573C1720.055,810.097,1722.617,804.907,1722.617,798.248z'
/>
</g>
</SvgIcon>
);
}
</SvgIcon>
);
}

View File

@ -1,50 +1,112 @@
import React, { Component } from "react";
import { SvgIcon } from "@mui/material"
import React, { Component } from 'react';
import { SvgIcon } from '@mui/material';
export default function SellSatsIcon(props) {
return (
<SvgIcon sx={props.sx} color={props.color} x="0px" y="0px" viewBox="0 0 300 300">
<g>
<polygon fill={props.color} points="121.609,110.04 125.066,113.125 130.511,117.982 133.404,120.564 138.846,125.421 141.355,127.66
return (
<SvgIcon sx={props.sx} color={props.color} x='0px' y='0px' viewBox='0 0 300 300'>
<g>
<polygon
fill={props.color}
points='121.609,110.04 125.066,113.125 130.511,117.982 133.404,120.564 138.846,125.421 141.355,127.66
142.283,128.49 147.107,123.082 144.288,120.564 138.846,115.709 135.951,113.125 130.509,108.27 127.053,105.184
122.057,100.729 119.174,102.5 115.611,104.689 116.166,105.184 "/>
<polygon fill={props.color} points="133.966,105.184 139.412,110.04 142.305,112.623 147.744,117.479 149.48,119.025 150.408,119.855
122.057,100.729 119.174,102.5 115.611,104.689 116.166,105.184 '
/>
<polygon
fill={props.color}
points='133.966,105.184 139.412,110.04 142.305,112.623 147.744,117.479 149.48,119.025 150.408,119.855
155.234,114.449 153.188,112.623 147.744,107.768 144.854,105.186 139.41,100.328 135.951,97.244 132.6,94.252 126.15,98.213
130.509,102.1 "/>
<polygon fill={props.color} points="142.305,97.745 147.747,102.602 150.643,105.184 156.084,110.04 157.986,111.74 162.811,106.332
161.523,105.184 156.08,100.328 153.188,97.746 147.744,92.891 144.288,89.805 142.473,88.184 136.027,92.146 138.846,94.66 "/>
<rect x="119.966" y="117.668" transform="matrix(0.7405 0.672 -0.672 0.7405 114.3002 -51.0573)" fill={props.color} width="6.611" height="9.654"/>
<polygon fill={props.color} points="158.871,88.123 152.965,84.797 149.123,89.025 154.02,93.467 "/>
<polygon fill={props.color} points="113.039,242.962 110.145,240.379 104.702,235.523 101.244,232.438 95.801,227.582 94.551,226.466
130.509,102.1 '
/>
<polygon
fill={props.color}
points='142.305,97.745 147.747,102.602 150.643,105.184 156.084,110.04 157.986,111.74 162.811,106.332
161.523,105.184 156.08,100.328 153.188,97.746 147.744,92.891 144.288,89.805 142.473,88.184 136.027,92.146 138.846,94.66 '
/>
<rect
x='119.966'
y='117.668'
transform='matrix(0.7405 0.672 -0.672 0.7405 114.3002 -51.0573)'
fill={props.color}
width='6.611'
height='9.654'
/>
<polygon
fill={props.color}
points='158.871,88.123 152.965,84.797 149.123,89.025 154.02,93.467 '
/>
<polygon
fill={props.color}
points='113.039,242.962 110.145,240.379 104.702,235.523 101.244,232.438 95.801,227.582 94.551,226.466
89.729,231.874 90.357,232.438 95.801,237.295 99.258,240.379 104.702,245.236 107.598,247.818 113.037,252.674 115.547,254.914
116.475,255.744 121.305,250.335 118.48,247.818 "/>
<polygon fill={props.color} points="121.938,235.021 119.046,232.438 113.602,227.582 110.145,224.497 104.702,219.641
116.475,255.744 121.305,250.335 118.48,247.818 '
/>
<polygon
fill={props.color}
points='121.938,235.021 119.046,232.438 113.602,227.582 110.145,224.497 104.702,219.641
103.604,218.66 102.675,217.834 97.854,223.24 99.258,224.497 104.702,229.352 108.159,232.438 113.602,237.295 116.495,239.876
121.938,244.732 123.672,246.279 124.604,247.109 129.428,241.701 127.382,239.876 "/>
<polygon fill={props.color} points="130.277,227.582 127.382,225 121.938,220.145 118.48,217.059 113.038,212.202 111.182,210.544
121.938,244.732 123.672,246.279 124.604,247.109 129.428,241.701 127.382,239.876 '
/>
<polygon
fill={props.color}
points='130.277,227.582 127.382,225 121.938,220.145 118.48,217.059 113.038,212.202 111.182,210.544
110.253,209.718 105.43,215.125 107.598,217.059 113.038,221.915 116.495,225 121.938,229.855 124.833,232.438 130.277,237.295
132.18,238.994 137.002,233.585 135.718,232.438 "/>
<rect x="94.143" y="244.919" transform="matrix(0.7403 0.6723 -0.6723 0.7403 193.2158 -0.6485)" fill={props.color} width="6.609" height="9.657"/>
<polygon fill={props.color} points="134.702,213.575 129.807,209.133 123.316,216.279 128.211,220.721 "/>
<polygon fill={props.color} points="211.316,197.4 212.245,198.23 217.073,192.823 194.257,172.467 190.32,168.954 185.497,174.359
187.4,176.062 "/>
<polygon fill={props.color} points="219.443,188.768 220.369,189.598 225.197,184.189 205.331,166.463 199.373,161.146
198.447,160.32 193.622,165.729 198.58,170.152 "/>
<polygon fill={props.color} points="227.951,181.48 232.771,176.072 215.531,160.689 207.816,153.805 206.949,153.032
206.021,152.205 201.341,157.452 201.198,157.611 208.893,164.475 "/>
<rect x="189.921" y="187.4" transform="matrix(0.7405 0.6721 -0.6721 0.7405 179.3349 -79.9756)" fill={props.color} width="6.609" height="9.655"/>
<polygon fill={props.color} points="230.471,156.062 227.62,153.475 225.578,151.619 219.378,158.445 219.086,158.766 223.98,163.208
"/>
<path fill={props.color} d="M112.534,187.282c-7.511,0-14.995,1.878-21.647,5.43c-10.848,5.793-18.796,15.479-22.379,27.277
132.18,238.994 137.002,233.585 135.718,232.438 '
/>
<rect
x='94.143'
y='244.919'
transform='matrix(0.7403 0.6723 -0.6723 0.7403 193.2158 -0.6485)'
fill={props.color}
width='6.609'
height='9.657'
/>
<polygon
fill={props.color}
points='134.702,213.575 129.807,209.133 123.316,216.279 128.211,220.721 '
/>
<polygon
fill={props.color}
points='211.316,197.4 212.245,198.23 217.073,192.823 194.257,172.467 190.32,168.954 185.497,174.359
187.4,176.062 '
/>
<polygon
fill={props.color}
points='219.443,188.768 220.369,189.598 225.197,184.189 205.331,166.463 199.373,161.146
198.447,160.32 193.622,165.729 198.58,170.152 '
/>
<polygon
fill={props.color}
points='227.951,181.48 232.771,176.072 215.531,160.689 207.816,153.805 206.949,153.032
206.021,152.205 201.341,157.452 201.198,157.611 208.893,164.475 '
/>
<rect
x='189.921'
y='187.4'
transform='matrix(0.7405 0.6721 -0.6721 0.7405 179.3349 -79.9756)'
fill={props.color}
width='6.609'
height='9.655'
/>
<polygon
fill={props.color}
points='230.471,156.062 227.62,153.475 225.578,151.619 219.378,158.445 219.086,158.766 223.98,163.208
'
/>
<path
fill={props.color}
d='M112.534,187.282c-7.511,0-14.995,1.878-21.647,5.43c-10.848,5.793-18.796,15.479-22.379,27.277
c-3.583,11.796-2.365,24.266,3.426,35.112c8.033,15.037,23.646,24.379,40.742,24.379c7.51,0,14.996-1.877,21.648-5.431
c10.847-5.793,18.794-15.479,22.377-27.274c3.583-11.797,2.365-24.267-3.426-35.113
C145.245,196.624,129.635,187.282,112.534,187.282z M128.768,263.645c-16.688,8.912-37.514,2.588-46.43-14.099
c-8.908-16.688-2.584-37.517,14.104-46.428c16.688-8.913,37.519-2.586,46.432,14.1
C151.781,233.908,145.456,254.732,128.768,263.645z"/>
<path fill={props.color} d="M52.191,144.639c6.326,5.757,17.865,13.457,34.295,22.881c18.544,10.64,43.998,23.457,59.836,27.527
C151.781,233.908,145.456,254.732,128.768,263.645z'
/>
<path
fill={props.color}
d='M52.191,144.639c6.326,5.757,17.865,13.457,34.295,22.881c18.544,10.64,43.998,23.457,59.836,27.527
c0.785,0.199,1.621,0.301,2.484,0.301c1.916,0,5.619-0.547,15.812-4.947c0.823,2.479,1.856,4.886,3.086,7.188
c8.032,15.037,23.644,24.379,40.743,24.379c7.509,0,14.994-1.878,21.646-5.43c10.847-5.792,18.796-15.478,22.378-27.274
c3.583-11.798,2.365-24.268-3.428-35.113c-1.488-2.787-3.262-5.414-5.295-7.842c3.117-2.348,5.279-4.3,6.706-6.043
@ -61,12 +123,16 @@ export default function SellSatsIcon(props) {
c8.911,16.687,2.588,37.511-14.101,46.422c-16.688,8.914-37.517,2.588-46.431-14.101c-1.646-3.086-2.771-6.316-3.406-9.586
C174.003,178.857,173.9,175.221,174.344,171.672z M240.285,125.102l-34.512-20.586l34.512-60.934V125.102z M197.343,99.484
l-47.237-28.183l-46.689,28.232l-37.43-66.089c0.445-0.066,0.896-0.113,1.357-0.113h163.938c1.125,0,2.196,0.216,3.19,0.595
L197.343,99.484z"/>
<path fill={props.color} d="M30.079,300H269.92c16.587,0,30.08-13.494,30.08-30.08V30.08C300,13.494,286.507,0,269.92,0H30.079
L197.343,99.484z'
/>
<path
fill={props.color}
d='M30.079,300H269.92c16.587,0,30.08-13.494,30.08-30.08V30.08C300,13.494,286.507,0,269.92,0H30.079
C13.494,0,0,13.494,0,30.08v239.84C0,286.506,13.494,300,30.079,300z M15.587,30.08c0-7.992,6.501-14.494,14.492-14.494H269.92
c7.992,0,14.494,6.502,14.494,14.494v239.84c0,7.992-6.502,14.494-14.494,14.494H30.079c-7.991,0-14.492-6.502-14.492-14.494
V30.08z"/>
</g>
</SvgIcon>
);
}
V30.08z'
/>
</g>
</SvgIcon>
);
}

View File

@ -1,13 +1,18 @@
import React, { Component } from "react";
import { SvgIcon } from "@mui/material"
import React, { Component } from 'react';
import { SvgIcon } from '@mui/material';
export default function SellSatsCheckedIcon(props) {
return (
<SvgIcon sx={props.sx} color={props.color} x="0px" y="0px" viewBox="0 0 300 300">
<SvgIcon sx={props.sx} color={props.color} x='0px' y='0px' viewBox='0 0 300 300'>
<g>
<polygon fill={props.color} points="240.285,125.102 240.285,43.582 205.773,104.516 "/>
<path fill={props.color} d="M58.583,40.294c-0.151,0.657-0.24,1.34-0.24,2.042v84.453l36.673-22.176L58.583,40.294z"/>
<path fill={props.color} d="M112.81,199.078c-5.534-0.042-11.153,1.255-16.368,4.04c-16.688,8.911-23.012,29.739-14.104,46.428
<polygon fill={props.color} points='240.285,125.102 240.285,43.582 205.773,104.516 ' />
<path
fill={props.color}
d='M58.583,40.294c-0.151,0.657-0.24,1.34-0.24,2.042v84.453l36.673-22.176L58.583,40.294z'
/>
<path
fill={props.color}
d='M112.81,199.078c-5.534-0.042-11.153,1.255-16.368,4.04c-16.688,8.911-23.012,29.739-14.104,46.428
c6.13,11.472,17.889,18.046,30.062,18.139c5.533,0.042,11.152-1.255,16.368-4.04c0.521-0.278,1.033-0.568,1.534-0.87
c1.002-0.603,1.963-1.249,2.882-1.938c1.46-1.093,2.813-2.289,4.056-3.571c5.381-5.558,8.668-12.749,9.475-20.292
c0.706-6.601-0.487-13.471-3.841-19.756C136.746,205.746,124.985,199.171,112.81,199.078z M129.807,209.133l4.895,4.442
@ -18,10 +23,16 @@ export default function SellSatsCheckedIcon(props) {
l-1.404-1.257l4.821-5.406l0.001,0.001l0.928,0.825l1.098,0.98l5.443,4.856l3.457,3.085l5.444,4.855l2.892,2.584l5.444,4.854
l2.046,1.825L124.604,247.109z M132.18,238.994l-1.903-1.699l-5.444-4.857l-2.895-2.582L116.495,225l-3.457-3.085l-5.44-4.856
l-2.168-1.934l4.823-5.407l0.929,0.826l1.856,1.658l5.442,4.856l3.458,3.086l4.854,4.33l0.589,0.525l2.895,2.582l5.441,4.855
l1.283,1.146l0.001,0.001L132.18,238.994z"/>
<path fill={props.color} d="M103.417,99.533l46.689-28.232l47.237,28.183l10.278-18.148l26.851-47.409l0.001-0.001
c-0.994-0.379-2.065-0.595-3.19-0.595H67.344c-0.461,0-0.912,0.047-1.357,0.113l0.002,0.003L103.417,99.533z"/>
<path fill={props.color} d="M188.072,137.845c2.039-1.089,4.131-1.976,6.249-2.708c12.551-4.337,26.17-2.534,37.015,4.271l0.001,0
l1.283,1.146l0.001,0.001L132.18,238.994z'
/>
<path
fill={props.color}
d='M103.417,99.533l46.689-28.232l47.237,28.183l10.278-18.148l26.851-47.409l0.001-0.001
c-0.994-0.379-2.065-0.595-3.19-0.595H67.344c-0.461,0-0.912,0.047-1.357,0.113l0.002,0.003L103.417,99.533z'
/>
<path
fill={props.color}
d='M188.072,137.845c2.039-1.089,4.131-1.976,6.249-2.708c12.551-4.337,26.17-2.534,37.015,4.271l0.001,0
c2.595-1.757,4.685-3.269,6.238-4.499l-56.337-33.61c0,0.001,0,0.003,0,0.004c1.895,17.026-6.509,34.3-22.518,42.847
c-6.553,3.5-13.612,5.129-20.564,5.076c-15.294-0.117-30.068-8.378-37.768-22.792c-1.731-3.242-3.001-6.613-3.84-10.029
l7.985-4.906l0.001,0c0.578,3.681,1.761,7.325,3.615,10.793c4.733,8.866,12.831,14.807,21.884,17.093
@ -35,8 +46,11 @@ export default function SellSatsCheckedIcon(props) {
M132.6,94.252l3.351,2.992l3.459,3.084l5.444,4.858l2.89,2.582l5.443,4.855l2.046,1.825l0.001,0.001l-4.826,5.406l-0.928-0.83
l-1.736-1.546l-5.439-4.856l-2.893-2.583l-5.446-4.856l-3.457-3.084l-4.359-3.887L132.6,94.252z M119.174,102.5l2.883-1.771
l4.996,4.455l3.456,3.086l5.442,4.855l2.895,2.584l5.442,4.855l2.819,2.518l-4.824,5.408l-0.928-0.83l-2.509-2.239l-5.442-4.857
l-2.893-2.582l-5.445-4.857l-3.457-3.085l-5.443-4.856l-0.553-0.493l-0.002-0.002L119.174,102.5z"/>
<path fill={props.color} d="M231.82,150.836c-2.649-2.482-5.65-4.502-8.875-6.015c-9.465-4.428-20.827-4.506-30.732,0.785
l-2.893-2.582l-5.445-4.857l-3.457-3.085l-5.443-4.856l-0.553-0.493l-0.002-0.002L119.174,102.5z'
/>
<path
fill={props.color}
d='M231.82,150.836c-2.649-2.482-5.65-4.502-8.875-6.015c-9.465-4.428-20.827-4.506-30.732,0.785
c-10.213,5.453-16.535,15.369-17.869,26.066c-0.119,0.951-0.191,1.909-0.23,2.87c-0.108,2.625,0.076,5.276,0.587,7.903
c0.635,3.271,1.76,6.5,3.405,9.587c8.914,16.688,29.744,23.015,46.432,14.101c10.432-5.569,16.812-15.792,17.956-26.751
c0.686-6.574-0.514-13.413-3.854-19.671C236.818,156.305,234.497,153.338,231.82,150.836z M219.378,158.445l6.2-6.826l2.042,1.855
@ -44,8 +58,11 @@ export default function SellSatsCheckedIcon(props) {
l1.473-1.623l4.895,4.442L197.446,192.499z M212.245,198.23l-0.929-0.83L187.4,176.062l-1.903-1.703l4.823-5.405l3.937,3.513
l22.816,20.356L212.245,198.23z M220.369,189.598l-0.926-0.83l-20.863-18.615l-4.958-4.424l4.825-5.408l0.926,0.826l5.958,5.316
l19.866,17.727L220.369,189.598z M227.951,181.48l-19.059-17.006l-7.694-6.863l0.143-0.159l4.681-5.247l0.928,0.827l0.867,0.772
l7.715,6.885l17.24,15.383L227.951,181.48z"/>
<path fill={props.color} d="M269.92,0H30.079C13.494,0,0,13.494,0,30.08v239.84C0,286.506,13.494,300,30.079,300H269.92
l7.715,6.885l17.24,15.383L227.951,181.48z'
/>
<path
fill={props.color}
d='M269.92,0H30.079C13.494,0,0,13.494,0,30.08v239.84C0,286.506,13.494,300,30.079,300H269.92
c16.587,0,30.08-13.494,30.08-30.08V30.08C300,13.494,286.507,0,269.92,0z M157.316,244.557c-0.187,0.741-0.392,1.481-0.615,2.219
c-3.583,11.795-11.53,21.48-22.377,27.273c-6.652,3.555-14.138,5.432-21.648,5.432c-17.096,0-32.709-9.342-40.742-24.379
c-3.62-6.779-5.453-14.192-5.444-21.679c0.004-2.995,0.302-6.001,0.898-8.983c0.298-1.491,0.671-2.977,1.119-4.451
@ -57,8 +74,9 @@ export default function SellSatsCheckedIcon(props) {
c-16.43-9.424-27.969-17.124-34.295-22.88c-1.155-1.052-2.111-2.014-2.902-2.899c-3.426-3.833-3.765-6.229-3.761-8.206V42.337
c0-12.027,9.789-21.814,21.817-21.814l163.939-0.005c5.638,0,10.784,2.151,14.66,5.675c4.393,3.994,7.155,9.752,7.155,16.143
v91.201c0,2.378-0.841,4.516-2.646,6.729c-1.427,1.743-3.589,3.695-6.706,6.043c2.033,2.428,3.807,5.055,5.295,7.841
C254.836,164.996,256.054,177.466,252.471,189.264z"/>
C254.836,164.996,256.054,177.466,252.471,189.264z'
/>
</g>
</SvgIcon>
);
}
}

View File

@ -1,11 +1,13 @@
import React, { Component } from "react";
import { SvgIcon } from "@mui/material"
import React, { Component } from 'react';
import { SvgIcon } from '@mui/material';
export default function SendReceiveIcon(props) {
return (
<SvgIcon sx={props.sx} color={props.color} x="0px" y="0px" viewBox="0 0 300 300">
<g>
<path fill={props.color} d="M150.021,2.579C68.341,2.579,2.127,68.793,2.127,150.472c0,81.682,66.214,147.895,147.895,147.895
return (
<SvgIcon sx={props.sx} color={props.color} x='0px' y='0px' viewBox='0 0 300 300'>
<g>
<path
fill={props.color}
d='M150.021,2.579C68.341,2.579,2.127,68.793,2.127,150.472c0,81.682,66.214,147.895,147.895,147.895
c81.676,0,147.89-66.213,147.89-147.895C297.911,68.793,231.697,2.579,150.021,2.579z M99.195,73.557
c0,0,60.301,60.307,60.306,60.309c4.899,4.895,11.188,13.849,8.663,21.061c-1.835,5.236-6.053,9.546-9.893,13.383
c-0.091,0.093-59.076,59.078-59.076,59.078c-6.26,6.26-16.406,6.26-22.665,0c-6.261-6.256-6.261-16.406,0-22.665l54.247-54.25
@ -13,8 +15,9 @@ export default function SendReceiveIcon(props) {
z M146.365,73.557c3.125-3.128,7.23-4.693,11.33-4.693c4.101,0,8.204,1.565,11.335,4.693c0,0,60.303,60.307,60.308,60.309
c4.895,4.894,11.188,13.849,8.659,21.061c-1.832,5.236-6.05,9.546-9.889,13.383c-0.092,0.094-59.078,59.078-59.078,59.078
c-6.256,6.26-16.405,6.26-22.665,0c-6.258-6.256-6.258-16.406,0-22.665l54.246-54.25l-54.246-54.25
C140.107,89.966,140.107,79.818,146.365,73.557z"/>
</g>
</SvgIcon>
);
}
C140.107,89.966,140.107,79.818,146.365,73.557z'
/>
</g>
</SvgIcon>
);
}

View File

@ -1,11 +1,10 @@
import React, { Component } from "react";
import { SvgIcon } from "@mui/material"
import React, { Component } from 'react';
import { SvgIcon } from '@mui/material';
export default function UserNinjaIcon(props) {
return (
<SvgIcon {...props} x="0px" y="0px" viewBox="0 0 512 512">
<path d="M64 192c27.25 0 51.75-11.5 69.25-29.75c15 54 64 93.75 122.8 93.75c70.75 0 127.1-57.25 127.1-128s-57.25-128-127.1-128c-50.38 0-93.63 29.38-114.5 71.75C124.1 47.75 96 32 64 32c0 33.37 17.12 62.75 43.13 80C81.13 129.3 64 158.6 64 192zM208 96h95.1C321.7 96 336 110.3 336 128h-160C176 110.3 190.3 96 208 96zM337.8 306.9L256 416L174.2 306.9C93.36 321.6 32 392.2 32 477.3c0 19.14 15.52 34.67 34.66 34.67H445.3c19.14 0 34.66-15.52 34.66-34.67C480 392.2 418.6 321.6 337.8 306.9z"/>
</SvgIcon>
);
}
return (
<SvgIcon {...props} x='0px' y='0px' viewBox='0 0 512 512'>
<path d='M64 192c27.25 0 51.75-11.5 69.25-29.75c15 54 64 93.75 122.8 93.75c70.75 0 127.1-57.25 127.1-128s-57.25-128-127.1-128c-50.38 0-93.63 29.38-114.5 71.75C124.1 47.75 96 32 64 32c0 33.37 17.12 62.75 43.13 80C81.13 129.3 64 158.6 64 192zM208 96h95.1C321.7 96 336 110.3 336 128h-160C176 110.3 190.3 96 208 96zM337.8 306.9L256 416L174.2 306.9C93.36 321.6 32 392.2 32 477.3c0 19.14 15.52 34.67 34.66 34.67H445.3c19.14 0 34.66-15.52 34.66-34.67C480 392.2 418.6 321.6 337.8 306.9z' />
</SvgIcon>
);
}

View File

@ -1,20 +1,20 @@
export { default as AmbossIcon } from "./Amboss";
export { default as BitcoinIcon } from "./Bitcoin";
export { default as BitcoinSignIcon } from "./BitcoinSign";
export { default as BuySatsIcon } from "./BuySats";
export { default as BuySatsCheckedIcon } from "./BuySatsChecked";
export { default as EarthIcon } from "./Earth";
export { default as GoldIcon } from "./Gold";
export { default as NewTabIcon } from "./NewTab";
export { default as RoboSatsIcon } from "./RoboSats";
export { default as RoboSatsNoTextIcon } from "./RoboSatsNoText";
export { default as RoboSatsTextIcon } from "./RoboSatsText";
export { default as SellSatsCheckedIcon } from "./SellSatsChecked";
export { default as SellSatsIcon } from "./SellSats";
export { default as SendReceiveIcon } from "./SendReceive";
export { default as ExportIcon } from "./Export";
export { default as UserNinjaIcon } from "./UserNinja";
export { default as AmbossIcon } from './Amboss';
export { default as BitcoinIcon } from './Bitcoin';
export { default as BitcoinSignIcon } from './BitcoinSign';
export { default as BuySatsIcon } from './BuySats';
export { default as BuySatsCheckedIcon } from './BuySatsChecked';
export { default as EarthIcon } from './Earth';
export { default as GoldIcon } from './Gold';
export { default as NewTabIcon } from './NewTab';
export { default as RoboSatsIcon } from './RoboSats';
export { default as RoboSatsNoTextIcon } from './RoboSatsNoText';
export { default as RoboSatsTextIcon } from './RoboSatsText';
export { default as SellSatsCheckedIcon } from './SellSatsChecked';
export { default as SellSatsIcon } from './SellSats';
export { default as SendReceiveIcon } from './SendReceive';
export { default as ExportIcon } from './Export';
export { default as UserNinjaIcon } from './UserNinja';
// Some Flags missing on react-flags
export { default as BasqueCountryFlag } from "./BasqueCountryFlag";
export { default as CataloniaFlag } from "./CataloniaFlag";
export { default as BasqueCountryFlag } from './BasqueCountryFlag';
export { default as CataloniaFlag } from './CataloniaFlag';

View File

@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react";
import { Box, LinearProgress } from "@mui/material"
import React, { useState, useEffect } from 'react';
import { Box, LinearProgress } from '@mui/material';
import { calcTimeDelta } from 'react-countdown';
type Props = {
@ -7,10 +7,7 @@ type Props = {
totalSecsExp: number;
};
const LinearDeterminate = ({
expiresAt,
totalSecsExp,
}: Props): JSX.Element => {
const LinearDeterminate = ({ expiresAt, totalSecsExp }: Props): JSX.Element => {
const [progress, setProgress] = useState<number>(0);
useEffect(() => {
@ -28,7 +25,7 @@ const LinearDeterminate = ({
return (
<Box sx={{ width: '100%' }}>
<LinearProgress variant="determinate" value={progress} />
<LinearProgress variant='determinate' value={progress} />
</Box>
);
};

View File

@ -1 +1 @@
export { default } from "./LinearDeterminate";
export { default } from './LinearDeterminate';

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,60 +1,107 @@
import React, { Component } from 'react'
import { withTranslation } from "react-i18next";
import PaymentIcon from './payment-methods/Icons'
import {Tooltip} from "@mui/material"
import { paymentMethods, swapDestinations } from "./payment-methods/Methods";
import React, { Component } from 'react';
import { withTranslation } from 'react-i18next';
import PaymentIcon from './payment-methods/Icons';
import { Tooltip } from '@mui/material';
import { paymentMethods, swapDestinations } from './payment-methods/Methods';
const ns = [{name: "not specified",icon:'notspecified'}];
const ns = [{ name: 'not specified', icon: 'notspecified' }];
const methods = ns.concat(swapDestinations).concat(paymentMethods);
class PaymentText extends Component {
constructor(props) {
super(props);
constructor(props) {
super(props);
}
parseText() {
const { t } = this.props;
var rows = [];
var custom_methods = this.props.text;
// Adds icons for each PaymentMethod that matches
methods.forEach((method, i) => {
if (this.props.text.includes(method.name)) {
custom_methods = custom_methods.replace(method.name, '');
rows.push(
<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,
}}
>
<PaymentIcon width={this.props.size} height={this.props.size} icon={method.icon} />
</div>
</Tooltip>,
);
}
});
// Adds a Custom icon if there are words that do not match
var chars_left = custom_methods
.replace(' ', '')
.replace(' ', '')
.replace(' ', '')
.replace(' ', '')
.replace(' ', '');
if (chars_left.length > 0) {
rows.push(
<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',
}}
>
<PaymentIcon
width={this.props.size * 1.1}
height={this.props.size * 1.1}
icon={'custom'}
/>
</div>
</Tooltip>,
);
}
parseText(){
const { t } = this.props;
var rows = [];
var custom_methods = this.props.text;
// Adds icons for each PaymentMethod that matches
methods.forEach((method, i) =>{
if(this.props.text.includes(method.name)){
custom_methods = custom_methods.replace(method.name,'')
rows.push(
<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}}>
<PaymentIcon width={this.props.size} height={this.props.size} icon={method.icon}/>
</div>
</Tooltip>
);
}
})
// Adds a Custom icon if there are words that do not match
var chars_left = custom_methods.replace(' ','').replace(' ','').replace(' ','').replace(' ','').replace(' ','')
if(chars_left.length > 0){rows.push(
<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'}}>
<PaymentIcon width={this.props.size*1.1} height={this.props.size*1.1} icon={"custom"}/>
</div>
</Tooltip>
)}
if(this.props.verbose){
return (<>{rows} <div style={{display: 'inline-block'}}> <span>{custom_methods}</span></div></>)
}else{
return rows
}
}
render() {
if (this.props.verbose) {
return (
<div style={{display:'flex',alignItems:'center', flexWrap:'wrap'}}>
{this.parseText()}
</div>
)
<>
{rows}{' '}
<div style={{ display: 'inline-block' }}>
{' '}
<span>{custom_methods}</span>
</div>
</>
);
} else {
return rows;
}
}
render() {
return (
<div style={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap' }}>
{this.parseText()}
</div>
);
}
}
export default withTranslation()(PaymentText);

View File

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

View File

@ -1,61 +1,74 @@
import React from "react"
import { Badge, Tooltip } from "@mui/material";
import SmoothImage from 'react-smooth-image'
import React from 'react';
import { Badge, Tooltip } from '@mui/material';
import SmoothImage from 'react-smooth-image';
import Order from "../../../models/Order.model"
import { useTranslation } from "react-i18next";
import { SendReceiveIcon } from "../../Icons";
import Order from '../../../models/Order.model';
import { useTranslation } from 'react-i18next';
import { SendReceiveIcon } from '../../Icons';
interface DepthChartProps {
order: Order
order: Order;
}
const RobotAvatar: React.FC<DepthChartProps> = ({ order }) => {
const { t } = useTranslation()
const { t } = useTranslation();
const avatarSrc: string = window.location.origin +'/static/assets/avatars/' + order?.maker_nick + '.png'
const avatarSrc: string =
window.location.origin + '/static/assets/avatars/' + order?.maker_nick + '.png';
const statusBadge = (
<div style={{position:"relative", left:"6px", top:"1px"}}>
{order?.type === 0 ?
<SendReceiveIcon sx={{transform: "scaleX(-1)",height:"18px",width:"18px"}} color="secondary"/> :
<SendReceiveIcon sx={{height:"20px",width:"20px"}} color="primary"/>}
<div style={{ position: 'relative', left: '6px', top: '1px' }}>
{order?.type === 0 ? (
<SendReceiveIcon
sx={{ transform: 'scaleX(-1)', height: '18px', width: '18px' }}
color='secondary'
/>
) : (
<SendReceiveIcon sx={{ height: '20px', width: '20px' }} color='primary' />
)}
</div>
)
);
const statusBadgeColor = () => {
if(!order){ return }
if(order.maker_status ==='Active'){ return("success") }
if(order.maker_status ==='Seen recently'){ return("warning") }
if(order.maker_status ==='Inactive'){ return('error') }
}
if (!order) {
return;
}
if (order.maker_status === 'Active') {
return 'success';
}
if (order.maker_status === 'Seen recently') {
return 'warning';
}
if (order.maker_status === 'Inactive') {
return 'error';
}
};
return order ? (
<Tooltip placement="right" enterTouchDelay={0} title={t(order.maker_status) || ""}>
<Badge
variant="dot"
overlap="circular"
badgeContent=""
color={statusBadgeColor()}
>
<Badge
overlap="circular"
anchorOrigin={{horizontal: 'right', vertical: 'bottom'}}
<Tooltip placement='right' enterTouchDelay={0} title={t(order.maker_status) || ''}>
<Badge variant='dot' overlap='circular' badgeContent='' color={statusBadgeColor()}>
<Badge
overlap='circular'
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
badgeContent={statusBadge}
>
<div style={{ width: 45, height: 45 }}>
<SmoothImage
src={avatarSrc}
imageStyles={{borderRadius: "50%",
transform: "scaleX(-1)",
border: "0.3px solid #555",
filter: "dropShadow(0.5px 0.5px 0.5px #000000)"}}
imageStyles={{
borderRadius: '50%',
transform: 'scaleX(-1)',
border: '0.3px solid #555',
filter: 'dropShadow(0.5px 0.5px 0.5px #000000)',
}}
/>
</div>
</Badge>
</Badge>
</Tooltip>
) : <></>
}
) : (
<></>
);
};
export default RobotAvatar
export default RobotAvatar;

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +1,28 @@
import React, { useState } from "react";
import { format } from "date-fns";
import { useTranslation } from "react-i18next";
import React, { useState } from 'react';
import { format } from 'date-fns';
import { useTranslation } from 'react-i18next';
import {
Avatar,
Badge,
ToggleButton,
ToggleButtonGroup,
List,
ListItem,
ListItemText,
ListItemIcon,
Grid,
Tooltip,
IconButton,
Accordion,
AccordionSummary,
AccordionDetails,
Typography,
} from "@mui/material"
import { pn } from "../utils/prettyNumbers";
import { saveAsJson } from "../utils/saveFile";
Avatar,
Badge,
ToggleButton,
ToggleButtonGroup,
List,
ListItem,
ListItemText,
ListItemIcon,
Grid,
Tooltip,
IconButton,
Accordion,
AccordionSummary,
AccordionDetails,
Typography,
} from '@mui/material';
import { pn } from '../utils/prettyNumbers';
import { saveAsJson } from '../utils/saveFile';
// Icons
import FlagWithProps from "./FlagWithProps";
import FlagWithProps from './FlagWithProps';
import ScheduleIcon from '@mui/icons-material/Schedule';
import PriceChangeIcon from '@mui/icons-material/PriceChange';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
@ -32,7 +32,7 @@ import RouteIcon from '@mui/icons-material/Route';
import AccountBoxIcon from '@mui/icons-material/AccountBox';
import LockOpenIcon from '@mui/icons-material/LockOpen';
import LinkIcon from '@mui/icons-material/Link';
import { RoboSatsNoTextIcon , SendReceiveIcon , BitcoinIcon} from "./Icons";
import { RoboSatsNoTextIcon, SendReceiveIcon, BitcoinIcon } from './Icons';
interface Item {
id: string;
@ -48,7 +48,7 @@ type Props = {
takerSummary: Record<string, Item>;
platformSummary: Record<string, Item>;
orderId: number;
}
};
const TradeSummary = ({
isMaker,
@ -60,186 +60,261 @@ const TradeSummary = ({
platformSummary,
orderId,
}: Props): JSX.Element => {
const { t , i18n } = useTranslation();
const { t, i18n } = useTranslation();
const [buttonValue, setButtonValue] = useState<number>(isMaker ? 0 : 2);
var userSummary = buttonValue == 0 ? makerSummary : takerSummary;
const contractTimestamp = new Date(platformSummary.contract_timestamp)
const total_time = platformSummary.contract_total_time
const hours = parseInt(total_time / 3600)
const mins = parseInt((total_time - hours * 3600) / 60)
const secs = parseInt(total_time - hours * 3600 - mins * 60)
const contractTimestamp = new Date(platformSummary.contract_timestamp);
const total_time = platformSummary.contract_total_time;
const hours = parseInt(total_time / 3600);
const mins = parseInt((total_time - hours * 3600) / 60);
const secs = parseInt(total_time - hours * 3600 - mins * 60);
return (
<Grid item xs={12} align="center">
<Accordion defaultExpanded={true} elevation={0} sx={{width:322, position:'relative', right:8}}>
<AccordionSummary expandIcon={<ExpandMoreIcon sx={{width:28}} color="primary"/>}>
<Typography sx={{flexGrow:1}} color="text.secondary">{t("Trade Summary")}</Typography>
<Grid item xs={12} align='center'>
<Accordion
defaultExpanded={true}
elevation={0}
sx={{ width: 322, position: 'relative', right: 8 }}
>
<AccordionSummary expandIcon={<ExpandMoreIcon sx={{ width: 28 }} color='primary' />}>
<Typography sx={{ flexGrow: 1 }} color='text.secondary'>
{t('Trade Summary')}
</Typography>
</AccordionSummary>
<AccordionDetails>
<div style={{position:'relative',left:14, display:'flex',alignItems:'center', justifyContent:'center', flexWrap:'wrap'}}>
<ToggleButtonGroup
size="small"
value={buttonValue}
exclusive>
<ToggleButton value={0} disableRipple={true} onClick={() => setButtonValue(0)}>
<Avatar
className="flippedSmallAvatar"
sx={{height:24,width:24}}
alt={makerNick}
src={window.location.origin +'/static/assets/avatars/' + makerNick + '.png'}
/>
&nbsp;
{t("Maker")}
</ToggleButton>
<ToggleButton value={1} disableRipple={true} onClick={() => setButtonValue(1)}>
<RoboSatsNoTextIcon/>
</ToggleButton>
<ToggleButton value={2} disableRipple={true} onClick={() => setButtonValue(2)}>
{t("Taker")}
&nbsp;
<Avatar
className="smallAvatar"
sx={{height:28,width:28}}
alt={takerNick}
src={window.location.origin +'/static/assets/avatars/' + takerNick + '.png'}
/>
</ToggleButton>
<div
style={{
position: 'relative',
left: 14,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexWrap: 'wrap',
}}
>
<ToggleButtonGroup size='small' value={buttonValue} exclusive>
<ToggleButton value={0} disableRipple={true} onClick={() => setButtonValue(0)}>
<Avatar
className='flippedSmallAvatar'
sx={{ height: 24, width: 24 }}
alt={makerNick}
src={window.location.origin + '/static/assets/avatars/' + makerNick + '.png'}
/>
&nbsp;
{t('Maker')}
</ToggleButton>
<ToggleButton value={1} disableRipple={true} onClick={() => setButtonValue(1)}>
<RoboSatsNoTextIcon />
</ToggleButton>
<ToggleButton value={2} disableRipple={true} onClick={() => setButtonValue(2)}>
{t('Taker')}
&nbsp;
<Avatar
className='smallAvatar'
sx={{ height: 28, width: 28 }}
alt={takerNick}
src={window.location.origin + '/static/assets/avatars/' + takerNick + '.png'}
/>
</ToggleButton>
</ToggleButtonGroup>
<Tooltip enterTouchDelay={250} title={t("Save trade summary as file")}>
<span>
<IconButton
color="primary"
onClick={()=> saveAsJson(`order${orderId}-summary.json`, {'order_id':orderId,'currency':currencyCode,'maker':makerSummary,'taker':takerSummary,'platform':platformSummary})}
>
<DownloadIcon sx={{width:26, height:26}}/>
</IconButton>
</span>
</Tooltip>
</div>
{/* Maker/Taker Summary */}
<div style={{display: [0,2].includes(buttonValue) ? '':'none'}}>
<List dense={true}>
<ListItem>
<ListItemIcon>
<Badge
overlap="circular"
anchorOrigin={{horizontal: 'right', vertical: 'bottom'}}
badgeContent={<div
style={{position:"relative", left:"3px", top:"2px"}}>
{userSummary.is_buyer ?
<SendReceiveIcon
sx={{transform: "scaleX(-1)",height:"18px",width:"18px"}}
color="secondary"/>
: <SendReceiveIcon
sx={{height:"18px",width:"18px"}}
color="primary"/>
}
</div>}>
<AccountBoxIcon sx={{position:'relative',left:-2,width:28,height:28}}/>
</Badge>
</ListItemIcon>
<ListItemText
primary={userSummary.is_buyer ? t("Buyer") : t("Seller")}
secondary={t("User role")}/>
<Tooltip enterTouchDelay={250} title={t('Save trade summary as file')}>
<span>
<IconButton
color='primary'
onClick={() =>
saveAsJson(`order${orderId}-summary.json`, {
order_id: orderId,
currency: currencyCode,
maker: makerSummary,
taker: takerSummary,
platform: platformSummary,
})
}
>
<DownloadIcon sx={{ width: 26, height: 26 }} />
</IconButton>
</span>
</Tooltip>
</div>
{/* Maker/Taker Summary */}
<div style={{ display: [0, 2].includes(buttonValue) ? '' : 'none' }}>
<List dense={true}>
<ListItem>
<ListItemIcon>
<Badge
overlap='circular'
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
badgeContent={
<div style={{ position: 'relative', left: '3px', top: '2px' }}>
{userSummary.is_buyer ? (
<SendReceiveIcon
sx={{ transform: 'scaleX(-1)', height: '18px', width: '18px' }}
color='secondary'
/>
) : (
<SendReceiveIcon sx={{ height: '18px', width: '18px' }} color='primary' />
)}
</div>
}
>
<AccountBoxIcon
sx={{ position: 'relative', left: -2, width: 28, height: 28 }}
/>
</Badge>
</ListItemIcon>
<ListItemText
primary={userSummary.is_buyer ? t('Buyer') : t('Seller')}
secondary={t('User role')}
/>
<ListItemIcon>
<div style={{position:'relative',left:15,zoom:1.25,opacity: 0.7,msZoom:1.25,WebkitZoom:1.25,MozTransform:'scale(1.25,1.25)',MozTransformOrigin:'left center'}}>
<FlagWithProps code={currencyCode}/>
</div>
</ListItemIcon>
<ListItemText
primary={(userSummary.is_buyer ? pn(userSummary.sent_fiat) : pn(userSummary.received_fiat))+" "+currencyCode}
secondary={userSummary.is_buyer ? t("Sent") : t("Received")}/>
</ListItem>
<ListItem>
<ListItemIcon>
<BitcoinIcon/>
</ListItemIcon>
<ListItemText
primary={pn(userSummary.is_buyer ? userSummary.received_sats : userSummary.sent_sats)+" Sats"}
secondary={userSummary.is_buyer ? "BTC received" : "BTC sent"}/>
<ListItemIcon>
<div
style={{
position: 'relative',
left: 15,
zoom: 1.25,
opacity: 0.7,
msZoom: 1.25,
WebkitZoom: 1.25,
MozTransform: 'scale(1.25,1.25)',
MozTransformOrigin: 'left center',
}}
>
<FlagWithProps code={currencyCode} />
</div>
</ListItemIcon>
<ListItemText
primary={
(userSummary.is_buyer
? pn(userSummary.sent_fiat)
: pn(userSummary.received_fiat)) +
' ' +
currencyCode
}
secondary={userSummary.is_buyer ? t('Sent') : t('Received')}
/>
</ListItem>
<ListItemText
primary={t("{{tradeFeeSats}} Sats ({{tradeFeePercent}}%)",{tradeFeeSats:pn(userSummary.trade_fee_sats),tradeFeePercent:parseFloat((userSummary.trade_fee_percent*100).toPrecision(3))})}
secondary={"Trade fee"}/>
</ListItem>
{userSummary.is_swap ?
<ListItem>
<ListItemIcon>
<LinkIcon/>
</ListItemIcon>
<ListItemText
primary={t("{{swapFeeSats}} Sats ({{swapFeePercent}}%)" , {swapFeeSats:pn(userSummary.swap_fee_sats), swapFeePercent:userSummary.swap_fee_percent})}
secondary={t("Onchain swap fee")}/>
<ListItemText
primary={t("{{miningFeeSats}} Sats",{miningFeeSats:pn(userSummary.mining_fee_sats)})}
secondary={t("Mining fee")}/>
</ListItem>
: null}
<ListItem>
<ListItemIcon>
<BitcoinIcon />
</ListItemIcon>
<ListItemText
primary={
pn(userSummary.is_buyer ? userSummary.received_sats : userSummary.sent_sats) +
' Sats'
}
secondary={userSummary.is_buyer ? 'BTC received' : 'BTC sent'}
/>
<ListItem>
<ListItemIcon>
<LockOpenIcon color="success"/>
</ListItemIcon>
<ListItemText
primary={t("{{bondSats}} Sats ({{bondPercent}}%)" , {bondSats:pn(userSummary.bond_size_sats), bondPercent:userSummary.bond_size_percent})}
secondary={buttonValue === 0 ? t("Maker bond") : t("Taker bond") }/>
<ListItemText
sx={{color:'#2e7d32'}}
primary={<b>{t("Unlocked")}</b>}/>
</ListItem>
</List>
</div>
{/* Platform Summary */}
<div style={{display: buttonValue == 1 ? '':'none'}}>
<List dense={true}>
<ListItemText
primary={t('{{tradeFeeSats}} Sats ({{tradeFeePercent}}%)', {
tradeFeeSats: pn(userSummary.trade_fee_sats),
tradeFeePercent: parseFloat(
(userSummary.trade_fee_percent * 100).toPrecision(3),
),
})}
secondary={'Trade fee'}
/>
</ListItem>
{userSummary.is_swap ? (
<ListItem>
<ListItemIcon>
<AccountBalanceIcon/>
<LinkIcon />
</ListItemIcon>
<ListItemText
primary={t("{{revenueSats}} Sats",{revenueSats:pn(platformSummary.trade_revenue_sats)})}
secondary={t("Platform trade revenue")}/>
</ListItem>
<ListItem>
<ListItemIcon>
<RouteIcon/>
</ListItemIcon>
<ListItemText
primary={t("{{routingFeeSats}} MiliSats",{routingFeeSats:pn(platformSummary.routing_fee_sats)})}
secondary={t("Platform covered routing fee")}/>
<ListItemText
primary={t('{{swapFeeSats}} Sats ({{swapFeePercent}}%)', {
swapFeeSats: pn(userSummary.swap_fee_sats),
swapFeePercent: userSummary.swap_fee_percent,
})}
secondary={t('Onchain swap fee')}
/>
<ListItemText
primary={t('{{miningFeeSats}} Sats', {
miningFeeSats: pn(userSummary.mining_fee_sats),
})}
secondary={t('Mining fee')}
/>
</ListItem>
) : null}
<ListItem>
<ListItemIcon>
<PriceChangeIcon/>
</ListItemIcon>
<ListItemText
primary={`${pn(platformSummary.contract_exchange_rate.toPrecision(7))} ${currencyCode}/BTC`}
secondary={t("Contract exchange rate")}/>
</ListItem>
<ListItem>
<ListItemText
primary={format(contractTimestamp, "do LLL HH:mm:ss")}
secondary={t("Timestamp")}/>
<ListItemIcon>
<ScheduleIcon/>
</ListItemIcon>
<ListItemText
primary={`${String(hours).padStart(2, '0')}:${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`}
secondary={t("Completed in")}/>
</ListItem>
</List>
</div>
<ListItem>
<ListItemIcon>
<LockOpenIcon color='success' />
</ListItemIcon>
<ListItemText
primary={t('{{bondSats}} Sats ({{bondPercent}}%)', {
bondSats: pn(userSummary.bond_size_sats),
bondPercent: userSummary.bond_size_percent,
})}
secondary={buttonValue === 0 ? t('Maker bond') : t('Taker bond')}
/>
<ListItemText sx={{ color: '#2e7d32' }} primary={<b>{t('Unlocked')}</b>} />
</ListItem>
</List>
</div>
{/* Platform Summary */}
<div style={{ display: buttonValue == 1 ? '' : 'none' }}>
<List dense={true}>
<ListItem>
<ListItemIcon>
<AccountBalanceIcon />
</ListItemIcon>
<ListItemText
primary={t('{{revenueSats}} Sats', {
revenueSats: pn(platformSummary.trade_revenue_sats),
})}
secondary={t('Platform trade revenue')}
/>
</ListItem>
<ListItem>
<ListItemIcon>
<RouteIcon />
</ListItemIcon>
<ListItemText
primary={t('{{routingFeeSats}} MiliSats', {
routingFeeSats: pn(platformSummary.routing_fee_sats),
})}
secondary={t('Platform covered routing fee')}
/>
</ListItem>
<ListItem>
<ListItemIcon>
<PriceChangeIcon />
</ListItemIcon>
<ListItemText
primary={`${pn(
platformSummary.contract_exchange_rate.toPrecision(7),
)} ${currencyCode}/BTC`}
secondary={t('Contract exchange rate')}
/>
</ListItem>
<ListItem>
<ListItemText
primary={format(contractTimestamp, 'do LLL HH:mm:ss')}
secondary={t('Timestamp')}
/>
<ListItemIcon>
<ScheduleIcon />
</ListItemIcon>
<ListItemText
primary={`${String(hours).padStart(2, '0')}:${String(mins).padStart(
2,
'0',
)}:${String(secs).padStart(2, '0')}`}
secondary={t('Completed in')}
/>
</ListItem>
</List>
</div>
</AccordionDetails>
</Accordion>
</Grid>
);
};
export default TradeSummary;

View File

@ -1,7 +1,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 React, { Component } from 'react';
import { withTranslation, Trans } from 'react-i18next';
import { Paper, Alert, AlertTitle, Button, Link } from '@mui/material';
import MediaQuery from 'react-responsive';
class UnsafeAlert extends Component {
constructor(props) {
@ -11,86 +11,126 @@ class UnsafeAlert extends Component {
isSelfhosted: this.isSelfhosted(),
};
}
getHost(){
var url = (window.location != window.parent.location) ? this.getHost(document.referrer) : document.location.href;
return url.split('/')[2]
getHost() {
var url =
window.location != window.parent.location
? this.getHost(document.referrer)
: document.location.href;
return url.split('/')[2];
}
isSelfhosted(){
isSelfhosted() {
var http = new XMLHttpRequest();
http.open('HEAD', `${location.protocol}//${this.getHost()}/selfhosted`, false);
http.send();
return http.status!=404;
return http.status != 404;
}
safe_urls = [
'robosats6tkf3eva7x2voqso3a5wcorsnw34jveyxfqi2fu7oyheasid.onion',
'robotestagw3dcxmd66r4rgksb4nmmr43fh77bzn2ia2eucduyeafnyd.onion',
'robodevs7ixniseezbv7uryxhamtz3hvcelzfwpx3rvoipttjomrmpqd.onion',
'robosats.i2p',
'r7r4sckft6ptmk4r2jajiuqbowqyxiwsle4iyg4fijtoordc6z7a.b32.i2p',
]
];
render() {
const { t, i18n} = this.props;
const { t, i18n } = this.props;
// If alert is hidden return null
if (!this.state.show){return null}
if (!this.state.show) {
return null;
}
// Show selfhosted notice
if (this.state.isSelfhosted){
return(
if (this.state.isSelfhosted) {
return (
<div>
<Paper elevation={6} className="alertUnsafe">
<Alert severity="success" sx={{maxHeight:"120px"}}
action={<Button color="success" onClick={() => this.setState({show:false})}>{t("Hide")}</Button>}
>
<AlertTitle>{t("You are self-hosting RoboSats")}</AlertTitle>
{t("RoboSats client is served from your own node granting you the strongest security and privacy.")}
<Paper elevation={6} className='alertUnsafe'>
<Alert
severity='success'
sx={{ maxHeight: '120px' }}
action={
<Button color='success' onClick={() => this.setState({ show: false })}>
{t('Hide')}
</Button>
}
>
<AlertTitle>{t('You are self-hosting RoboSats')}</AlertTitle>
{t(
'RoboSats client is served from your own node granting you the strongest security and privacy.',
)}
</Alert>
</Paper>
</div>
)
);
}
// Show unsafe alert
if (!this.safe_urls.includes(this.getHost())){
return(
if (!this.safe_urls.includes(this.getHost())) {
return (
<div>
<MediaQuery minWidth={800}>
<Paper elevation={6} className="alertUnsafe">
<Alert severity="warning" sx={{maxHeight:"100px"}}
action={<Button onClick={() => this.setState({show:false})}>{t("Hide")}</Button>}
<Paper elevation={6} className='alertUnsafe'>
<Alert
severity='warning'
sx={{ maxHeight: '100px' }}
action={<Button onClick={() => this.setState({ show: false })}>{t('Hide')}</Button>}
>
<AlertTitle>{t("You are not using RoboSats privately")}</AlertTitle>
<Trans i18nKey="desktop_unsafe_alert">
Some features are disabled for your protection (e.g. chat) and you will not be able to complete a
trade without them. To protect your privacy and fully enable RoboSats, use <Link href='https://www.torproject.org/download/' target="_blank">Tor Browser</Link> and visit the <Link href='http://robosats6tkf3eva7x2voqso3a5wcorsnw34jveyxfqi2fu7oyheasid.onion' target="_blank">Onion</Link> site.
<AlertTitle>{t('You are not using RoboSats privately')}</AlertTitle>
<Trans i18nKey='desktop_unsafe_alert'>
Some features are disabled for your protection (e.g. chat) and you will not be
able to complete a trade without them. To protect your privacy and fully enable
RoboSats, use{' '}
<Link href='https://www.torproject.org/download/' target='_blank'>
Tor Browser
</Link>{' '}
and visit the{' '}
<Link
href='http://robosats6tkf3eva7x2voqso3a5wcorsnw34jveyxfqi2fu7oyheasid.onion'
target='_blank'
>
Onion
</Link>{' '}
site.
</Trans>
</Alert>
</Paper>
</MediaQuery>
<MediaQuery maxWidth={799}>
<Paper elevation={6} className="alertUnsafe">
<Alert severity="warning" sx={{maxHeight:"120px"}}>
<AlertTitle>{t("You are not using RoboSats privately")}</AlertTitle>
<Trans i18nKey="phone_unsafe_alert">
You will not be able to complete a
trade. Use <Link href='https://www.torproject.org/download/' target="_blank">Tor Browser</Link> and visit the <Link href='http://robosats6tkf3eva7x2voqso3a5wcorsnw34jveyxfqi2fu7oyheasid.onion' target="_blank">Onion</Link> site.
</Trans>
<div style={{width: '100%'}}>
</div>
<div align="center">
<Button className="hideAlertButton" onClick={() => this.setState({show:false})}>{t("Hide")}</Button>
</div>
</Alert>
<Paper elevation={6} className='alertUnsafe'>
<Alert severity='warning' sx={{ maxHeight: '120px' }}>
<AlertTitle>{t('You are not using RoboSats privately')}</AlertTitle>
<Trans i18nKey='phone_unsafe_alert'>
You will not be able to complete a trade. Use{' '}
<Link href='https://www.torproject.org/download/' target='_blank'>
Tor Browser
</Link>{' '}
and visit the{' '}
<Link
href='http://robosats6tkf3eva7x2voqso3a5wcorsnw34jveyxfqi2fu7oyheasid.onion'
target='_blank'
>
Onion
</Link>{' '}
site.
</Trans>
<div style={{ width: '100%' }}></div>
<div align='center'>
<Button
className='hideAlertButton'
onClick={() => this.setState({ show: false })}
>
{t('Hide')}
</Button>
</div>
</Alert>
</Paper>
</MediaQuery>
</div>
)
);
}
}
}

View File

@ -1,23 +1,32 @@
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 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';
import ContentCopy from "@mui/icons-material/ContentCopy";
import ContentCopy from '@mui/icons-material/ContentCopy';
import BoltIcon from '@mui/icons-material/Bolt';
import DownloadIcon from '@mui/icons-material/Download';
import { RoboSatsNoTextIcon } from "./Icons";
import { RoboSatsNoTextIcon } from './Icons';
import { sha256 } from 'js-sha256';
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 { 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';
class UserGenPage extends Component {
constructor(props) {
@ -25,7 +34,7 @@ class UserGenPage extends Component {
this.state = {
openInfo: false,
tokenHasChanged: false,
token: ""
token: '',
};
this.refCode = this.props.match.params.refCode;
@ -34,304 +43,398 @@ class UserGenPage extends Component {
componentDidMount() {
// Checks in parent HomePage if there is already a nick and token
// Displays the existing one
if (this.props.nickname != null){
if (this.props.nickname != null) {
this.setState({
nickname: this.props.nickname,
token: this.props.token? this.props.token : "",
token: this.props.token ? this.props.token : '',
avatarUrl: '/static/assets/avatars/' + this.props.nickname + '.png',
loadingRobot: false
loadingRobot: false,
});
}
else{
var newToken = genBase62Token(36)
} else {
var newToken = genBase62Token(36);
this.setState({
token: newToken
token: newToken,
});
this.getGeneratedUser(newToken);
}
}
getGeneratedUser=(token)=>{
getGeneratedUser = (token) => {
const strength = tokenStrength(token);
const refCode = this.refCode
const refCode = this.refCode;
const requestOptions = genKey(token).then(function(key) {
const requestOptions = genKey(token).then(function (key) {
return {
method: 'POST',
headers: {'Content-Type':'application/json', 'X-CSRFToken': getCookie('csrftoken')},
headers: { 'Content-Type': 'application/json', 'X-CSRFToken': getCookie('csrftoken') },
body: JSON.stringify({
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,
})
}}
);
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,
}),
};
});
console.log(requestOptions);
console.log(requestOptions)
requestOptions.then((options) =>
fetch("/api/user/",options)
fetch('/api/user/', options)
.then((response) => response.json())
.then((data) => { console.log(data) &
this.setState({
.then((data) => {
console.log(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,
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: 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,
})) & 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)
})
}) &
// 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: 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,
}) &
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);
}),
);
}
};
delGeneratedUser() {
const requestOptions = {
method: 'DELETE',
headers: {'Content-Type':'application/json', 'X-CSRFToken': getCookie('csrftoken')},
headers: { 'Content-Type': 'application/json', 'X-CSRFToken': getCookie('csrftoken') },
};
fetch("/api/user", requestOptions)
.then((response) => response.json());
fetch('/api/user', requestOptions).then((response) => response.json());
deleteCookie("sessionid");
deleteCookie("robot_token");
deleteCookie("pub_key");
deleteCookie("enc_priv_key");
deleteCookie('sessionid');
deleteCookie('robot_token');
deleteCookie('pub_key');
deleteCookie('enc_priv_key');
}
handleClickNewRandomToken=()=>{
handleClickNewRandomToken = () => {
var token = genBase62Token(36);
this.setState({
token: token,
tokenHasChanged: true,
});
this.props.setAppState({copiedToken: true})
}
this.props.setAppState({ copiedToken: true });
};
handleChangeToken=(e)=>{
handleChangeToken = (e) => {
this.setState({
token: e.target.value.split(' ').join(''),
tokenHasChanged: true,
})
}
});
};
handleClickSubmitToken=()=>{
handleClickSubmitToken = () => {
this.delGeneratedUser();
this.getGeneratedUser(this.state.token);
this.setState({loadingRobot: true, tokenHasChanged: false});
this.props.setAppState({avatarLoaded: false,
nickname: null,
token: null,
copiedToken: false,
this.setState({ loadingRobot: true, tokenHasChanged: false });
this.props.setAppState({
avatarLoaded: false,
nickname: null,
token: null,
copiedToken: false,
lastOrderId: null,
activeOrderId: null});
}
activeOrderId: null,
});
};
handleClickOpenInfo = () => {
this.setState({openInfo: true});
this.setState({ openInfo: true });
};
handleCloseInfo = () => {
this.setState({openInfo: false});
this.setState({ openInfo: false });
};
createJsonFile = () => {
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'),
})
}
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() {
const { t, i18n} = this.props;
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'/>
<div className='clickTrough' />
</Grid>
<Grid item xs={12} align="center" sx={{width:370 * fontSizeFactor, height: 260 * fontSizeFactor}}>
{!this.state.loadingRobot && this.state.avatarUrl ?
<Grid
item
xs={12}
align='center'
sx={{ width: 370 * fontSizeFactor, height: 260 * fontSizeFactor }}
>
{!this.state.loadingRobot && this.state.avatarUrl ? (
<div>
<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>
<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>
<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>
<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>
<br/>
<br />
</Grid>
</div>
: <CircularProgress sx={{position: 'relative', top: 100, }}/>}
) : (
<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>
{
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 ? true : false}
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}}>
) : (
''
)}
<Grid container align='center'>
<Grid item xs={12} align='center'>
<TextField
sx={{ maxWidth: 280 * fontSizeFactor }}
error={this.state.bad_request ? true : false}
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,
}}
>
<Grid container>
<Grid item xs={6}>
<Tooltip enterTouchDelay={250} title={t("Save token and PGP credentials to file")}>
<Grid item xs={6}>
<Tooltip
enterTouchDelay={250}
title={t('Save token and PGP credentials to file')}
>
<span>
<IconButton
color="primary"
disabled={!(getCookie('robot_token')==this.state.token) || !this.props.avatarLoaded}
onClick={()=> saveAsJson(this.state.nickname+'.json', this.createJsonFile())}
<IconButton
color='primary'
disabled={
!(getCookie('robot_token') == this.state.token) ||
!this.props.avatarLoaded
}
onClick={() =>
saveAsJson(this.state.nickname + '.json', this.createJsonFile())
}
>
<DownloadIcon sx={{width: 22*fontSizeFactor, height: 22*fontSizeFactor}}/>
</IconButton>
<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.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}}/>
<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>
</div>,
endAdornment:
<Tooltip enterTouchDelay={250} title={t("Generate a new token")}>
</div>
),
endAdornment: (
<Tooltip enterTouchDelay={250} title={t('Generate a new token')}>
<IconButton onClick={this.handleClickNewRandomToken}>
<CasinoIcon sx={{width: 18*fontSizeFactor, height: 18*fontSizeFactor}}/>
<CasinoIcon
sx={{ width: 18 * fontSizeFactor, height: 18 * fontSizeFactor }}
/>
</IconButton>
</Tooltip>,
}}
/>
</Grid>
</Tooltip>
),
}}
/>
</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>
</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")}>
) : (
<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>
<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">
<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>
)}
</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>
<Grid item xs={12} align="center" sx={{width: 370*fontSizeFactor}}>
<Grid item>
<div style={{height: 30*fontSizeFactor}}/>
</Grid>
<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 item xs={12} align='center' sx={{ width: 370 * fontSizeFactor }}>
<Grid item>
<div style={{ height: 30 * fontSizeFactor }} />
</Grid>
<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>
);
}

View File

@ -1,6 +1,6 @@
import i18n from "i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import { initReactI18next } from "react-i18next";
import i18n from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';
import HttpApi from 'i18next-http-backend';
// import translationEN from "../../static/locales/en.json";
@ -20,49 +20,49 @@ i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
// resources: {
// en: {translations: translationEN},
// es: {translations: translationES},
// ru: {translations: translationRU},
// de: {translations: translationDE},
// // zh: {translations: translationZH},
// pl: {translations: translationPL},
// fr: {translations: translationFR},
// ca: {translations: translationCA},
// it: {translations: translationIT},
// pt: {translations: translationPT},
// eu: {translations: translationEU},
// sv: {translations: translationSV},
// cs: {translations: translationCS},
// th: {translations: translationCS},
// },
backend:{
loadPath: '/static/locales/{{lng}}.json',
allowMultiLoading: false, // set loadPath: '/locales/resources.json?lng={{lng}}&ns={{ns}}' to adapt to multiLoading
crossDomain: false,
withCredentials: false,
overrideMimeType: false,
reloadInterval: false // can be used to reload resources in a specific interval (useful in server environments)
},
// resources: {
// en: {translations: translationEN},
// es: {translations: translationES},
// ru: {translations: translationRU},
// de: {translations: translationDE},
// // zh: {translations: translationZH},
// pl: {translations: translationPL},
// fr: {translations: translationFR},
// ca: {translations: translationCA},
// it: {translations: translationIT},
// pt: {translations: translationPT},
// eu: {translations: translationEU},
// sv: {translations: translationSV},
// cs: {translations: translationCS},
// th: {translations: translationCS},
// },
fallbackLng: "en",
debug: false,
backend: {
loadPath: '/static/locales/{{lng}}.json',
allowMultiLoading: false, // set loadPath: '/locales/resources.json?lng={{lng}}&ns={{ns}}' to adapt to multiLoading
crossDomain: false,
withCredentials: false,
overrideMimeType: false,
reloadInterval: false, // can be used to reload resources in a specific interval (useful in server environments)
},
// have a common namespace used around the full app
ns: ["translations"],
defaultNS: "translations",
fallbackLng: 'en',
debug: false,
keySeparator: false, // we use content as keys
// have a common namespace used around the full app
ns: ['translations'],
defaultNS: 'translations',
interpolation: {
escapeValue: false,
formatSeparator: ","
},
react: {
useSuspense: false,
}
});
keySeparator: false, // we use content as keys
export default i18n;
interpolation: {
escapeValue: false,
formatSeparator: ',',
},
react: {
useSuspense: false,
},
});
export default i18n;

File diff suppressed because one or more lines are too long

View File

@ -1,86 +1,86 @@
export const paymentMethods = [
{name: "Revolut",icon:'revolut'},
{name: "CashApp",icon:'cashapp'},
{name: "Zelle",icon:'zelle'},
{name: "Strike",icon:'strike'},
{name: "WeChat Pay", icon: 'wechatpay'},
{name: "Rebellion",icon:'rebellion'},
{name: "Instant SEPA", icon:'sepa'},
{name: "Interac e-Transfer",icon:'interac'},
{name: "Wise",icon:'wise'},
{name: "Venmo",icon:'venmo'},
{name: "Faster Payments",icon:'faster'},
{name: "Paypal Friends & Family",icon:'paypal'},
{name: "LINE Pay",icon:'linepay'},
{name: "PromptPay",icon:'promptpay'},
{name: "Bizum",icon:'bizum'},
{name: "N26",icon:'n26'},
{name: "Tinkoff",icon:'tinkoff'},
{name: "TWINT",icon:'twint'},
{name: "BLIK",icon:'blik'},
{name: "MBWay",icon:'mbway'},
{name: "W1TTY",icon:'w1tty'},
{name: "Verse",icon:'verse'},
{name: "Paysera",icon:'paysera'},
{name: "Amazon GiftCard",icon:'amazon'},
{name: "Ozon GiftCard",icon:'ozon'},
{name: "AliPay", icon: 'alipay'},
{name: "GPay", icon: 'gpay'},
{name: "Bancolombia",icon:'bancolombia'},
{name: "Pago Movil BDV",icon:'pagomovilbdv'},
{name: "SPEI",icon:'spei'},
{name: "PIX",icon:'pix'},
{name: "PayID",icon:'payid'},
{name: "Paysafe",icon:'paysafe'},
{name: "Sberbank",icon:'sberbank'},
{name: "PhonePe",icon:'phonepe'},
{name: "OVO",icon:'ovo'},
{name: "HalCash",icon:'halcash'},
{name: "Vivid",icon:'vivid'},
{name: "Google Play Gift Code",icon:'googleplay'},
{name: "Apple Pay",icon:'applepay'},
{name: "Steam",icon:'steam'},
{name: "Nequi",icon:'nequi'},
{name: "ShakePay",icon:'shakepay'},
{name: "DaviPlata",icon:'daviplata'},
{name: "CoDi",icon:'codi'},
{name: "TaiwanPay",icon:'taiwanpay'},
{name: "MaiCoin",icon:'maicoin'},
{name: "GoPay", icon: 'gopay'},
{name: "MercadoPago",icon:'mercadopago'},
{name: "Monero",icon:'monero'},
{name: "USDT",icon:'usdt'},
{name: "Airtel Money",icon:'airtel'},
{name: "MTN Money",icon:'mtn'},
{name: "M-Pesa",icon:'mpesa'},
{name: "UPI",icon:'upi'},
{name: "MoMo",icon:'momo'},
{name: "Tigo Pesa",icon:'tigopesa'},
{name: "Cash F2F",icon:'cash'},
{name: "Amazon USA GiftCard",icon:'amazonus'},
{name: "Amazon DE GiftCard",icon:'amazonde'},
{name: "Amazon AU GiftCard",icon:'amazonau'},
{name: "Amazon SA GiftCard",icon:'amazonsa'},
{name: "Amazon ES GiftCard",icon:'amazones'},
{name: "Amazon CA GiftCard",icon:'amazonca'},
{name: "Amazon CN GiftCard",icon:'amazoncn'},
{name: "Amazon AE GiftCard",icon:'amazonae'},
{name: "Amazon FR GiftCard",icon:'amazonfr'},
{name: "Amazon NL GiftCard",icon:'amazonnl'},
{name: "Amazon IN GiftCard",icon:'amazonin'},
{name: "Amazon IT GiftCard",icon:'amazonit'},
{name: "Amazon JP GiftCard",icon:'amazonjp'},
{name: "Amazon MX GiftCard",icon:'amazonmx'},
{name: "Amazon PL GiftCard",icon:'amazonpl'},
{name: "Amazon UK GiftCard",icon:'amazonuk'},
{name: "Amazon SE GiftCard",icon:'amazonse'},
{name: "Amazon SG GiftCard",icon:'amazonsg'},
{name: "Amazon TR GiftCard",icon:'amazontr'},
];
{ name: 'Revolut', icon: 'revolut' },
{ name: 'CashApp', icon: 'cashapp' },
{ name: 'Zelle', icon: 'zelle' },
{ name: 'Strike', icon: 'strike' },
{ name: 'WeChat Pay', icon: 'wechatpay' },
{ name: 'Rebellion', icon: 'rebellion' },
{ name: 'Instant SEPA', icon: 'sepa' },
{ name: 'Interac e-Transfer', icon: 'interac' },
{ name: 'Wise', icon: 'wise' },
{ name: 'Venmo', icon: 'venmo' },
{ name: 'Faster Payments', icon: 'faster' },
{ name: 'Paypal Friends & Family', icon: 'paypal' },
{ name: 'LINE Pay', icon: 'linepay' },
{ name: 'PromptPay', icon: 'promptpay' },
{ name: 'Bizum', icon: 'bizum' },
{ name: 'N26', icon: 'n26' },
{ name: 'Tinkoff', icon: 'tinkoff' },
{ name: 'TWINT', icon: 'twint' },
{ name: 'BLIK', icon: 'blik' },
{ name: 'MBWay', icon: 'mbway' },
{ name: 'W1TTY', icon: 'w1tty' },
{ name: 'Verse', icon: 'verse' },
{ name: 'Paysera', icon: 'paysera' },
{ name: 'Amazon GiftCard', icon: 'amazon' },
{ name: 'Ozon GiftCard', icon: 'ozon' },
{ name: 'AliPay', icon: 'alipay' },
{ name: 'GPay', icon: 'gpay' },
{ name: 'Bancolombia', icon: 'bancolombia' },
{ name: 'Pago Movil BDV', icon: 'pagomovilbdv' },
{ name: 'SPEI', icon: 'spei' },
{ name: 'PIX', icon: 'pix' },
{ name: 'PayID', icon: 'payid' },
{ name: 'Paysafe', icon: 'paysafe' },
{ name: 'Sberbank', icon: 'sberbank' },
{ name: 'PhonePe', icon: 'phonepe' },
{ name: 'OVO', icon: 'ovo' },
{ name: 'HalCash', icon: 'halcash' },
{ name: 'Vivid', icon: 'vivid' },
{ name: 'Google Play Gift Code', icon: 'googleplay' },
{ name: 'Apple Pay', icon: 'applepay' },
{ name: 'Steam', icon: 'steam' },
{ name: 'Nequi', icon: 'nequi' },
{ name: 'ShakePay', icon: 'shakepay' },
{ name: 'DaviPlata', icon: 'daviplata' },
{ name: 'CoDi', icon: 'codi' },
{ name: 'TaiwanPay', icon: 'taiwanpay' },
{ name: 'MaiCoin', icon: 'maicoin' },
{ name: 'GoPay', icon: 'gopay' },
{ name: 'MercadoPago', icon: 'mercadopago' },
{ name: 'Monero', icon: 'monero' },
{ name: 'USDT', icon: 'usdt' },
{ name: 'Airtel Money', icon: 'airtel' },
{ name: 'MTN Money', icon: 'mtn' },
{ name: 'M-Pesa', icon: 'mpesa' },
{ name: 'UPI', icon: 'upi' },
{ name: 'MoMo', icon: 'momo' },
{ name: 'Tigo Pesa', icon: 'tigopesa' },
{ name: 'Cash F2F', icon: 'cash' },
{ name: 'Amazon USA GiftCard', icon: 'amazonus' },
{ name: 'Amazon DE GiftCard', icon: 'amazonde' },
{ name: 'Amazon AU GiftCard', icon: 'amazonau' },
{ name: 'Amazon SA GiftCard', icon: 'amazonsa' },
{ name: 'Amazon ES GiftCard', icon: 'amazones' },
{ name: 'Amazon CA GiftCard', icon: 'amazonca' },
{ name: 'Amazon CN GiftCard', icon: 'amazoncn' },
{ name: 'Amazon AE GiftCard', icon: 'amazonae' },
{ name: 'Amazon FR GiftCard', icon: 'amazonfr' },
{ name: 'Amazon NL GiftCard', icon: 'amazonnl' },
{ name: 'Amazon IN GiftCard', icon: 'amazonin' },
{ name: 'Amazon IT GiftCard', icon: 'amazonit' },
{ name: 'Amazon JP GiftCard', icon: 'amazonjp' },
{ name: 'Amazon MX GiftCard', icon: 'amazonmx' },
{ name: 'Amazon PL GiftCard', icon: 'amazonpl' },
{ name: 'Amazon UK GiftCard', icon: 'amazonuk' },
{ name: 'Amazon SE GiftCard', icon: 'amazonse' },
{ name: 'Amazon SG GiftCard', icon: 'amazonsg' },
{ name: 'Amazon TR GiftCard', icon: 'amazontr' },
];
export const swapDestinations = [
{name: "On-Chain BTC",icon:'onchain'},
{name: "RBTC",icon:'rbtc'},
{name: "LBTC",icon:'lbtc'},
{name: "WBTC",icon:'wbtc'},
];
{ name: 'On-Chain BTC', icon: 'onchain' },
{ name: 'RBTC', icon: 'rbtc' },
{ name: 'LBTC', icon: 'lbtc' },
{ name: 'WBTC', icon: 'wbtc' },
];

View File

@ -1 +1 @@
import App from "./components/App";
import App from './components/App';

View File

@ -1,13 +1,13 @@
export interface Limit {
code: string,
price: number,
min_amount: number,
max_amount: number,
max_bondless_amount: number
code: string;
price: number;
min_amount: number;
max_amount: number;
max_bondless_amount: number;
}
export interface LimitList {
[currencyCode: string]: Limit
[currencyCode: string]: Limit;
}
export default Limit
export default Limit;

View File

@ -1,24 +1,24 @@
export interface Order {
id: number,
created_at: Date,
expires_at: Date,
type: number,
currency: number,
amount: string,
base_amount?: number,
has_range: boolean,
min_amount: number,
max_amount: number,
payment_method: string,
is_explicit: false,
premium: number,
satoshis: number,
bondless_taker: boolean,
maker: number,
escrow_duration: number,
maker_nick: string,
price: number,
maker_status: "Active" | "Seen recently" | "Inactive"
id: number;
created_at: Date;
expires_at: Date;
type: number;
currency: number;
amount: string;
base_amount?: number;
has_range: boolean;
min_amount: number;
max_amount: number;
payment_method: string;
is_explicit: false;
premium: number;
satoshis: number;
bondless_taker: boolean;
maker: number;
escrow_duration: number;
maker_nick: string;
price: number;
maker_status: 'Active' | 'Seen recently' | 'Inactive';
}
export default Order
export default Order;

View File

@ -1,24 +1,25 @@
export function copyToClipboard(textToCopy) {
// navigator clipboard api needs a secure context (https)
// this function attempts to copy also on http contexts
// useful on the http i2p site and on torified browsers
if (navigator.clipboard && window.isSecureContext) {
// navigator clipboard api needs a secure context (https)
// this function attempts to copy also on http contexts
// useful on the http i2p site and on torified browsers
if (navigator.clipboard && window.isSecureContext) {
// navigator clipboard api method'
return navigator.clipboard.writeText(textToCopy);
} else {
} else {
// text area method
let textArea = document.createElement("textarea");
let textArea = document.createElement('textarea');
textArea.value = textToCopy;
// make the textarea out of viewport
textArea.style.position = "fixed";
textArea.style.left = "-999999px";
textArea.style.top = "-999999px";
textArea.style.position = 'fixed';
textArea.style.left = '-999999px';
textArea.style.top = '-999999px';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
return new Promise((res, rej) => {
// here the magic happens
document.execCommand('copy') ? res() : rej();
textArea.remove();
// here the magic happens
document.execCommand('copy') ? res() : rej();
textArea.remove();
});
}}
}
}

View File

@ -5,7 +5,7 @@ export const getCookie = (name) => {
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
if (cookie.substring(0, name.length + 1) === name + '=') {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
@ -14,10 +14,10 @@ export const getCookie = (name) => {
return cookieValue;
};
export const writeCookie = (key,value) => {
document.cookie=`${key}=${value};path=/;SameSite=Strict`;
}
export const writeCookie = (key, value) => {
document.cookie = `${key}=${value};path=/;SameSite=Strict`;
};
export const deleteCookie = (name) => {
document.cookie = `${name}= ; expires = Thu, 01 Jan 1970 00:00:00 GMT`
}
document.cookie = `${name}= ; expires = Thu, 01 Jan 1970 00:00:00 GMT`;
};

View File

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

View File

@ -1,12 +1,12 @@
import {
generateKey,
readKey,
readPrivateKey,
decryptKey,
encrypt,
decrypt,
createMessage,
readMessage
import {
generateKey,
readKey,
readPrivateKey,
decryptKey,
encrypt,
decrypt,
createMessage,
readMessage,
} from 'openpgp/lightweight';
import { sha256 } from 'js-sha256';
@ -16,60 +16,69 @@ export async function genKey(highEntropyToken) {
const keyPair = await generateKey({
type: 'ecc', // Type of the key, defaults to ECC
curve: 'curve25519', // ECC curve name, defaults to curve25519
userIDs: [{name: 'RoboSats ID '+ sha256(sha256(highEntropyToken))}], //Ideally it would be the avatar nickname, but the nickname is generated only after submission. The second SHA256 can be converted into the Nickname using nick_generator package.
userIDs: [{ name: 'RoboSats ID ' + sha256(sha256(highEntropyToken)) }], //Ideally it would be the avatar nickname, but the nickname is generated only after submission. The second SHA256 can be converted into the Nickname using nick_generator package.
passphrase: highEntropyToken,
format: 'armored',
date: d.setDate(d.getDate()-1) // One day of offset. Helps reducing errors due to client's system time being in the future.
})
date: d.setDate(d.getDate() - 1), // One day of offset. Helps reducing errors due to client's system time being in the future.
});
return {publicKeyArmored: keyPair.publicKey, encryptedPrivateKeyArmored: keyPair.privateKey}
};
return { publicKeyArmored: keyPair.publicKey, encryptedPrivateKeyArmored: keyPair.privateKey };
}
// Encrypt and sign a message
export async function encryptMessage(plaintextMessage, ownPublicKeyArmored, peerPublicKeyArmored, privateKeyArmored, passphrase) {
export async function encryptMessage(
plaintextMessage,
ownPublicKeyArmored,
peerPublicKeyArmored,
privateKeyArmored,
passphrase,
) {
const ownPublicKey = await readKey({ armoredKey: ownPublicKeyArmored });
const peerPublicKey = await readKey({ armoredKey: peerPublicKeyArmored });
const privateKey = await decryptKey({
privateKey: await readPrivateKey({ armoredKey: privateKeyArmored }),
passphrase
privateKey: await readPrivateKey({ armoredKey: privateKeyArmored }),
passphrase,
});
const d = new Date();
const encryptedMessage = await encrypt({
message: await createMessage({ text: plaintextMessage }), // input as Message object, message must be string
encryptionKeys: [ ownPublicKey, peerPublicKey ],
signingKeys: privateKey, // optional
date: d.setDate(d.getDate()-1) // One day of offset, avoids verification issue due to clock mismatch
message: await createMessage({ text: plaintextMessage }), // input as Message object, message must be string
encryptionKeys: [ownPublicKey, peerPublicKey],
signingKeys: privateKey, // optional
date: d.setDate(d.getDate() - 1), // One day of offset, avoids verification issue due to clock mismatch
});
return encryptedMessage; // '-----BEGIN PGP MESSAGE ... END PGP MESSAGE-----'
};
}
// Decrypt and check signature of a message
export async function decryptMessage(encryptedMessage, publicKeyArmored, privateKeyArmored, passphrase) {
export async function decryptMessage(
encryptedMessage,
publicKeyArmored,
privateKeyArmored,
passphrase,
) {
const publicKey = await readKey({ armoredKey: publicKeyArmored });
const privateKey = await decryptKey({
privateKey: await readPrivateKey({ armoredKey: privateKeyArmored }),
passphrase
privateKey: await readPrivateKey({ armoredKey: privateKeyArmored }),
passphrase,
});
const message = await readMessage({
armoredMessage: encryptedMessage // parse armored message
armoredMessage: encryptedMessage, // parse armored message
});
const { data: decrypted, signatures } = await decrypt({
message,
verificationKeys: publicKey, // optional
decryptionKeys: privateKey
message,
verificationKeys: publicKey, // optional
decryptionKeys: privateKey,
});
// check signature validity (signed messages only)
try {
await signatures[0].verified; // throws on invalid signature
console.log('Signature is valid');
return {decryptedMessage: decrypted, validSignature: true}
return { decryptedMessage: decrypted, validSignature: true };
} catch (e) {
return {decryptedMessage: decrypted, validSignature: false};
return { decryptedMessage: decrypted, validSignature: false };
}
};
}

View File

@ -1,45 +1,45 @@
import { pn, amountToString } from "./prettyNumbers";
import { pn, amountToString } from './prettyNumbers';
describe("prettyNumbers", () => {
test("pn()", () => {
describe('prettyNumbers', () => {
test('pn()', () => {
[
{input: null, output: undefined},
{input: undefined, output: undefined},
{input: 0, output: "0"},
{input: 1, output: "1"},
{input: 2, output: "2"},
{input: 10, output: "10"},
{input: 11, output: "11"},
{input: 11.0, output: "11"},
{input: 12.0, output: "12"},
{input: 100.50, output: "100.5"},
{input: 224.56, output: "224.56"},
{input: 1567, output: "1,567"},
{input: 15678, output: "15,678"},
{input: 2984.99, output: "2,984.99"},
{input: 100000.00, output: "100,000"},
{ input: null, output: undefined },
{ input: undefined, output: undefined },
{ input: 0, output: '0' },
{ input: 1, output: '1' },
{ input: 2, output: '2' },
{ input: 10, output: '10' },
{ input: 11, output: '11' },
{ input: 11.0, output: '11' },
{ input: 12.0, output: '12' },
{ input: 100.5, output: '100.5' },
{ input: 224.56, output: '224.56' },
{ input: 1567, output: '1,567' },
{ input: 15678, output: '15,678' },
{ input: 2984.99, output: '2,984.99' },
{ input: 100000.0, output: '100,000' },
].forEach((it) => {
const response = pn(it.input);
expect(response).toBe(it.output);
});
});
})
});
describe("amountToString", () => {
test("pn()", () => {
describe('amountToString', () => {
test('pn()', () => {
[
{input: null, output: "NaN"},
{input: undefined, output: "NaN"},
{input: ["", false, 50, 150] , output: "0"},
{input: ["100.00", false, 50, 150] , output: "100"},
{input: ["100.00", true, undefined, undefined] , output: "NaN-NaN"},
{input: ["100.00", true, undefined, 150] , output: "NaN-150"},
{input: ["100.00", true, 50, undefined] , output: "50-NaN"},
{input: ["100.00", true, 50, 150] , output: "50-150"},
{ input: null, output: 'NaN' },
{ input: undefined, output: 'NaN' },
{ input: ['', false, 50, 150], output: '0' },
{ input: ['100.00', false, 50, 150], output: '100' },
{ input: ['100.00', true, undefined, undefined], output: 'NaN-NaN' },
{ input: ['100.00', true, undefined, 150], output: 'NaN-150' },
{ input: ['100.00', true, 50, undefined], output: '50-NaN' },
{ input: ['100.00', true, 50, 150], output: '50-150' },
].forEach((it) => {
const params: any[] = it.input || []
const response = amountToString(params[0],params[1],params[2],params[3]);
const params: any[] = it.input || [];
const response = amountToString(params[0], params[1], params[2], params[3]);
expect(response).toBe(it.output);
});
});
})
});

View File

@ -3,21 +3,27 @@ export const pn = (value?: number | null): string | undefined => {
return;
}
const parts = value.toString().split(".");
const parts = value.toString().split('.');
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return parts.join(".");
return parts.join('.');
};
export const amountToString: (amount: string, has_range: boolean , min_amount: number, max_amount: number) => string =
(amount, has_range, min_amount, max_amount) => {
if (has_range){
return pn(parseFloat(Number(min_amount).toPrecision(4))) +
'-' +
pn(parseFloat(Number(max_amount).toPrecision(4)))
export const amountToString: (
amount: string,
has_range: boolean,
min_amount: number,
max_amount: number,
) => string = (amount, has_range, min_amount, max_amount) => {
if (has_range) {
return (
pn(parseFloat(Number(min_amount).toPrecision(4))) +
'-' +
pn(parseFloat(Number(max_amount).toPrecision(4)))
);
}
return pn(parseFloat(Number(amount).toPrecision(4))) || ""
}
return pn(parseFloat(Number(amount).toPrecision(4))) || '';
};
export default pn
export default pn;

View File

@ -1,22 +1,22 @@
/* function to save DATA as text from browser
* @param {String} file -- file name to save to
* @param {filename} data -- object to save
*/
* @param {String} file -- file name to save to
* @param {filename} data -- object to save
*/
export const saveAsJson = (filename, dataObjToWrite) => {
const blob = new Blob([JSON.stringify(dataObjToWrite, null, 2)], { type: "text/json" });
const link = document.createElement("a");
const blob = new Blob([JSON.stringify(dataObjToWrite, null, 2)], { type: 'text/json' });
const link = document.createElement('a');
link.download = filename;
link.href = window.URL.createObjectURL(blob);
link.dataset.downloadurl = ["text/json", link.download, link.href].join(":");
link.download = filename;
link.href = window.URL.createObjectURL(blob);
link.dataset.downloadurl = ['text/json', link.download, link.href].join(':');
const evt = new MouseEvent("click", {
view: window,
bubbles: true,
cancelable: true,
});
const evt = new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true,
});
link.dispatchEvent(evt);
link.remove()
};
link.dispatchEvent(evt);
link.remove();
};

View File

@ -1,17 +1,19 @@
// sort of cryptographically strong function to generate Base62 token client-side
export function genBase62Token(length){
return window.btoa(Array.from(
window.crypto.getRandomValues(
new Uint8Array(length * 2)))
.map((b) => String.fromCharCode(b))
.join("")).replace(/[+/]/g, "")
.substring(0, length);
export function genBase62Token(length) {
return window
.btoa(
Array.from(window.crypto.getRandomValues(new Uint8Array(length * 2)))
.map((b) => String.fromCharCode(b))
.join(''),
)
.replace(/[+/]/g, '')
.substring(0, length);
}
export function tokenStrength(token) {
const characters = token.split("").reduce(function(obj, s){
const characters = token.split('').reduce(function (obj, s) {
obj[s] = (obj[s] || 0) + 1;
return obj;
}, {});
return {uniqueValues:Object.keys(characters).length,counts:Object.values(characters)}
}
return { uniqueValues: Object.keys(characters).length, counts: Object.values(characters) };
}

View File

@ -1,18 +1,18 @@
import { requestProvider, WeblnProvider } from "webln";
import { requestProvider, WeblnProvider } from 'webln';
export const getWebln = async (): Promise<WeblnProvider> => {
const resultPromise = new Promise<WeblnProvider>(async (resolve, reject) => {
try {
const webln = await requestProvider()
const webln = await requestProvider();
if (webln) {
webln.enable()
resolve(webln)
webln.enable();
resolve(webln);
}
} catch (err) {
console.log("Coulnd't connect to Webln")
reject()
console.log("Coulnd't connect to Webln");
reject();
}
})
});
return resultPromise
}
return resultPromise;
};