mirror of
https://github.com/RoboSats/robosats.git
synced 2025-09-13 00:56:22 +00:00
Frontend
This commit is contained in:
12
api/views.py
12
api/views.py
@ -241,13 +241,7 @@ class OrderView(viewsets.ViewSet):
|
||||
data["is_maker"] = order.maker == request.user
|
||||
data["is_taker"] = order.taker == request.user or take_order.exists()
|
||||
data["is_participant"] = data["is_maker"] or data["is_taker"]
|
||||
|
||||
# 1) If order has a password
|
||||
if not data["is_participant"] and order.password is not None:
|
||||
return Response(
|
||||
{"bad_request": "This order is password protected"},
|
||||
status.HTTP_403_FORBIDDEN,
|
||||
)
|
||||
data["has_password"] = order.password is not None
|
||||
|
||||
# 2) If order has been cancelled
|
||||
if order.status == Order.Status.UCA:
|
||||
@ -268,6 +262,10 @@ class OrderView(viewsets.ViewSet):
|
||||
if is_penalized:
|
||||
data["penalty"] = request.user.robot.penalty_expiration
|
||||
|
||||
# 1) If order has a password
|
||||
if not data["is_participant"] and order.password is not None:
|
||||
return Response(data, status.HTTP_200_OK)
|
||||
|
||||
# 3.a) If not a participant and order is not public, forbid.
|
||||
if (
|
||||
order.maker != request.user
|
||||
|
||||
@ -80,6 +80,7 @@ const OrderPage = (): React.JSX.Element => {
|
||||
<OrderDetails
|
||||
shortAlias={String(currentOrder.shortAlias)}
|
||||
currentOrder={currentOrder}
|
||||
setCurrentOrder={setCurrentOrder}
|
||||
onClickCoordinator={onClickCoordinator}
|
||||
/>
|
||||
) : (
|
||||
@ -118,11 +119,27 @@ const OrderPage = (): React.JSX.Element => {
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{!currentOrder?.maker_hash_id && <CircularProgress />}
|
||||
{!currentOrder?.maker_hash_id && !currentOrder?.bad_request && <CircularProgress />}
|
||||
{currentOrder?.bad_request && currentOrder.status !== 5 ? (
|
||||
<Typography align='center' variant='subtitle2' color='secondary'>
|
||||
{t(currentOrder.bad_request)}
|
||||
</Typography>
|
||||
<>
|
||||
<Typography align='center' variant='subtitle2' color='secondary'>
|
||||
{t(currentOrder.bad_request)}
|
||||
</Typography>
|
||||
{currentOrder?.bad_request?.includes('password') && (
|
||||
<Grid item xs={6} style={{ width: '21em' }}>
|
||||
<Paper
|
||||
elevation={12}
|
||||
style={{
|
||||
width: '21em',
|
||||
maxHeight: `${maxHeight}em`,
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
{orderDetailsSpace}
|
||||
</Paper>
|
||||
</Grid>
|
||||
)}
|
||||
</>
|
||||
) : null}
|
||||
{currentOrder?.maker_hash_id && (!currentOrder.bad_request || currentOrder.status === 5) ? (
|
||||
currentOrder.is_participant ? (
|
||||
|
||||
@ -41,6 +41,7 @@ import SelectCoordinator from './SelectCoordinator';
|
||||
import { FederationContext, type UseFederationStoreType } from '../../contexts/FederationContext';
|
||||
import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { sha256 } from 'js-sha256';
|
||||
|
||||
interface MakerFormProps {
|
||||
disableRequest?: boolean;
|
||||
@ -252,6 +253,7 @@ const MakerForm = ({
|
||||
latitude: maker.latitude,
|
||||
longitude: maker.longitude,
|
||||
shortAlias: maker.coordinator,
|
||||
password: maker.password ? sha256(maker.password) : null,
|
||||
};
|
||||
|
||||
void slot
|
||||
@ -286,6 +288,13 @@ const MakerForm = ({
|
||||
});
|
||||
};
|
||||
|
||||
const handlePasswordChange = function (event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setMaker({
|
||||
...maker,
|
||||
password: event.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
const handleChangeEscrowDuration = function (date: Date): void {
|
||||
const d = new Date(date);
|
||||
const hours: number = d.getHours();
|
||||
@ -821,6 +830,24 @@ const MakerForm = ({
|
||||
/>
|
||||
</Grid>
|
||||
<Collapse in={maker.advancedOptions} sx={{ width: '100%' }}>
|
||||
<Grid item sx={{ width: '100%' }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
label={`${t('Password')}`}
|
||||
type='password'
|
||||
value={maker.password}
|
||||
style={{ marginBottom: 8 }}
|
||||
inputProps={{
|
||||
style: {
|
||||
textAlign: 'center',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
borderRadius: 4,
|
||||
},
|
||||
}}
|
||||
onChange={handlePasswordChange}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item sx={{ width: '100%' }}>
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<MobileTimePicker
|
||||
|
||||
@ -27,10 +27,13 @@ import { computeSats } from '../../utils';
|
||||
import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext';
|
||||
import { type UseFederationStoreType, FederationContext } from '../../contexts/FederationContext';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { sha256 } from 'js-sha256';
|
||||
|
||||
interface TakeButtonProps {
|
||||
currentOrder: Order;
|
||||
password?: string;
|
||||
info?: Info;
|
||||
setCurrentOrder: (currentOrder: Order) => void;
|
||||
onClickGenerateRobot?: () => void;
|
||||
}
|
||||
|
||||
@ -41,7 +44,9 @@ interface OpenDialogsProps {
|
||||
const closeAll = { inactiveMaker: false, confirmation: false };
|
||||
|
||||
const TakeButton = ({
|
||||
password,
|
||||
currentOrder,
|
||||
setCurrentOrder,
|
||||
info,
|
||||
onClickGenerateRobot = () => null,
|
||||
}: TakeButtonProps): React.JSX.Element => {
|
||||
@ -317,6 +322,8 @@ const TakeButton = ({
|
||||
|
||||
if (currentOrder === null || slot === null) return;
|
||||
|
||||
if (password) currentOrder.password = sha256(password);
|
||||
|
||||
setLoadingTake(true);
|
||||
|
||||
slot
|
||||
@ -326,8 +333,10 @@ const TakeButton = ({
|
||||
setBadRequest(order.bad_request);
|
||||
} else {
|
||||
setBadRequest('');
|
||||
setCurrentOrder(order);
|
||||
navigate(`/order/${order.shortAlias}/${order.id}`);
|
||||
}
|
||||
setLoadingTake(false);
|
||||
})
|
||||
.catch(() => {
|
||||
setBadRequest('Request error');
|
||||
|
||||
@ -16,6 +16,7 @@ import {
|
||||
IconButton,
|
||||
Tooltip,
|
||||
ListItemButton,
|
||||
TextField,
|
||||
} from '@mui/material';
|
||||
|
||||
import Countdown, { type CountdownRenderProps, zeroPad } from 'react-countdown';
|
||||
@ -46,6 +47,7 @@ import { type Order } from '../../models';
|
||||
interface OrderDetailsProps {
|
||||
shortAlias: string;
|
||||
currentOrder: Order;
|
||||
setCurrentOrder: (currentOrder: Order) => void;
|
||||
onClickCoordinator?: () => void;
|
||||
onClickGenerateRobot?: () => void;
|
||||
}
|
||||
@ -53,6 +55,7 @@ interface OrderDetailsProps {
|
||||
const OrderDetails = ({
|
||||
shortAlias,
|
||||
currentOrder,
|
||||
setCurrentOrder,
|
||||
onClickCoordinator = () => null,
|
||||
onClickGenerateRobot = () => null,
|
||||
}: OrderDetailsProps): React.JSX.Element => {
|
||||
@ -65,6 +68,7 @@ const OrderDetails = ({
|
||||
const [currencyCode, setCurrencyCode] = useState<string | null>();
|
||||
const [showSatsDetails, setShowSatsDetails] = useState<boolean>(false);
|
||||
const [openWorldmap, setOpenWorldmap] = useState<boolean>(false);
|
||||
const [password, setPassword] = useState<string>();
|
||||
|
||||
useEffect(() => {
|
||||
setCoordinator(federation.getCoordinator(shortAlias));
|
||||
@ -142,6 +146,10 @@ const OrderDetails = ({
|
||||
);
|
||||
};
|
||||
|
||||
const onPasswordChange = (password: string) => {
|
||||
setPassword(password);
|
||||
};
|
||||
|
||||
// Countdown Renderer callback with condition
|
||||
const countdownPenaltyRenderer = ({
|
||||
minutes,
|
||||
@ -294,234 +302,239 @@ const OrderDetails = ({
|
||||
)}
|
||||
|
||||
<Divider />
|
||||
|
||||
<ListItem>
|
||||
<ListItemAvatar sx={{ width: '4em', height: '4em' }}>
|
||||
<RobotAvatar
|
||||
statusColor={statusBadgeColor(currentOrder?.maker_status ?? '')}
|
||||
hashId={currentOrder?.maker_hash_id}
|
||||
tooltip={t(currentOrder?.maker_status ?? '')}
|
||||
orderType={currentOrder?.type}
|
||||
small={true}
|
||||
/>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={`${String(currentOrder?.maker_nick)} (${
|
||||
currentOrder?.type === 1
|
||||
? t(currentOrder?.currency === 1000 ? 'Swapping Out' : 'Seller')
|
||||
: t(currentOrder?.currency === 1000 ? 'Swapping In' : 'Buyer')
|
||||
})`}
|
||||
secondary={t('Order maker')}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
<Collapse in={currentOrder?.is_participant && currentOrder?.taker_nick !== 'None'}>
|
||||
<Divider />
|
||||
<ListItem>
|
||||
<ListItemText
|
||||
primary={`${String(currentOrder?.taker_nick)} (${
|
||||
currentOrder?.type === 1
|
||||
? t(currentOrder?.currency === 1000 ? 'Swapping In' : 'Buyer')
|
||||
: t(currentOrder?.currency === 1000 ? 'Swapping Out' : 'Seller')
|
||||
})`}
|
||||
secondary={t('Order taker')}
|
||||
/>
|
||||
<ListItemAvatar>
|
||||
<RobotAvatar
|
||||
avatarClass='smallAvatar'
|
||||
statusColor={statusBadgeColor(currentOrder?.taker_status ?? '')}
|
||||
hashId={
|
||||
currentOrder?.taker_hash_id === 'None' ? undefined : currentOrder?.taker_hash_id
|
||||
}
|
||||
tooltip={t(currentOrder?.taker_status ?? '')}
|
||||
orderType={currentOrder?.type === 0 ? 1 : 0}
|
||||
small={true}
|
||||
/>
|
||||
</ListItemAvatar>
|
||||
</ListItem>
|
||||
</Collapse>
|
||||
<Divider>
|
||||
<Chip label={t('Order Details')} />
|
||||
</Divider>
|
||||
|
||||
<Collapse in={currentOrder?.is_participant}>
|
||||
<ListItem>
|
||||
<ListItemIcon>
|
||||
<Article />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={t(currentOrder?.status_message ?? '')}
|
||||
secondary={t('Order status')}
|
||||
/>
|
||||
</ListItem>
|
||||
<Divider />
|
||||
</Collapse>
|
||||
|
||||
<ListItem>
|
||||
<ListItemIcon>
|
||||
<div
|
||||
style={{
|
||||
zoom: 1.25,
|
||||
opacity: 0.7,
|
||||
msZoom: 1.25,
|
||||
WebkitZoom: 1.25,
|
||||
MozTransform: 'scale(1.25,1.25)',
|
||||
MozTransformOrigin: 'left center',
|
||||
}}
|
||||
>
|
||||
<FlagWithProps code={currencyCode} width='1.2em' height='1.2em' />
|
||||
</div>
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={amountString}
|
||||
secondary={(currentOrder?.amount ?? 0) > 0 ? 'Amount' : 'Amount Range'}
|
||||
/>
|
||||
<ListItemIcon>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setShowSatsDetails(!showSatsDetails);
|
||||
}}
|
||||
>
|
||||
{showSatsDetails ? <ExpandLess /> : <ExpandMore color='primary' />}
|
||||
</IconButton>
|
||||
</ListItemIcon>
|
||||
</ListItem>
|
||||
|
||||
<Collapse in={showSatsDetails}>
|
||||
<List dense={true} sx={{ position: 'relative', bottom: '0.5em' }}>
|
||||
{!currentOrder.bad_request && (
|
||||
<>
|
||||
<ListItem>
|
||||
<ListItemIcon sx={{ position: 'relative', left: '0.3em' }}>
|
||||
<SendReceiveIcon
|
||||
sx={{ transform: 'scaleX(-1)', width: '0.9em', opacity: 0.9 }}
|
||||
color='secondary'
|
||||
<ListItemAvatar sx={{ width: '4em', height: '4em' }}>
|
||||
<RobotAvatar
|
||||
statusColor={statusBadgeColor(currentOrder?.maker_status ?? '')}
|
||||
hashId={currentOrder?.maker_hash_id}
|
||||
tooltip={t(currentOrder?.maker_status ?? '')}
|
||||
orderType={currentOrder?.type}
|
||||
small={true}
|
||||
/>
|
||||
</ListItemIcon>
|
||||
<Typography variant='body2'>{satsSummary.send}</Typography>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={`${String(currentOrder?.maker_nick)} (${
|
||||
currentOrder?.type === 1
|
||||
? t(currentOrder?.currency === 1000 ? 'Swapping Out' : 'Seller')
|
||||
: t(currentOrder?.currency === 1000 ? 'Swapping In' : 'Buyer')
|
||||
})`}
|
||||
secondary={t('Order maker')}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
<ListItem>
|
||||
<ListItemIcon sx={{ position: 'relative', left: '0.3em' }}>
|
||||
<SendReceiveIcon
|
||||
sx={{ left: '0.1em', width: '0.9em', opacity: 0.9 }}
|
||||
color='primary'
|
||||
<Collapse in={currentOrder?.is_participant && currentOrder?.taker_nick !== 'None'}>
|
||||
<Divider />
|
||||
<ListItem>
|
||||
<ListItemText
|
||||
primary={`${String(currentOrder?.taker_nick)} (${
|
||||
currentOrder?.type === 1
|
||||
? t(currentOrder?.currency === 1000 ? 'Swapping In' : 'Buyer')
|
||||
: t(currentOrder?.currency === 1000 ? 'Swapping Out' : 'Seller')
|
||||
})`}
|
||||
secondary={t('Order taker')}
|
||||
/>
|
||||
</ListItemIcon>
|
||||
<Typography variant='body2'>{satsSummary.receive}</Typography>
|
||||
</ListItem>
|
||||
</List>
|
||||
</Collapse>
|
||||
<ListItemAvatar>
|
||||
<RobotAvatar
|
||||
avatarClass='smallAvatar'
|
||||
statusColor={statusBadgeColor(currentOrder?.taker_status ?? '')}
|
||||
hashId={
|
||||
currentOrder?.taker_hash_id === 'None'
|
||||
? undefined
|
||||
: currentOrder?.taker_hash_id
|
||||
}
|
||||
tooltip={t(currentOrder?.taker_status ?? '')}
|
||||
orderType={currentOrder?.type === 0 ? 1 : 0}
|
||||
small={true}
|
||||
/>
|
||||
</ListItemAvatar>
|
||||
</ListItem>
|
||||
</Collapse>
|
||||
<Divider>
|
||||
<Chip label={t('Order Details')} />
|
||||
</Divider>
|
||||
|
||||
<Divider />
|
||||
<Collapse in={currentOrder?.is_participant}>
|
||||
<ListItem>
|
||||
<ListItemIcon>
|
||||
<Article />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={t(currentOrder?.status_message ?? '')}
|
||||
secondary={t('Order status')}
|
||||
/>
|
||||
</ListItem>
|
||||
<Divider />
|
||||
</Collapse>
|
||||
|
||||
<ListItem>
|
||||
<ListItemIcon>
|
||||
<Payments />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={
|
||||
<PaymentStringAsIcons
|
||||
size={1.42 * theme.typography.fontSize}
|
||||
othersText={t('Others')}
|
||||
verbose={true}
|
||||
text={currentOrder?.payment_method}
|
||||
/>
|
||||
}
|
||||
secondary={
|
||||
currentOrder?.currency === 1000
|
||||
? t('Swap destination')
|
||||
: t('Accepted payment methods')
|
||||
}
|
||||
/>
|
||||
{currentOrder?.payment_method.includes('Cash F2F') && (
|
||||
<ListItemIcon>
|
||||
<Tooltip enterTouchDelay={0} title={t('F2F location')}>
|
||||
<div>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setOpenWorldmap(true);
|
||||
}}
|
||||
>
|
||||
<Map />
|
||||
</IconButton>
|
||||
<ListItem>
|
||||
<ListItemIcon>
|
||||
<div
|
||||
style={{
|
||||
zoom: 1.25,
|
||||
opacity: 0.7,
|
||||
msZoom: 1.25,
|
||||
WebkitZoom: 1.25,
|
||||
MozTransform: 'scale(1.25,1.25)',
|
||||
MozTransformOrigin: 'left center',
|
||||
}}
|
||||
>
|
||||
<FlagWithProps code={currencyCode} width='1.2em' height='1.2em' />
|
||||
</div>
|
||||
</Tooltip>
|
||||
</ListItemIcon>
|
||||
)}
|
||||
</ListItem>
|
||||
<Divider />
|
||||
|
||||
{/* If there is live Price and Premium data, show it. Otherwise show the order maker settings */}
|
||||
<ListItem>
|
||||
<ListItemIcon>
|
||||
<PriceChange />
|
||||
</ListItemIcon>
|
||||
|
||||
{currentOrder?.price_now !== undefined ? (
|
||||
<ListItemText
|
||||
primary={t('{{price}} {{currencyCode}}/BTC - Premium: {{premium}}%', {
|
||||
price: pn(currentOrder?.price_now),
|
||||
currencyCode,
|
||||
premium: currentOrder?.premium_now,
|
||||
})}
|
||||
secondary={t('Price and Premium')}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{currentOrder?.price_now === undefined && currentOrder?.is_explicit ? (
|
||||
<ListItemText
|
||||
primary={pn(currentOrder?.satoshis)}
|
||||
secondary={t('Amount of Satoshis')}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{currentOrder?.price_now === undefined && !currentOrder?.is_explicit ? (
|
||||
<ListItemText
|
||||
primary={`${parseFloat(Number(currentOrder?.premium).toFixed(2))}%`}
|
||||
secondary={t('Premium over market price')}
|
||||
/>
|
||||
) : null}
|
||||
</ListItem>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Grid container direction='row' justifyContent='center' alignItems='center'>
|
||||
<ListItem style={{ width: '50%' }}>
|
||||
<ListItemIcon>
|
||||
<Numbers />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={currentOrder?.id} secondary={t('Order ID')} />
|
||||
</ListItem>
|
||||
|
||||
<ListItem style={{ width: '50%' }}>
|
||||
<ListItemIcon>
|
||||
<HourglassTop />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={timerRenderer(currentOrder?.escrow_duration)}
|
||||
secondary={t('Deposit')}
|
||||
/>
|
||||
</ListItem>
|
||||
</Grid>
|
||||
|
||||
{/* if order is in a status that does not expire, do not show countdown */}
|
||||
<Collapse in={![4, 5, 12, 13, 14, 15, 16, 17, 18].includes(currentOrder?.status ?? 0)}>
|
||||
<Divider />
|
||||
<ListItem>
|
||||
<ListItemIcon>
|
||||
<AccessTime />
|
||||
</ListItemIcon>
|
||||
<ListItemText secondary={t('Expires in')}>
|
||||
<Countdown
|
||||
date={new Date(currentOrder?.expires_at ?? '')}
|
||||
renderer={countdownRenderer}
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={amountString}
|
||||
secondary={(currentOrder?.amount ?? 0) > 0 ? 'Amount' : 'Amount Range'}
|
||||
/>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<LinearDeterminate
|
||||
totalSecsExp={currentOrder?.total_secs_exp ?? 0}
|
||||
expiresAt={currentOrder?.expires_at ?? ''}
|
||||
/>
|
||||
</Collapse>
|
||||
<ListItemIcon>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setShowSatsDetails(!showSatsDetails);
|
||||
}}
|
||||
>
|
||||
{showSatsDetails ? <ExpandLess /> : <ExpandMore color='primary' />}
|
||||
</IconButton>
|
||||
</ListItemIcon>
|
||||
</ListItem>
|
||||
|
||||
<Collapse in={showSatsDetails}>
|
||||
<List dense={true} sx={{ position: 'relative', bottom: '0.5em' }}>
|
||||
<ListItem>
|
||||
<ListItemIcon sx={{ position: 'relative', left: '0.3em' }}>
|
||||
<SendReceiveIcon
|
||||
sx={{ transform: 'scaleX(-1)', width: '0.9em', opacity: 0.9 }}
|
||||
color='secondary'
|
||||
/>
|
||||
</ListItemIcon>
|
||||
<Typography variant='body2'>{satsSummary.send}</Typography>
|
||||
</ListItem>
|
||||
|
||||
<ListItem>
|
||||
<ListItemIcon sx={{ position: 'relative', left: '0.3em' }}>
|
||||
<SendReceiveIcon
|
||||
sx={{ left: '0.1em', width: '0.9em', opacity: 0.9 }}
|
||||
color='primary'
|
||||
/>
|
||||
</ListItemIcon>
|
||||
<Typography variant='body2'>{satsSummary.receive}</Typography>
|
||||
</ListItem>
|
||||
</List>
|
||||
</Collapse>
|
||||
|
||||
<Divider />
|
||||
|
||||
<ListItem>
|
||||
<ListItemIcon>
|
||||
<Payments />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={
|
||||
<PaymentStringAsIcons
|
||||
size={1.42 * theme.typography.fontSize}
|
||||
othersText={t('Others')}
|
||||
verbose={true}
|
||||
text={currentOrder?.payment_method}
|
||||
/>
|
||||
}
|
||||
secondary={
|
||||
currentOrder?.currency === 1000
|
||||
? t('Swap destination')
|
||||
: t('Accepted payment methods')
|
||||
}
|
||||
/>
|
||||
{currentOrder?.payment_method.includes('Cash F2F') && (
|
||||
<ListItemIcon>
|
||||
<Tooltip enterTouchDelay={0} title={t('F2F location')}>
|
||||
<div>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setOpenWorldmap(true);
|
||||
}}
|
||||
>
|
||||
<Map />
|
||||
</IconButton>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</ListItemIcon>
|
||||
)}
|
||||
</ListItem>
|
||||
<Divider />
|
||||
|
||||
{/* If there is live Price and Premium data, show it. Otherwise show the order maker settings */}
|
||||
<ListItem>
|
||||
<ListItemIcon>
|
||||
<PriceChange />
|
||||
</ListItemIcon>
|
||||
|
||||
{currentOrder?.price_now !== undefined ? (
|
||||
<ListItemText
|
||||
primary={t('{{price}} {{currencyCode}}/BTC - Premium: {{premium}}%', {
|
||||
price: pn(currentOrder?.price_now),
|
||||
currencyCode,
|
||||
premium: currentOrder?.premium_now,
|
||||
})}
|
||||
secondary={t('Price and Premium')}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{currentOrder?.price_now === undefined && currentOrder?.is_explicit ? (
|
||||
<ListItemText
|
||||
primary={pn(currentOrder?.satoshis)}
|
||||
secondary={t('Amount of Satoshis')}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{currentOrder?.price_now === undefined && !currentOrder?.is_explicit ? (
|
||||
<ListItemText
|
||||
primary={`${parseFloat(Number(currentOrder?.premium).toFixed(2))}%`}
|
||||
secondary={t('Premium over market price')}
|
||||
/>
|
||||
) : null}
|
||||
</ListItem>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Grid container direction='row' justifyContent='center' alignItems='center'>
|
||||
<ListItem style={{ width: '50%' }}>
|
||||
<ListItemIcon>
|
||||
<Numbers />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={currentOrder?.id} secondary={t('Order ID')} />
|
||||
</ListItem>
|
||||
|
||||
<ListItem style={{ width: '50%' }}>
|
||||
<ListItemIcon>
|
||||
<HourglassTop />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={timerRenderer(currentOrder?.escrow_duration)}
|
||||
secondary={t('Deposit')}
|
||||
/>
|
||||
</ListItem>
|
||||
</Grid>
|
||||
|
||||
{/* if order is in a status that does not expire, do not show countdown */}
|
||||
<Collapse in={![4, 5, 12, 13, 14, 15, 16, 17, 18].includes(currentOrder?.status ?? 0)}>
|
||||
<Divider />
|
||||
<ListItem>
|
||||
<ListItemIcon>
|
||||
<AccessTime />
|
||||
</ListItemIcon>
|
||||
<ListItemText secondary={t('Expires in')}>
|
||||
<Countdown
|
||||
date={new Date(currentOrder?.expires_at ?? '')}
|
||||
renderer={countdownRenderer}
|
||||
/>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
<LinearDeterminate
|
||||
totalSecsExp={currentOrder?.total_secs_exp ?? 0}
|
||||
expiresAt={currentOrder?.expires_at ?? ''}
|
||||
/>
|
||||
</Collapse>
|
||||
</>
|
||||
)}
|
||||
</List>
|
||||
|
||||
{/* If the user has a penalty/limit */}
|
||||
@ -538,10 +551,32 @@ const OrderDetails = ({
|
||||
<></>
|
||||
)}
|
||||
|
||||
{!currentOrder?.is_participant ? (
|
||||
{currentOrder.bad_request?.includes('password') && (
|
||||
<Grid item style={{ width: '100%', padding: '16px' }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
label={`${t('Password')}`}
|
||||
type='password'
|
||||
value={password}
|
||||
style={{ marginBottom: 8 }}
|
||||
inputProps={{
|
||||
style: {
|
||||
textAlign: 'center',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
borderRadius: 4,
|
||||
},
|
||||
}}
|
||||
onChange={(e) => onPasswordChange(e.target.value)}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
{!currentOrder?.is_participant || currentOrder.bad_request?.includes('password') ? (
|
||||
<Grid item style={{ width: '100%', padding: '8px' }}>
|
||||
<TakeButton
|
||||
password={password}
|
||||
currentOrder={currentOrder}
|
||||
setCurrentOrder={setCurrentOrder}
|
||||
info={coordinator.info}
|
||||
onClickGenerateRobot={onClickGenerateRobot}
|
||||
/>
|
||||
|
||||
@ -21,6 +21,7 @@ export interface Maker {
|
||||
badPremiumText: string;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
password: string | null;
|
||||
}
|
||||
|
||||
export const defaultMaker: Maker = {
|
||||
@ -47,6 +48,7 @@ export const defaultMaker: Maker = {
|
||||
badSatoshisText: '',
|
||||
latitude: 0,
|
||||
longitude: 0,
|
||||
password: null,
|
||||
};
|
||||
|
||||
export default Maker;
|
||||
|
||||
@ -22,6 +22,7 @@ export interface SubmitActionProps {
|
||||
rating?: number;
|
||||
amount?: number;
|
||||
cancel_status?: number;
|
||||
password?: string;
|
||||
}
|
||||
|
||||
export interface TradeRobotSummary {
|
||||
@ -83,6 +84,7 @@ class Order {
|
||||
satoshis_now: number = 0;
|
||||
latitude: number = 0;
|
||||
longitude: number = 0;
|
||||
password: string | undefined = undefined;
|
||||
premium_now: number | undefined = undefined;
|
||||
premium_percentile: number = 0;
|
||||
num_similar_orders: number = 0;
|
||||
@ -199,6 +201,7 @@ class Order {
|
||||
bond_size: this.bond_size,
|
||||
latitude: this.latitude,
|
||||
longitude: this.longitude,
|
||||
password: this.password,
|
||||
};
|
||||
|
||||
if (slot) {
|
||||
@ -223,6 +226,7 @@ class Order {
|
||||
) => {
|
||||
return await this.submitAction(federation, slot, {
|
||||
action: 'take',
|
||||
password: this?.password,
|
||||
amount: this?.currency === 1000 ? Number(takeAmount) / 100000000 : Number(takeAmount),
|
||||
});
|
||||
};
|
||||
|
||||
@ -385,6 +385,7 @@
|
||||
"Onchain amount to send (BTC)": "Quantitat a enviar Onchain (BTC)",
|
||||
"Order current rate:": "Preu actual:",
|
||||
"Order for ": "Ordre per ",
|
||||
"Password": "Password",
|
||||
"Premium over Market (%)": "Prima sobre el mercat (%)",
|
||||
"Public Duration (HH:mm)": "Duració pública (HH:mm)",
|
||||
"Public order length": "Duració de l'ordre pública",
|
||||
|
||||
@ -385,6 +385,7 @@
|
||||
"Onchain amount to send (BTC)": "Onchain amount to send (BTC)",
|
||||
"Order current rate:": "Aktuální kurz nabídky:",
|
||||
"Order for ": "Order for ",
|
||||
"Password": "Password",
|
||||
"Premium over Market (%)": "Prémium vůči tržní ceně (%)",
|
||||
"Public Duration (HH:mm)": "Doba zveřejnění (HH:mm)",
|
||||
"Public order length": "Public order length",
|
||||
|
||||
@ -385,6 +385,7 @@
|
||||
"Onchain amount to send (BTC)": "Onchain amount to send (BTC)",
|
||||
"Order current rate:": "Aktueller Order-Kurs:",
|
||||
"Order for ": "Order for ",
|
||||
"Password": "Password",
|
||||
"Premium over Market (%)": "Marktpreis Aufschlag (%)",
|
||||
"Public Duration (HH:mm)": "Angebotslaufzeit (HH:mm)",
|
||||
"Public order length": "Public order length",
|
||||
|
||||
@ -385,6 +385,7 @@
|
||||
"Onchain amount to send (BTC)": "Onchain amount to send (BTC)",
|
||||
"Order current rate:": "Order current rate:",
|
||||
"Order for ": "Order for ",
|
||||
"Password": "Password",
|
||||
"Premium over Market (%)": "Premium over Market (%)",
|
||||
"Public Duration (HH:mm)": "Public Duration (HH:mm)",
|
||||
"Public order length": "Public order length",
|
||||
|
||||
@ -385,6 +385,7 @@
|
||||
"Onchain amount to send (BTC)": "Cantidad onchain a enviar (BTC)",
|
||||
"Order current rate:": "Precio actual:",
|
||||
"Order for ": "Orden por ",
|
||||
"Password": "Password",
|
||||
"Premium over Market (%)": "Prima sobre el mercado (%)",
|
||||
"Public Duration (HH:mm)": "Duración pública (HH:mm)",
|
||||
"Public order length": "Public order length",
|
||||
|
||||
@ -385,6 +385,7 @@
|
||||
"Onchain amount to send (BTC)": "Onchain amount to send (BTC)",
|
||||
"Order current rate:": "Uneko Prezioa:",
|
||||
"Order for ": "Order for ",
|
||||
"Password": "Password",
|
||||
"Premium over Market (%)": "Merkatuarekiko Prima (%)",
|
||||
"Public Duration (HH:mm)": "Iraupen publikoa (HH:mm)",
|
||||
"Public order length": "Public order length",
|
||||
|
||||
@ -385,6 +385,7 @@
|
||||
"Onchain amount to send (BTC)": "Onchain montant à envoyer (BTC)",
|
||||
"Order current rate:": "Commander le taux actuel:",
|
||||
"Order for ": "Ordre pour ",
|
||||
"Password": "Password",
|
||||
"Premium over Market (%)": "Prime sur le marché (%)",
|
||||
"Public Duration (HH:mm)": "Durée publique (HH:mm)",
|
||||
"Public order length": "Durée de l'ordre public",
|
||||
|
||||
@ -385,6 +385,7 @@
|
||||
"Onchain amount to send (BTC)": "Importo da inviare onchain (BTC)",
|
||||
"Order current rate:": "Ordina al prezzo corrente:",
|
||||
"Order for ": "Ordina per ",
|
||||
"Password": "Password",
|
||||
"Premium over Market (%)": "Premio sul prezzo di mercato (%)",
|
||||
"Public Duration (HH:mm)": "Durata pubblica (HH:mm)",
|
||||
"Public order length": "Durata dell'ordine pubblico",
|
||||
|
||||
@ -385,6 +385,7 @@
|
||||
"Onchain amount to send (BTC)": "送信するオンチェーン量(BTC)",
|
||||
"Order current rate:": "現在のレート:",
|
||||
"Order for ": "Order for ",
|
||||
"Password": "Password",
|
||||
"Premium over Market (%)": "マーケットに対するプレミアム(%)",
|
||||
"Public Duration (HH:mm)": "公開期間(HH:mm)",
|
||||
"Public order length": "公開注文の期間",
|
||||
|
||||
@ -385,6 +385,7 @@
|
||||
"Onchain amount to send (BTC)": "Onchain amount to send (BTC)",
|
||||
"Order current rate:": "Order current rate:",
|
||||
"Order for ": "Order for ",
|
||||
"Password": "Password",
|
||||
"Premium over Market (%)": "Premia nad rynkiem (%)",
|
||||
"Public Duration (HH:mm)": "Czas trwania publicznego (HH:mm)",
|
||||
"Public order length": "Public order length",
|
||||
|
||||
@ -385,6 +385,7 @@
|
||||
"Onchain amount to send (BTC)": "Valor onchain a enviar (BTC)",
|
||||
"Order current rate:": "Taxa atual do pedido:",
|
||||
"Order for ": "Ordem para ",
|
||||
"Password": "Password",
|
||||
"Premium over Market (%)": "Prêmio sobre o mercado (%)",
|
||||
"Public Duration (HH:mm)": "Duração Pública (HH:mm)",
|
||||
"Public order length": "Tempo da ordem pública",
|
||||
|
||||
@ -385,6 +385,7 @@
|
||||
"Onchain amount to send (BTC)": "Сумма для отправки на ончейн (BTC)",
|
||||
"Order current rate:": "Текущий курс ордера:",
|
||||
"Order for ": "Ордер на ",
|
||||
"Password": "Password",
|
||||
"Premium over Market (%)": "Наценка по сравнению с рынком (%)",
|
||||
"Public Duration (HH:mm)": "Публичная продолжительность (ЧЧ: мм)",
|
||||
"Public order length": "Длина общественного ордера",
|
||||
|
||||
@ -385,6 +385,7 @@
|
||||
"Onchain amount to send (BTC)": "Onchain amount to send (BTC)",
|
||||
"Order current rate:": "Aktuell kurs: ",
|
||||
"Order for ": "Order for ",
|
||||
"Password": "Password",
|
||||
"Premium over Market (%)": "Premium över marknaden (%)",
|
||||
"Public Duration (HH:mm)": "Publikt tidsspann (HH:mm)",
|
||||
"Public order length": "Public order length",
|
||||
|
||||
@ -385,6 +385,7 @@
|
||||
"Onchain amount to send (BTC)": "Kiasi cha onchain cha kutuma (BTC)",
|
||||
"Order current rate:": "Kiwango cha sasa cha amri:",
|
||||
"Order for ": "Amri kwa ",
|
||||
"Password": "Password",
|
||||
"Premium over Market (%)": "Faida juu ya Soko (%)",
|
||||
"Public Duration (HH:mm)": "Muda wa Umma (HH:mm)",
|
||||
"Public order length": "Urefu wa amri ya umma",
|
||||
|
||||
@ -385,6 +385,7 @@
|
||||
"Onchain amount to send (BTC)": "Onchain amount to send (BTC)",
|
||||
"Order current rate:": "ราคาที่คุณกำหนด:",
|
||||
"Order for ": "Order for ",
|
||||
"Password": "Password",
|
||||
"Premium over Market (%)": "ค่าพรีเมี่ยมจากท้องตลาด (%)",
|
||||
"Public Duration (HH:mm)": "ประกาศรายการซื้อขายมีอายุ (ชม:นาที)",
|
||||
"Public order length": "Public order length",
|
||||
|
||||
@ -385,6 +385,7 @@
|
||||
"Onchain amount to send (BTC)": "链上发送金额(BTC)",
|
||||
"Order current rate:": "订单当前价格:",
|
||||
"Order for ": "Order for ",
|
||||
"Password": "Password",
|
||||
"Premium over Market (%)": "市场溢价(%)",
|
||||
"Public Duration (HH:mm)": "公开时间 (HH:mm)",
|
||||
"Public order length": "订单公开长度",
|
||||
|
||||
@ -385,6 +385,7 @@
|
||||
"Onchain amount to send (BTC)": "鏈上發送金額(BTC)",
|
||||
"Order current rate:": "訂單當前價格:",
|
||||
"Order for ": "Order for ",
|
||||
"Password": "Password",
|
||||
"Premium over Market (%)": "市場溢價 (%)",
|
||||
"Public Duration (HH:mm)": "公開時間 (HH:mm)",
|
||||
"Public order length": "訂單公開長度",
|
||||
|
||||
Reference in New Issue
Block a user