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, UseGarageStoreType } from '../../contexts/GarageContext'; import { UseAppStoreType, AppContext } from '../../contexts/AppContext'; import { UseFederationStoreType, FederationContext } from '../../contexts/FederationContext'; interface TakeButtonProps { baseUrl: string; info?: Info; onClickGenerateRobot?: () => void; } interface OpenDialogsProps { inactiveMaker: boolean; confirmation: boolean; } const closeAll = { inactiveMaker: false, confirmation: false }; const TakeButton = ({ baseUrl, info, onClickGenerateRobot = () => null, }: TakeButtonProps): JSX.Element => { const { t } = useTranslation(); const theme = useTheme(); const { settings, origin, hostUrl } = useContext(AppContext); const { garage, orderUpdatedAt } = useContext(GarageContext); const { federation, focusedCoordinator } = 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 => { const order = garage.getOrder(); if (order === null) return; const tradeFee = info?.taker_fee ?? 0; const defaultRoutingBudget = 0.001; const btcNow = order.satoshis_now / 100000000; const rate = order.amount != null ? order.amount / btcNow : order.max_amount / btcNow; const amount = order.currency === 1000 ? Number(takeAmount) / 100000000 : Number(takeAmount); const satoshis = computeSats({ amount, routingBudget: order.is_buyer ? defaultRoutingBudget : 0, fee: tradeFee, rate, }); return satoshis; }; useEffect(() => { setSatoshis(satoshisNow() ?? ''); }, [orderUpdatedAt, takeAmount, info]); const currencyCode: string = garage.getOrder()?.currency === 1000 ? 'Sats' : currencies[`${garage.getOrder()?.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(() => { const order = garage.getOrder(); if (order === null) return; const amount = order.currency === 1000 ? Number(takeAmount) / 100000000 : Number(takeAmount); if (amount < Number(order.min_amount) && takeAmount !== '') { return t('Too low'); } else if (amount > Number(order.max_amount) && takeAmount !== '') { return t('Too high'); } else { return null; } }, [orderUpdatedAt, takeAmount]); const onTakeOrderClicked = function (): void { if (garage.getOrder()?.maker_status === 'Inactive') { setOpen({ inactiveMaker: true, confirmation: false }); } else { setOpen({ inactiveMaker: false, confirmation: true }); } }; const invalidTakeAmount = useMemo(() => { const order = garage.getOrder(); const amount = order?.currency === 1000 ? Number(takeAmount) / 100000000 : Number(takeAmount); return ( amount < Number(order?.min_amount) || amount > Number(order?.max_amount) || takeAmount === '' || takeAmount == null ); }, [takeAmount, orderUpdatedAt]); const takeOrderButton = function (): JSX.Element { if (garage.getOrder()?.has_range) { return (
{t('Take Order')}
{t('Take Order')}
{satoshis !== '0' && satoshis !== '' && !invalidTakeAmount ? ( {garage.getOrder()?.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 { if (!focusedCoordinator) return; setLoadingTake(true); const { url } = federation .getCoordinator(focusedCoordinator) .getEndpoint(settings.network, origin, settings.selfhostedClient, hostUrl); apiClient .post( url, `/api/order/?order_id=${String(garage.getOrder()?.id)}`, { action: 'take', amount: garage.getOrder()?.currency === 1000 ? takeAmount / 100000000 : takeAmount, }, { tokenSHA256: garage.getRobot().tokenSHA256 }, ) .then((data) => { setLoadingTake(false); if (data?.bad_request !== undefined) { setBadRequest(data.bad_request); } else { garage.updateOrder(data as Order); setBadRequest(''); } }) .catch(() => { setBadRequest('Request error'); }); }; return ( {badRequest !== '' ? ( {t(badRequest)} ) : ( <> )} { setOpen({ ...open, confirmation: false }); }} onClickDone={() => { takeOrder(); setLoadingTake(true); setOpen(closeAll); }} hasRobot={garage.getRobot().avatarLoaded} onClickGenerateRobot={onClickGenerateRobot} /> ); }; export default TakeButton;