import React, { useState, useMemo, useEffect, useContext } from 'react'; import { useTranslation } from 'react-i18next'; import { Dialog, DialogTitle, DialogContentText, DialogActions, DialogContent, Box, Button, Tooltip, Grid, TextField, useTheme, Typography, FormHelperText, } from '@mui/material'; import Countdown from 'react-countdown'; import currencies from '../../../static/assets/currencies.json'; import { apiClient } from '../../services/api'; import { type Order, type Info } from '../../models'; import { ConfirmationDialog } from '../Dialogs'; import { LoadingButton } from '@mui/lab'; import { computeSats } from '../../utils'; import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext'; import { type UseAppStoreType, AppContext } from '../../contexts/AppContext'; import { type UseFederationStoreType, FederationContext } from '../../contexts/FederationContext'; interface TakeButtonProps { currentOrder: Order; info?: Info; updateCurrentOrder?: () => void; onClickGenerateRobot?: () => void; } interface OpenDialogsProps { inactiveMaker: boolean; confirmation: boolean; } const closeAll = { inactiveMaker: false, confirmation: false }; const TakeButton = ({ currentOrder, info, updateCurrentOrder = () => null, onClickGenerateRobot = () => null, }: TakeButtonProps): JSX.Element => { const { t } = useTranslation(); const theme = useTheme(); const { settings, origin, hostUrl } = useContext(AppContext); const { garage, orderUpdatedAt } = useContext(GarageContext); const { federation } = useContext(FederationContext); const [takeAmount, setTakeAmount] = useState(''); const [badRequest, setBadRequest] = useState(''); const [loadingTake, setLoadingTake] = useState(false); const [open, setOpen] = useState(closeAll); const [satoshis, setSatoshis] = useState(''); const satoshisNow = (): string | undefined => { if (currentOrder === null) return; const tradeFee = info?.taker_fee ?? 0; const defaultRoutingBudget = 0.001; const btcNow = currentOrder.satoshis_now / 100000000; const rate = currentOrder.amount != null ? currentOrder.amount / btcNow : currentOrder.max_amount / btcNow; const amount = currentOrder.currency === 1000 ? Number(takeAmount) / 100000000 : Number(takeAmount); const satoshis = computeSats({ amount, routingBudget: currentOrder.is_buyer ? defaultRoutingBudget : 0, fee: tradeFee, rate, }); return satoshis; }; useEffect(() => { setSatoshis(satoshisNow() ?? ''); }, [orderUpdatedAt, takeAmount, info]); const currencyCode: string = currentOrder?.currency === 1000 ? 'Sats' : currencies[`${Number(currentOrder?.currency)}`]; const InactiveMakerDialog = function (): JSX.Element { return ( { setOpen({ ...open, inactiveMaker: false }); }} > {t('The maker is away')} {t( 'By taking this order you risk wasting your time. If the maker does not proceed in time, you will be compensated in satoshis for 50% of the maker bond.', )} ); }; interface countdownTakeOrderRendererProps { seconds: number; completed: boolean; } const countdownTakeOrderRenderer = function ({ seconds, completed, }: countdownTakeOrderRendererProps): JSX.Element { if (isNaN(seconds) || completed) { return takeOrderButton(); } else { return ( {t('Take Order')} ); } }; const handleTakeAmountChange = function (e: React.ChangeEvent): void { if (e.target.value !== '' && e.target.value != null) { setTakeAmount(`${parseFloat(e.target.value)}`); } else { setTakeAmount(e.target.value); } }; const amountHelperText = useMemo(() => { if (currentOrder === null) return; const amount = currentOrder.currency === 1000 ? Number(takeAmount) / 100000000 : Number(takeAmount); if (amount < Number(currentOrder.min_amount) && takeAmount !== '') { return t('Too low'); } else if (amount > Number(currentOrder.max_amount) && takeAmount !== '') { return t('Too high'); } else { return null; } }, [orderUpdatedAt, takeAmount]); const onTakeOrderClicked = function (): void { if (currentOrder?.maker_status === 'Inactive') { setOpen({ inactiveMaker: true, confirmation: false }); } else { setOpen({ inactiveMaker: false, confirmation: true }); } }; const invalidTakeAmount = useMemo(() => { const amount = currentOrder?.currency === 1000 ? Number(takeAmount) / 100000000 : Number(takeAmount); return ( amount < Number(currentOrder?.min_amount) || amount > Number(currentOrder?.max_amount) || takeAmount === '' || takeAmount == null ); }, [takeAmount, orderUpdatedAt]); const takeOrderButton = function (): JSX.Element { if (currentOrder?.has_range) { return (
{t('Take Order')}
{t('Take Order')}
{satoshis !== '0' && satoshis !== '' && !invalidTakeAmount ? ( {currentOrder?.type === 1 ? t('You will receive {{satoshis}} Sats (Approx)', { satoshis }) : t('You will send {{satoshis}} Sats (Approx)', { satoshis })} ) : null}
); } else { return ( {t('Take Order')} ); } }; const takeOrder = function (): void { const robot = garage.getSlot()?.getRobot() ?? null; if (currentOrder === null || robot === null) return; setLoadingTake(true); const { url, basePath } = federation .getCoordinator(currentOrder.shortAlias) .getEndpoint(settings.network, origin, settings.selfhostedClient, hostUrl); apiClient .post( url + basePath, `/api/order/?order_id=${String(currentOrder?.id)}`, { action: 'take', amount: currentOrder?.currency === 1000 ? takeAmount / 100000000 : takeAmount, }, { tokenSHA256: robot?.tokenSHA256 }, ) .then((data) => { if (data?.bad_request !== undefined) { setBadRequest(data.bad_request); } else { updateCurrentOrder(); setBadRequest(''); } }) .catch(() => { setBadRequest('Request error'); }); }; return ( {badRequest !== '' ? ( {t(badRequest)} ) : ( <> )} { setOpen({ ...open, confirmation: false }); }} onClickDone={() => { takeOrder(); setLoadingTake(true); setOpen(closeAll); }} hasRobot={Boolean(garage.getSlot()?.hashId)} onClickGenerateRobot={onClickGenerateRobot} /> ); }; export default TakeButton;