mirror of
https://github.com/RoboSats/robosats.git
synced 2024-12-15 20:06:24 +00:00
741fba11d5
* Add swappable baseurls (network and coordinators) * Add testnet bitcoin styles * Small fixes * Small fixes
973 lines
34 KiB
TypeScript
973 lines
34 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import {
|
|
InputAdornment,
|
|
LinearProgress,
|
|
ButtonGroup,
|
|
Slider,
|
|
Switch,
|
|
Tooltip,
|
|
Button,
|
|
Grid,
|
|
Typography,
|
|
TextField,
|
|
Select,
|
|
FormHelperText,
|
|
MenuItem,
|
|
FormControl,
|
|
Radio,
|
|
FormControlLabel,
|
|
RadioGroup,
|
|
Box,
|
|
useTheme,
|
|
Collapse,
|
|
IconButton,
|
|
} from '@mui/material';
|
|
|
|
import { LimitList, Maker, Favorites, defaultMaker } from '../../models';
|
|
|
|
import { LocalizationProvider, TimePicker } from '@mui/x-date-pickers';
|
|
import DateFnsUtils from '@date-io/date-fns';
|
|
import { useHistory } from 'react-router-dom';
|
|
import { ConfirmationDialog } from '../Dialogs';
|
|
import { apiClient } from '../../services/api';
|
|
|
|
import { FlagWithProps } from '../Icons';
|
|
import AutocompletePayments from './AutocompletePayments';
|
|
import AmountRange from './AmountRange';
|
|
import currencyDict from '../../../static/assets/currencies.json';
|
|
import { pn } from '../../utils';
|
|
|
|
import { SelfImprovement, Lock, HourglassTop, DeleteSweep, Edit } from '@mui/icons-material';
|
|
import { LoadingButton } from '@mui/lab';
|
|
import { Page } from '../../basic/NavBar';
|
|
|
|
interface MakerFormProps {
|
|
limits: { list: LimitList; loading: boolean };
|
|
fetchLimits: () => void;
|
|
pricingMethods?: boolean;
|
|
maker: Maker;
|
|
fav: Favorites;
|
|
setFav: (state: Favorites) => void;
|
|
setMaker: (state: Maker) => void;
|
|
disableRequest?: boolean;
|
|
collapseAll?: boolean;
|
|
onSubmit?: () => void;
|
|
onReset?: () => void;
|
|
submitButtonLabel?: string;
|
|
onOrderCreated?: (id: number) => void;
|
|
hasRobot?: boolean;
|
|
setPage?: (state: Page) => void;
|
|
baseUrl: string;
|
|
}
|
|
|
|
const MakerForm = ({
|
|
limits,
|
|
fetchLimits,
|
|
pricingMethods = false,
|
|
fav,
|
|
setFav,
|
|
maker,
|
|
setMaker,
|
|
disableRequest = false,
|
|
collapseAll = false,
|
|
onSubmit = () => {},
|
|
onReset = () => {},
|
|
submitButtonLabel = 'Create Order',
|
|
onOrderCreated = () => null,
|
|
hasRobot = true,
|
|
setPage = () => null,
|
|
baseUrl,
|
|
}: MakerFormProps): JSX.Element => {
|
|
const { t } = useTranslation();
|
|
const theme = useTheme();
|
|
const history = useHistory();
|
|
const [badRequest, setBadRequest] = useState<string | null>(null);
|
|
const [amountLimits, setAmountLimits] = useState<number[]>([1, 1000]);
|
|
const [satoshisLimits, setSatoshisLimits] = useState<number[]>([20000, 4000000]);
|
|
const [currentPrice, setCurrentPrice] = useState<number | string>('...');
|
|
const [currencyCode, setCurrencyCode] = useState<string>('USD');
|
|
|
|
const [openDialogs, setOpenDialogs] = useState<boolean>(false);
|
|
const [submittingRequest, setSubmittingRequest] = useState<boolean>(false);
|
|
|
|
const maxRangeAmountMultiple = 7.8;
|
|
const minRangeAmountMultiple = 1.6;
|
|
const amountSafeThresholds = [1.03, 0.98];
|
|
|
|
useEffect(() => {
|
|
setCurrencyCode(currencyDict[fav.currency == 0 ? 1 : fav.currency]);
|
|
if (Object.keys(limits.list).length === 0) {
|
|
fetchLimits().then((data) => {
|
|
updateAmountLimits(data, fav.currency, maker.premium);
|
|
updateCurrentPrice(data, fav.currency, maker.premium);
|
|
updateSatoshisLimits(data);
|
|
});
|
|
} else {
|
|
updateAmountLimits(limits.list, fav.currency, maker.premium);
|
|
updateCurrentPrice(limits.list, fav.currency, maker.premium);
|
|
updateSatoshisLimits(limits.list);
|
|
|
|
fetchLimits();
|
|
}
|
|
}, []);
|
|
|
|
const updateAmountLimits = function (limitList: LimitList, currency: number, premium: number) {
|
|
const index = currency == 0 ? 1 : currency;
|
|
let minAmountLimit: number = limitList[index].min_amount * (1 + premium / 100);
|
|
let maxAmountLimit: number = limitList[index].max_amount * (1 + premium / 100);
|
|
|
|
// apply thresholds to ensure good request
|
|
minAmountLimit = minAmountLimit * amountSafeThresholds[0];
|
|
maxAmountLimit = maxAmountLimit * amountSafeThresholds[1];
|
|
setAmountLimits([minAmountLimit, maxAmountLimit]);
|
|
};
|
|
|
|
const updateSatoshisLimits = function (limitList: LimitList) {
|
|
const minAmount: number = limitList[1000].min_amount * 100000000;
|
|
const maxAmount: number = limitList[1000].max_amount * 100000000;
|
|
setSatoshisLimits([minAmount, maxAmount]);
|
|
};
|
|
|
|
const updateCurrentPrice = function (limitsList: LimitList, currency: number, premium: number) {
|
|
const index = currency == 0 ? 1 : currency;
|
|
let price = '...';
|
|
if (maker.isExplicit && maker.amount > 0 && maker.satoshis > 0) {
|
|
price = maker.amount / (maker.satoshis / 100000000);
|
|
} else if (!maker.is_explicit) {
|
|
price = limitsList[index].price * (1 + premium / 100);
|
|
}
|
|
setCurrentPrice(parseFloat(Number(price).toPrecision(5)));
|
|
};
|
|
|
|
const handleCurrencyChange = function (newCurrency: number) {
|
|
const currencyCode: string = currencyDict[newCurrency];
|
|
setCurrencyCode(currencyCode);
|
|
setFav({
|
|
...fav,
|
|
currency: newCurrency,
|
|
});
|
|
updateAmountLimits(limits.list, newCurrency, maker.premium);
|
|
updateCurrentPrice(limits.list, newCurrency, maker.premium);
|
|
if (maker.advancedOptions) {
|
|
setMaker({
|
|
...maker,
|
|
minAmount: parseFloat(Number(limits.list[newCurrency].max_amount * 0.25).toPrecision(2)),
|
|
maxAmount: parseFloat(Number(limits.list[newCurrency].max_amount * 0.75).toPrecision(2)),
|
|
});
|
|
}
|
|
};
|
|
|
|
const handlePaymentMethodChange = function (paymentArray: string[]) {
|
|
let str = '';
|
|
const arrayLength = paymentArray.length;
|
|
for (let i = 0; i < arrayLength; i++) {
|
|
str += paymentArray[i].name + ' ';
|
|
}
|
|
const paymentMethodText = str.slice(0, -1);
|
|
setMaker({
|
|
...maker,
|
|
paymentMethods: paymentArray,
|
|
paymentMethodsText: paymentMethodText,
|
|
badPaymentMethod: paymentMethodText.length > 50,
|
|
});
|
|
};
|
|
|
|
const handleMinAmountChange = function (e) {
|
|
setMaker({
|
|
...maker,
|
|
minAmount: parseFloat(Number(e.target.value).toPrecision(e.target.value < 100 ? 2 : 3)),
|
|
});
|
|
};
|
|
|
|
const handleMaxAmountChange = function (e) {
|
|
setMaker({
|
|
...maker,
|
|
maxAmount: parseFloat(Number(e.target.value).toPrecision(e.target.value < 100 ? 2 : 3)),
|
|
});
|
|
};
|
|
|
|
const handlePremiumChange = function (e: object) {
|
|
const max = 999;
|
|
const min = -100;
|
|
const newPremium = Math.floor(e.target.value * Math.pow(10, 2)) / Math.pow(10, 2);
|
|
let premium: number = newPremium;
|
|
let badPremiumText: string = '';
|
|
if (newPremium > 999) {
|
|
badPremiumText = t('Must be less than {{max}}%', { max });
|
|
premium = 999;
|
|
} else if (newPremium <= -100) {
|
|
badPremiumText = t('Must be more than {{min}}%', { min });
|
|
premium = -99.99;
|
|
}
|
|
updateCurrentPrice(limits.list, fav.currency, premium);
|
|
updateAmountLimits(limits.list, fav.currency, premium);
|
|
setMaker({
|
|
...maker,
|
|
premium,
|
|
badPremiumText,
|
|
});
|
|
};
|
|
|
|
const handleSatoshisChange = function (e: object) {
|
|
const newSatoshis = e.target.value;
|
|
let badSatoshisText: string = '';
|
|
let satoshis: string = newSatoshis;
|
|
if (newSatoshis > satoshisLimits[1]) {
|
|
badSatoshisText = t('Must be less than {{maxSats}', { maxSats: pn(satoshisLimits[1]) });
|
|
satoshis = satoshisLimits[1];
|
|
}
|
|
if (newSatoshis < satoshisLimits[0]) {
|
|
badSatoshisText = t('Must be more than {{minSats}}', { minSats: pn(satoshisLimits[0]) });
|
|
satoshis = satoshisLimits[0];
|
|
}
|
|
|
|
setMaker({
|
|
...maker,
|
|
satoshis,
|
|
badSatoshisText,
|
|
});
|
|
};
|
|
|
|
const handleClickRelative = function () {
|
|
setMaker({
|
|
...maker,
|
|
isExplicit: false,
|
|
});
|
|
};
|
|
|
|
const handleClickExplicit = function () {
|
|
if (!maker.advancedOptions) {
|
|
setMaker({
|
|
...maker,
|
|
isExplicit: true,
|
|
});
|
|
}
|
|
};
|
|
|
|
const handleCreateOrder = function () {
|
|
if (!disableRequest) {
|
|
setSubmittingRequest(true);
|
|
const body = {
|
|
type: fav.type == 0 ? 1 : 0,
|
|
currency: fav.currency == 0 ? 1 : fav.currency,
|
|
amount: maker.advancedOptions ? null : maker.amount,
|
|
has_range: maker.advancedOptions,
|
|
min_amount: maker.advancedOptions ? maker.minAmount : null,
|
|
max_amount: maker.advancedOptions ? maker.maxAmount : null,
|
|
payment_method:
|
|
maker.paymentMethodsText === '' ? 'not specified' : maker.paymentMethodsText,
|
|
is_explicit: maker.isExplicit,
|
|
premium: maker.isExplicit ? null : maker.premium == '' ? 0 : maker.premium,
|
|
satoshis: maker.isExplicit ? maker.satoshis : null,
|
|
public_duration: maker.publicDuration,
|
|
escrow_duration: maker.escrowDuration,
|
|
bond_size: maker.bondSize,
|
|
};
|
|
apiClient.post(baseUrl, '/api/make/', body).then((data: object) => {
|
|
setBadRequest(data.bad_request);
|
|
if (data.id) {
|
|
onOrderCreated(data.id);
|
|
}
|
|
setSubmittingRequest(false);
|
|
});
|
|
}
|
|
setOpenDialogs(false);
|
|
};
|
|
|
|
const handleChangePublicDuration = function (date: Date) {
|
|
const d = new Date(date);
|
|
const hours: number = d.getHours();
|
|
const minutes: number = d.getMinutes();
|
|
|
|
const total_secs: number = hours * 60 * 60 + minutes * 60;
|
|
|
|
setMaker({
|
|
...maker,
|
|
publicExpiryTime: date,
|
|
publicDuration: total_secs,
|
|
});
|
|
};
|
|
|
|
const handleChangeEscrowDuration = function (date: Date) {
|
|
const d = new Date(date);
|
|
const hours: number = d.getHours();
|
|
const minutes: number = d.getMinutes();
|
|
|
|
const total_secs: number = hours * 60 * 60 + minutes * 60;
|
|
|
|
setMaker({
|
|
...maker,
|
|
escrowExpiryTime: date,
|
|
escrowDuration: total_secs,
|
|
});
|
|
};
|
|
|
|
const handleClickAdvanced = function () {
|
|
if (maker.advancedOptions) {
|
|
handleClickRelative();
|
|
setMaker({ ...maker, advancedOptions: false });
|
|
} else {
|
|
resetRange(true);
|
|
}
|
|
};
|
|
|
|
const minAmountError = function () {
|
|
return (
|
|
maker.minAmount < amountLimits[0] * 0.99 ||
|
|
maker.maxAmount < maker.minAmount ||
|
|
maker.minAmount < maker.maxAmount / (maxRangeAmountMultiple + 0.15) ||
|
|
maker.minAmount * (minRangeAmountMultiple - 0.1) > maker.maxAmount
|
|
);
|
|
};
|
|
|
|
const maxAmountError = function () {
|
|
return (
|
|
maker.maxAmount > amountLimits[1] * 1.01 ||
|
|
maker.maxAmount < maker.minAmount ||
|
|
maker.minAmount < maker.maxAmount / (maxRangeAmountMultiple + 0.15) ||
|
|
maker.minAmount * (minRangeAmountMultiple - 0.1) > maker.maxAmount
|
|
);
|
|
};
|
|
|
|
const resetRange = function (advancedOptions: boolean) {
|
|
const index = fav.currency === 0 ? 1 : fav.currency;
|
|
const minAmount = maker.amount
|
|
? parseFloat((maker.amount / 2).toPrecision(2))
|
|
: parseFloat(Number(limits.list[index].max_amount * 0.25).toPrecision(2));
|
|
const maxAmount = maker.amount
|
|
? parseFloat(maker.amount)
|
|
: parseFloat(Number(limits.list[index].max_amount * 0.75).toPrecision(2));
|
|
|
|
setMaker({
|
|
...maker,
|
|
advancedOptions,
|
|
minAmount,
|
|
maxAmount,
|
|
});
|
|
};
|
|
|
|
const handleRangeAmountChange = function (e: any, newValue, activeThumb: number) {
|
|
let minAmount = e.target.value[0];
|
|
let maxAmount = e.target.value[1];
|
|
|
|
minAmount = Math.min(
|
|
(amountLimits[1] * amountSafeThresholds[1]) / minRangeAmountMultiple,
|
|
minAmount,
|
|
);
|
|
maxAmount = Math.max(
|
|
minRangeAmountMultiple * amountLimits[0] * amountSafeThresholds[0],
|
|
maxAmount,
|
|
);
|
|
|
|
if (minAmount > maxAmount / minRangeAmountMultiple) {
|
|
if (activeThumb === 0) {
|
|
maxAmount = minRangeAmountMultiple * minAmount;
|
|
} else {
|
|
minAmount = maxAmount / minRangeAmountMultiple;
|
|
}
|
|
} else if (minAmount < maxAmount / maxRangeAmountMultiple) {
|
|
if (activeThumb === 0) {
|
|
maxAmount = maxRangeAmountMultiple * minAmount;
|
|
} else {
|
|
minAmount = maxAmount / maxRangeAmountMultiple;
|
|
}
|
|
}
|
|
|
|
setMaker({
|
|
...maker,
|
|
minAmount: parseFloat(Number(minAmount).toPrecision(minAmount < 100 ? 2 : 3)),
|
|
maxAmount: parseFloat(Number(maxAmount).toPrecision(maxAmount < 100 ? 2 : 3)),
|
|
});
|
|
};
|
|
|
|
const disableSubmit = function () {
|
|
return (
|
|
fav.type == null ||
|
|
(maker.amount != '' &&
|
|
!maker.advancedOptions &&
|
|
(maker.amount < amountLimits[0] || maker.amount > amountLimits[1])) ||
|
|
(maker.amount == null && (!maker.advancedOptions || limits.loading)) ||
|
|
(maker.advancedOptions && (minAmountError() || maxAmountError())) ||
|
|
(maker.amount <= 0 && !maker.advancedOptions) ||
|
|
(maker.isExplicit && (maker.badSatoshisText != '' || maker.satoshis == '')) ||
|
|
(!maker.isExplicit && maker.badPremiumText != '')
|
|
);
|
|
};
|
|
|
|
const clearMaker = function () {
|
|
setFav({ ...fav, type: null });
|
|
setMaker(defaultMaker);
|
|
};
|
|
|
|
const SummaryText = function () {
|
|
return (
|
|
<Typography
|
|
component='h2'
|
|
variant='subtitle2'
|
|
align='center'
|
|
color={disableSubmit() ? 'text.secondary' : 'text.primary'}
|
|
>
|
|
{fav.type == null
|
|
? t('Order for ')
|
|
: fav.type == 1
|
|
? t('Buy order for ')
|
|
: t('Sell order for ')}
|
|
{maker.advancedOptions && maker.minAmount != ''
|
|
? pn(maker.minAmount) + '-' + pn(maker.maxAmount)
|
|
: pn(maker.amount)}
|
|
{' ' + currencyCode}
|
|
{maker.isExplicit
|
|
? t(' of {{satoshis}} Satoshis', { satoshis: pn(maker.satoshis) })
|
|
: maker.premium == 0
|
|
? t(' at market price')
|
|
: maker.premium > 0
|
|
? t(' at a {{premium}}% premium', { premium: maker.premium })
|
|
: t(' at a {{discount}}% discount', { discount: -maker.premium })}
|
|
</Typography>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<Box>
|
|
<ConfirmationDialog
|
|
open={openDialogs}
|
|
onClose={() => setOpenDialogs(false)}
|
|
setPage={setPage}
|
|
onClickDone={handleCreateOrder}
|
|
hasRobot={hasRobot}
|
|
/>
|
|
<Collapse in={limits.list.length == 0}>
|
|
<div style={{ display: limits.list.length == 0 ? '' : 'none' }}>
|
|
<LinearProgress />
|
|
</div>
|
|
</Collapse>
|
|
<Collapse in={!(limits.list.length == 0 || collapseAll)}>
|
|
<Grid container justifyContent='space-between' spacing={0} sx={{ maxHeight: '1em' }}>
|
|
<Grid item>
|
|
<IconButton
|
|
sx={{
|
|
width: '1.3em',
|
|
height: '1.3em',
|
|
position: 'relative',
|
|
bottom: '0.2em',
|
|
right: '0.2em',
|
|
color: 'text.secondary',
|
|
}}
|
|
onClick={clearMaker}
|
|
>
|
|
<Tooltip
|
|
placement='top'
|
|
enterTouchDelay={500}
|
|
enterDelay={700}
|
|
enterNextDelay={2000}
|
|
title={t('Clear form')}
|
|
>
|
|
<DeleteSweep sx={{ width: '1em', height: '1em' }} />
|
|
</Tooltip>
|
|
</IconButton>
|
|
</Grid>
|
|
<Grid item>
|
|
<Tooltip enterTouchDelay={0} placement='top' title={t('Enable advanced options')}>
|
|
<div
|
|
style={{
|
|
display: 'flex',
|
|
width: '4em',
|
|
height: '1.1em',
|
|
}}
|
|
>
|
|
<Switch
|
|
size='small'
|
|
disabled={limits.list.length == 0}
|
|
checked={maker.advancedOptions}
|
|
onChange={handleClickAdvanced}
|
|
/>
|
|
<SelfImprovement sx={{ color: 'text.secondary' }} />
|
|
</div>
|
|
</Tooltip>
|
|
</Grid>
|
|
</Grid>
|
|
</Collapse>
|
|
|
|
<Collapse in={!collapseAll}>
|
|
<Grid container spacing={1} justifyContent='center' alignItems='center'>
|
|
<Grid item>
|
|
<FormControl component='fieldset'>
|
|
<FormHelperText sx={{ textAlign: 'center' }}>
|
|
{t('Buy or Sell Bitcoin?')}
|
|
</FormHelperText>
|
|
<div style={{ textAlign: 'center' }}>
|
|
<ButtonGroup>
|
|
<Button
|
|
size={maker.advancedOptions ? 'small' : 'large'}
|
|
variant='contained'
|
|
onClick={() =>
|
|
setFav({
|
|
...fav,
|
|
type: 1,
|
|
})
|
|
}
|
|
disableElevation={fav.type == 1}
|
|
sx={{
|
|
backgroundColor: fav.type == 1 ? 'primary.main' : 'background.paper',
|
|
color: fav.type == 1 ? 'background.paper' : 'text.secondary',
|
|
':hover': {
|
|
color: 'background.paper',
|
|
},
|
|
}}
|
|
>
|
|
{t('Buy')}
|
|
</Button>
|
|
<Button
|
|
size={maker.advancedOptions ? 'small' : 'large'}
|
|
variant='contained'
|
|
onClick={() =>
|
|
setFav({
|
|
...fav,
|
|
type: 0,
|
|
})
|
|
}
|
|
disableElevation={fav.type == 0}
|
|
color='secondary'
|
|
sx={{
|
|
backgroundColor: fav.type == 0 ? 'secondary.main' : 'background.paper',
|
|
color: fav.type == 0 ? 'background.secondary' : 'text.secondary',
|
|
':hover': {
|
|
color: 'background.paper',
|
|
},
|
|
}}
|
|
>
|
|
{t('Sell')}
|
|
</Button>
|
|
</ButtonGroup>
|
|
</div>
|
|
</FormControl>
|
|
</Grid>
|
|
|
|
<Grid item>
|
|
<Collapse in={maker.advancedOptions}>
|
|
<AmountRange
|
|
minAmount={maker.minAmount}
|
|
handleRangeAmountChange={handleRangeAmountChange}
|
|
currency={fav.currency}
|
|
currencyCode={currencyCode}
|
|
handleCurrencyChange={handleCurrencyChange}
|
|
amountLimits={amountLimits}
|
|
maxAmount={maker.maxAmount}
|
|
minAmountError={minAmountError()}
|
|
maxAmountError={maxAmountError()}
|
|
handleMinAmountChange={handleMinAmountChange}
|
|
handleMaxAmountChange={handleMaxAmountChange}
|
|
/>
|
|
</Collapse>
|
|
<Collapse in={!maker.advancedOptions}>
|
|
<Grid item>
|
|
<Grid container alignItems='stretch' style={{ display: 'flex' }}>
|
|
<Grid item xs={6}>
|
|
<Tooltip
|
|
placement='top'
|
|
enterTouchDelay={500}
|
|
enterDelay={700}
|
|
enterNextDelay={2000}
|
|
title={t('Amount of fiat to exchange for bitcoin')}
|
|
>
|
|
<TextField
|
|
fullWidth
|
|
disabled={maker.advancedOptions}
|
|
variant={maker.advancedOptions ? 'filled' : 'outlined'}
|
|
error={
|
|
maker.amount != '' &&
|
|
(maker.amount < amountLimits[0] || maker.amount > amountLimits[1])
|
|
}
|
|
helperText={
|
|
maker.amount < amountLimits[0] && maker.amount != ''
|
|
? t('Must be more than {{minAmount}}', {
|
|
minAmount: pn(parseFloat(amountLimits[0].toPrecision(2))),
|
|
})
|
|
: maker.amount > amountLimits[1] && maker.amount != ''
|
|
? t('Must be less than {{maxAmount}}', {
|
|
maxAmount: pn(parseFloat(amountLimits[1].toPrecision(2))),
|
|
})
|
|
: null
|
|
}
|
|
label={t('Amount')}
|
|
required={true}
|
|
value={maker.amount}
|
|
type='number'
|
|
inputProps={{
|
|
min: 0,
|
|
style: {
|
|
textAlign: 'center',
|
|
backgroundColor: theme.palette.background.paper,
|
|
borderRadius: '4px',
|
|
},
|
|
}}
|
|
onChange={(e) => setMaker({ ...maker, amount: e.target.value })}
|
|
/>
|
|
</Tooltip>
|
|
</Grid>
|
|
|
|
<Grid item xs={6}>
|
|
<Select
|
|
fullWidth
|
|
sx={{ backgroundColor: theme.palette.background.paper, borderRadius: '4px' }}
|
|
required={true}
|
|
inputProps={{
|
|
style: { textAlign: 'center' },
|
|
}}
|
|
value={fav.currency == 0 ? 1 : fav.currency}
|
|
onChange={(e) => handleCurrencyChange(e.target.value)}
|
|
>
|
|
{Object.entries(currencyDict).map(([key, value]) => (
|
|
<MenuItem key={key} value={parseInt(key)}>
|
|
<div style={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap' }}>
|
|
<FlagWithProps code={value} />
|
|
{' ' + value}
|
|
</div>
|
|
</MenuItem>
|
|
))}
|
|
</Select>
|
|
</Grid>
|
|
</Grid>
|
|
</Grid>
|
|
</Collapse>
|
|
</Grid>
|
|
|
|
<Grid item xs={12}>
|
|
<AutocompletePayments
|
|
onAutocompleteChange={handlePaymentMethodChange}
|
|
// listBoxProps={{ sx: { width: '15.3em', maxHeight: '20em' } }}
|
|
optionsType={fav.currency == 1000 ? 'swap' : 'fiat'}
|
|
error={maker.badPaymentMethod}
|
|
helperText={maker.badPaymentMethod ? t('Must be shorter than 65 characters') : ''}
|
|
label={fav.currency == 1000 ? t('Swap Destination(s)') : t('Fiat Payment Method(s)')}
|
|
tooltipTitle={t(
|
|
'Enter your preferred fiat payment methods. Fast methods are highly recommended.',
|
|
)}
|
|
listHeaderText={t('You can add new methods')}
|
|
addNewButtonText={t('Add New')}
|
|
asFilter={false}
|
|
value={maker.paymentMethods}
|
|
/>
|
|
</Grid>
|
|
|
|
{!maker.advancedOptions && pricingMethods ? (
|
|
<Grid item xs={12}>
|
|
<Box
|
|
sx={{
|
|
padding: '0.5em',
|
|
backgroundColor: 'background.paper',
|
|
border: '1px solid',
|
|
borderRadius: '4px',
|
|
borderColor: theme.palette.mode === 'dark' ? '#434343' : '#c4c4c4',
|
|
'&:hover': {
|
|
borderColor: theme.palette.mode === 'dark' ? '#ffffff' : '#2f2f2f',
|
|
},
|
|
}}
|
|
>
|
|
<FormControl component='fieldset'>
|
|
<FormHelperText sx={{ textAlign: 'center', position: 'relative', top: '0.2em' }}>
|
|
{t('Choose a Pricing Method')}
|
|
</FormHelperText>
|
|
<RadioGroup row defaultValue='relative'>
|
|
<Tooltip
|
|
placement='top'
|
|
enterTouchDelay={0}
|
|
enterDelay={1000}
|
|
enterNextDelay={2000}
|
|
title={t('Let the price move with the market')}
|
|
>
|
|
<FormControlLabel
|
|
value='relative'
|
|
control={<Radio color='primary' />}
|
|
label={t('Relative')}
|
|
labelPlacement='end'
|
|
onClick={handleClickRelative}
|
|
/>
|
|
</Tooltip>
|
|
<Tooltip
|
|
placement='top'
|
|
enterTouchDelay={0}
|
|
enterDelay={1000}
|
|
enterNextDelay={2000}
|
|
title={t('Set a fix amount of satoshis')}
|
|
>
|
|
<FormControlLabel
|
|
disabled={maker.advancedOptions}
|
|
value='explicit'
|
|
control={<Radio color='secondary' />}
|
|
label={t('Exact')}
|
|
labelPlacement='end'
|
|
onClick={handleClickExplicit}
|
|
/>
|
|
</Tooltip>
|
|
</RadioGroup>
|
|
</FormControl>
|
|
</Box>
|
|
</Grid>
|
|
) : null}
|
|
<Grid item xs={12}>
|
|
<div style={{ display: maker.isExplicit ? '' : 'none' }}>
|
|
<TextField
|
|
fullWidth
|
|
label={t('Satoshis')}
|
|
error={maker.badSatoshisText != ''}
|
|
helperText={maker.badSatoshisText === '' ? null : maker.badSatoshisText}
|
|
type='number'
|
|
required={true}
|
|
value={maker.satoshis}
|
|
inputProps={{
|
|
min: satoshisLimits[0],
|
|
max: satoshisLimits[1],
|
|
style: {
|
|
textAlign: 'center',
|
|
backgroundColor: theme.palette.background.paper,
|
|
borderRadius: '4px',
|
|
},
|
|
}}
|
|
onChange={handleSatoshisChange}
|
|
/>
|
|
</div>
|
|
<div style={{ display: maker.isExplicit ? 'none' : '' }}>
|
|
<TextField
|
|
fullWidth
|
|
error={maker.badPremiumText != ''}
|
|
helperText={maker.badPremiumText === '' ? null : maker.badPremiumText}
|
|
label={t('Premium over Market (%)')}
|
|
type='number'
|
|
value={maker.premium}
|
|
inputProps={{
|
|
min: -100,
|
|
max: 999,
|
|
style: {
|
|
textAlign: 'center',
|
|
backgroundColor: theme.palette.background.paper,
|
|
borderRadius: '4px',
|
|
},
|
|
}}
|
|
onChange={handlePremiumChange}
|
|
/>
|
|
</div>
|
|
</Grid>
|
|
<Grid item>
|
|
<Collapse in={maker.advancedOptions}>
|
|
<Grid container spacing={1}>
|
|
<Grid item xs={12}>
|
|
<LocalizationProvider dateAdapter={DateFnsUtils}>
|
|
<TimePicker
|
|
ampm={false}
|
|
openTo='hours'
|
|
views={['hours', 'minutes']}
|
|
inputFormat='HH:mm'
|
|
mask='__:__'
|
|
components={{
|
|
OpenPickerIcon: HourglassTop,
|
|
}}
|
|
InputProps={{
|
|
endAdornment: (
|
|
<InputAdornment position='end'>
|
|
<HourglassTop />
|
|
</InputAdornment>
|
|
),
|
|
style: {
|
|
backgroundColor: theme.palette.background.paper,
|
|
borderRadius: '4px',
|
|
},
|
|
}}
|
|
renderInput={(props) => <TextField {...props} />}
|
|
label={t('Public Duration (HH:mm)')}
|
|
value={maker.publicExpiryTime}
|
|
onChange={handleChangePublicDuration}
|
|
minTime={new Date(0, 0, 0, 0, 10)}
|
|
maxTime={new Date(0, 0, 0, 23, 59)}
|
|
/>
|
|
</LocalizationProvider>
|
|
</Grid>
|
|
|
|
<Grid item xs={12}>
|
|
<LocalizationProvider dateAdapter={DateFnsUtils}>
|
|
<TimePicker
|
|
ampm={false}
|
|
openTo='hours'
|
|
views={['hours', 'minutes']}
|
|
inputFormat='HH:mm'
|
|
mask='__:__'
|
|
components={{
|
|
OpenPickerIcon: HourglassTop,
|
|
}}
|
|
InputProps={{
|
|
endAdornment: (
|
|
<InputAdornment position='end'>
|
|
<HourglassTop />
|
|
</InputAdornment>
|
|
),
|
|
style: {
|
|
backgroundColor: theme.palette.background.paper,
|
|
borderRadius: '4px',
|
|
},
|
|
}}
|
|
renderInput={(props) => <TextField {...props} />}
|
|
label={t('Escrow/Invoice Timer (HH:mm)')}
|
|
value={maker.escrowExpiryTime}
|
|
onChange={handleChangeEscrowDuration}
|
|
minTime={new Date(0, 0, 0, 1, 0)}
|
|
maxTime={new Date(0, 0, 0, 8, 0)}
|
|
/>
|
|
</LocalizationProvider>
|
|
</Grid>
|
|
|
|
<Grid item xs={12}>
|
|
<Box
|
|
sx={{
|
|
padding: '0.5em',
|
|
backgroundColor: 'background.paper',
|
|
border: '1px solid',
|
|
borderRadius: '4px',
|
|
borderColor: theme.palette.mode === 'dark' ? '#434343' : '#c4c4c4',
|
|
'&:hover': {
|
|
borderColor: theme.palette.mode === 'dark' ? '#ffffff' : '#2f2f2f',
|
|
},
|
|
}}
|
|
>
|
|
<Grid container direction='column' alignItems='center' spacing={0.5}>
|
|
<Grid
|
|
item
|
|
sx={{
|
|
width: '100%',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
flexWrap: 'wrap',
|
|
}}
|
|
>
|
|
<Tooltip
|
|
enterDelay={800}
|
|
enterTouchDelay={0}
|
|
placement='top'
|
|
title={t(
|
|
'Set the skin-in-the-game, increase for higher safety assurance',
|
|
)}
|
|
>
|
|
<Typography
|
|
variant='caption'
|
|
sx={{
|
|
color: 'text.secondary',
|
|
display: 'flex',
|
|
flexWrap: 'wrap',
|
|
}}
|
|
>
|
|
{t('Fidelity Bond Size')}{' '}
|
|
<Lock sx={{ height: '0.8em', width: '0.8em' }} />
|
|
</Typography>
|
|
</Tooltip>
|
|
</Grid>
|
|
<Grid item sx={{ width: 'calc(100% - 2em)' }}>
|
|
<Slider
|
|
sx={{ width: '100%', align: 'center' }}
|
|
aria-label='Bond Size (%)'
|
|
defaultValue={3}
|
|
value={maker.bondSize}
|
|
valueLabelDisplay='auto'
|
|
valueLabelFormat={(x: string) => x + '%'}
|
|
step={0.25}
|
|
marks={[
|
|
{ value: 2, label: '2%' },
|
|
{ value: 5, label: '5%' },
|
|
{ value: 10, label: '10%' },
|
|
{ value: 15, label: '15%' },
|
|
]}
|
|
min={2}
|
|
max={15}
|
|
onChange={(e) => setMaker({ ...maker, bondSize: e.target.value })}
|
|
/>
|
|
</Grid>
|
|
</Grid>
|
|
</Box>
|
|
</Grid>
|
|
</Grid>
|
|
</Collapse>
|
|
</Grid>
|
|
</Grid>
|
|
</Collapse>
|
|
|
|
<Grid container direction='column' alignItems='center'>
|
|
<Grid item>
|
|
<SummaryText />
|
|
</Grid>
|
|
|
|
<Grid item>
|
|
<Grid container direction='row' justifyItems='center' alignItems='center' spacing={1}>
|
|
<Grid item>
|
|
{/* conditions to disable the make button */}
|
|
{disableSubmit() ? (
|
|
<Tooltip enterTouchDelay={0} title={t('You must fill the form correctly')}>
|
|
<div>
|
|
<Button disabled color='primary' variant='contained'>
|
|
{t(submitButtonLabel)}
|
|
</Button>
|
|
</div>
|
|
</Tooltip>
|
|
) : (
|
|
<LoadingButton
|
|
loading={submittingRequest}
|
|
color='primary'
|
|
variant='contained'
|
|
onClick={() => {
|
|
disableRequest ? onSubmit() : setOpenDialogs(true);
|
|
}}
|
|
>
|
|
{t(submitButtonLabel)}
|
|
</LoadingButton>
|
|
)}
|
|
</Grid>
|
|
{collapseAll ? (
|
|
<Grid item>
|
|
<Collapse in={collapseAll} orientation='vertical'>
|
|
<IconButton onClick={onReset}>
|
|
<Tooltip
|
|
placement='top'
|
|
enterTouchDelay={500}
|
|
enterDelay={700}
|
|
enterNextDelay={2000}
|
|
title={t('Edit order')}
|
|
>
|
|
<Edit sx={{ width: '1.5em', height: '1.5em' }} />
|
|
</Tooltip>
|
|
</IconButton>
|
|
</Collapse>
|
|
</Grid>
|
|
) : null}
|
|
</Grid>
|
|
</Grid>
|
|
|
|
<Grid item>
|
|
<Typography align='center' component='h2' variant='subtitle2' color='secondary'>
|
|
{badRequest}
|
|
</Typography>
|
|
</Grid>
|
|
|
|
<Collapse in={!(limits.list.length == 0)}>
|
|
<Tooltip
|
|
placement='top'
|
|
enterTouchDelay={0}
|
|
enterDelay={1000}
|
|
enterNextDelay={2000}
|
|
title={
|
|
maker.isExplicit
|
|
? t('Your order fixed exchange rate')
|
|
: t("Your order's current exchange rate. Rate will move with the market.")
|
|
}
|
|
>
|
|
<Typography align='center' variant='caption' color='text.secondary'>
|
|
{(maker.isExplicit ? t('Order rate:') : t('Order current rate:')) +
|
|
` ${pn(currentPrice)} ${currencyCode}/BTC`}
|
|
</Typography>
|
|
</Tooltip>
|
|
</Collapse>
|
|
</Grid>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
export default MakerForm;
|