From 91a4f350fc5e3a1c4c498078574fe6ce2cfdf3d8 Mon Sep 17 00:00:00 2001 From: lendihop Date: Thu, 12 Dec 2024 23:31:48 +0100 Subject: [PATCH] deposit step finished --- public/_locales/de/messages.json | 4 +- public/_locales/en/messages.json | 4 +- public/_locales/pt/messages.json | 4 +- public/_locales/tr/messages.json | 4 +- .../components/ExchangeCountdown.tsx | 34 ++++++++ .../crypto-exchange/components/InfoBlock.tsx | 35 ++++++++ .../crypto-exchange/components/Stepper.tsx | 2 +- .../components/SupportButton.tsx | 22 ++++++ .../pages/Market/crypto-exchange/config.ts | 2 +- .../pages/Market/crypto-exchange/context.ts | 19 +++-- .../hooks/use-top-up-update.ts | 35 ++++++++ .../pages/Market/crypto-exchange/index.tsx | 15 ++-- .../components/DepositAddressBlock.tsx | 8 +- .../Deposit/components/DepositQrCodeModal.tsx | 16 ++-- .../Deposit/components/ExpiresInBlock.tsx | 27 +++++-- .../crypto-exchange/steps/Deposit/index.tsx | 79 +++++++++++++++++-- .../OrderCreation/components/FormContent.tsx | 56 +++++++++---- .../OrderCreation/components/InfoCard.tsx | 14 ++-- .../steps/OrderCreation/index.tsx | 10 +-- src/app/pages/Market/index.tsx | 47 ++++++++--- src/lib/apis/exolix/utils.ts | 32 +++++--- tailwind.config.js | 1 + 22 files changed, 369 insertions(+), 101 deletions(-) create mode 100644 src/app/pages/Market/crypto-exchange/components/ExchangeCountdown.tsx create mode 100644 src/app/pages/Market/crypto-exchange/components/InfoBlock.tsx create mode 100644 src/app/pages/Market/crypto-exchange/components/SupportButton.tsx create mode 100644 src/app/pages/Market/crypto-exchange/hooks/use-top-up-update.ts diff --git a/public/_locales/de/messages.json b/public/_locales/de/messages.json index f4e61a2b0..8681bf53c 100644 --- a/public/_locales/de/messages.json +++ b/public/_locales/de/messages.json @@ -1943,13 +1943,13 @@ "message": "Mit einer Transaktion senden" }, "youGet": { - "message": "Sie erhalten:" + "message": "Sie erhalten" }, "fixedRate": { "message": "Festpreis:" }, "transactionId": { - "message": "Transaktions-ID:" + "message": "Transaktions-ID" }, "atomDepositMemo": { "message": "Atom Deposit Memo" diff --git a/public/_locales/en/messages.json b/public/_locales/en/messages.json index 2f6f7c27f..3208e5689 100644 --- a/public/_locales/en/messages.json +++ b/public/_locales/en/messages.json @@ -3221,13 +3221,13 @@ "message": "Send by one transaction" }, "youGet": { - "message": "You get:" + "message": "You get" }, "fixedRate": { "message": "Fixed rate:" }, "transactionId": { - "message": "Transaction ID:" + "message": "Transaction ID" }, "atomDepositMemo": { "message": "Atom Deposit Memo" diff --git a/public/_locales/pt/messages.json b/public/_locales/pt/messages.json index 263228de8..656ed673c 100644 --- a/public/_locales/pt/messages.json +++ b/public/_locales/pt/messages.json @@ -1943,13 +1943,13 @@ "message": "Enviar por uma transação" }, "youGet": { - "message": "Recebe:" + "message": "Recebe" }, "fixedRate": { "message": "Taxa fixa:" }, "transactionId": { - "message": "ID de transação:" + "message": "ID de transação" }, "atomDepositMemo": { "message": "Memorando de depósito de átomo" diff --git a/public/_locales/tr/messages.json b/public/_locales/tr/messages.json index f894c2a43..1b165e6e7 100644 --- a/public/_locales/tr/messages.json +++ b/public/_locales/tr/messages.json @@ -1943,13 +1943,13 @@ "message": "Bir işlem başına gönder" }, "youGet": { - "message": "Aldığınız:" + "message": "Aldığınız" }, "fixedRate": { "message": "Sabit oran:" }, "transactionId": { - "message": "İşlem Kimliği:" + "message": "İşlem Kimliği" }, "atomDepositMemo": { "message": "Atom Yatırma Notu" diff --git a/src/app/pages/Market/crypto-exchange/components/ExchangeCountdown.tsx b/src/app/pages/Market/crypto-exchange/components/ExchangeCountdown.tsx new file mode 100644 index 000000000..a6a4d92d0 --- /dev/null +++ b/src/app/pages/Market/crypto-exchange/components/ExchangeCountdown.tsx @@ -0,0 +1,34 @@ +import React, { memo } from 'react'; + +import Countdown from 'react-countdown'; + +import { getExchangeData } from 'lib/apis/exolix/utils'; + +import { useCryptoExchangeDataState } from '../context'; + +const FORTY_FIVE_MINUTES_IN_MS = 45 * 60 * 1000; + +interface Props { + className?: string; +} + +export const ExchangeCountdown = memo(({ className }) => { + const { exchangeData, setExchangeData } = useCryptoExchangeDataState(); + + if (!exchangeData) return null; + + return ( + ( + + {props.minutes}:{props.seconds < 10 ? '0' + props.seconds : props.seconds} + + )} + date={new Date(exchangeData.createdAt).getTime() + FORTY_FIVE_MINUTES_IN_MS} + onComplete={async () => { + const data = await getExchangeData(exchangeData.id); + setExchangeData(data); + }} + /> + ); +}); diff --git a/src/app/pages/Market/crypto-exchange/components/InfoBlock.tsx b/src/app/pages/Market/crypto-exchange/components/InfoBlock.tsx new file mode 100644 index 000000000..7b36c3ecf --- /dev/null +++ b/src/app/pages/Market/crypto-exchange/components/InfoBlock.tsx @@ -0,0 +1,35 @@ +import React, { FC } from 'react'; + +import clsx from 'clsx'; + +import { T, TID } from 'lib/i18n'; + +interface InfoContainerProps extends PropsWithChildren { + className?: string; +} + +export const InfoContainer: FC = ({ className, children }) => ( +
+ {children} +
+); + +interface InfoRawProps extends InfoContainerProps { + title: TID; + bottomSeparator?: boolean; +} + +export const InfoRaw: FC = ({ title, bottomSeparator, className, children }) => ( +
+

+ +

+ {children} +
+); diff --git a/src/app/pages/Market/crypto-exchange/components/Stepper.tsx b/src/app/pages/Market/crypto-exchange/components/Stepper.tsx index 256c82115..65e1dd253 100644 --- a/src/app/pages/Market/crypto-exchange/components/Stepper.tsx +++ b/src/app/pages/Market/crypto-exchange/components/Stepper.tsx @@ -2,7 +2,7 @@ import React, { FC, memo } from 'react'; import clsx from 'clsx'; -export type Steps = 0 | 1 | 2 | 3; +import { Steps } from '../context'; type Status = 'active' | 'next' | 'default'; diff --git a/src/app/pages/Market/crypto-exchange/components/SupportButton.tsx b/src/app/pages/Market/crypto-exchange/components/SupportButton.tsx new file mode 100644 index 000000000..cd357d43c --- /dev/null +++ b/src/app/pages/Market/crypto-exchange/components/SupportButton.tsx @@ -0,0 +1,22 @@ +import React, { memo } from 'react'; + +import clsx from 'clsx'; + +import { Anchor, IconBase } from 'app/atoms'; +import { ReactComponent as OutLinkIcon } from 'app/icons/base/outLink.svg'; +import { T } from 'lib/i18n'; + +import { EXOLIX_CONTACT_LINK } from '../config'; + +interface Props { + className?: string; +} + +export const SupportLink = memo(({ className }) => ( + + + + + + +)); diff --git a/src/app/pages/Market/crypto-exchange/config.ts b/src/app/pages/Market/crypto-exchange/config.ts index 66bf9c5b0..7fd151035 100644 --- a/src/app/pages/Market/crypto-exchange/config.ts +++ b/src/app/pages/Market/crypto-exchange/config.ts @@ -1,7 +1,7 @@ import { PageModalProps } from 'app/atoms/PageModal'; import { StoredExolixCurrency } from 'app/store/crypto-exchange/state'; -// export const EXOLIX_CONTACT_LINK = 'https://exolix.com/contact'; +export const EXOLIX_CONTACT_LINK = 'https://exolix.com/contact'; export const EXOLIX_TERMS_LINK = 'https://exolix.com/terms'; export const EXOLIX_PRIVICY_LINK = 'https://exolix.com/privacy'; diff --git a/src/app/pages/Market/crypto-exchange/context.ts b/src/app/pages/Market/crypto-exchange/context.ts index 5150ad13e..8381d2e4f 100644 --- a/src/app/pages/Market/crypto-exchange/context.ts +++ b/src/app/pages/Market/crypto-exchange/context.ts @@ -1,11 +1,20 @@ -import { useState } from 'react'; - import constate from 'constate'; import { ExchangeData } from 'lib/apis/exolix/types'; +import { useStorage } from 'lib/temple/front'; +import { useAccount } from 'temple/front'; + +export type Steps = 0 | 1 | 2 | 3; + +export const [CryptoExchangeDataProvider, useCryptoExchangeDataState] = constate(() => { + const currentAccount = useAccount(); + + const [exchangeData, setExchangeData] = useStorage( + `topup_exchange_data_state_${currentAccount.id}`, + null + ); -export const [ExchangeDataProvider, useExchangeDataState] = constate(() => { - const [exchangeData, setExchangeData] = useState(null); + const [step, setStep] = useStorage(`topup_step_state_${currentAccount.id}`, 0); - return { exchangeData, setExchangeData }; + return { exchangeData, setExchangeData, step, setStep }; }); diff --git a/src/app/pages/Market/crypto-exchange/hooks/use-top-up-update.ts b/src/app/pages/Market/crypto-exchange/hooks/use-top-up-update.ts new file mode 100644 index 000000000..3a7257df1 --- /dev/null +++ b/src/app/pages/Market/crypto-exchange/hooks/use-top-up-update.ts @@ -0,0 +1,35 @@ +import { useEffect, useRef } from 'react'; + +import { toastError } from 'app/toaster'; +import { getExchangeData } from 'lib/apis/exolix/utils'; + +import { useCryptoExchangeDataState } from '../context'; + +export const useTopUpUpdate = () => { + const { exchangeData, setExchangeData } = useCryptoExchangeDataState(); + + const isAlive = useRef(false); + + useEffect(() => { + let timeoutId = setTimeout(async function repeat() { + isAlive.current = true; + if (!exchangeData) return; + + try { + const data = await getExchangeData(exchangeData.id); + if (!isAlive.current) { + return; + } + setExchangeData(data); + timeoutId = setTimeout(repeat, 3000); + } catch (e) { + toastError('Failed to update order status!'); + } + }, 3000); + + return () => { + isAlive.current = false; + clearTimeout(timeoutId); + }; + }, [exchangeData, setExchangeData]); +}; diff --git a/src/app/pages/Market/crypto-exchange/index.tsx b/src/app/pages/Market/crypto-exchange/index.tsx index d5a45de61..7a27640f1 100644 --- a/src/app/pages/Market/crypto-exchange/index.tsx +++ b/src/app/pages/Market/crypto-exchange/index.tsx @@ -2,9 +2,8 @@ import React, { FC, useState } from 'react'; import { PageModal } from 'app/atoms/PageModal'; -import { Steps } from './components/Stepper'; import { defaultModalHeaderConfig } from './config'; -import { ExchangeDataProvider } from './context'; +import { CryptoExchangeDataProvider, useCryptoExchangeDataState } from './context'; import { Deposit } from './steps/Deposit'; import { OrderCreation } from './steps/OrderCreation'; @@ -16,22 +15,22 @@ interface Props { export const CryptoExchange: FC = ({ opened, onRequestClose }) => { const [modalHeaderConfig, setModalHeaderConfig] = useState(defaultModalHeaderConfig); - const [exchangeStep, setExchangeStep] = useState(1); + const { step } = useCryptoExchangeDataState(); return ( - + {(() => { - switch (exchangeStep) { + switch (step) { case 0: - return ; + return ; case 1: - return ; + return ; default: return null; } })()} - + ); }; diff --git a/src/app/pages/Market/crypto-exchange/steps/Deposit/components/DepositAddressBlock.tsx b/src/app/pages/Market/crypto-exchange/steps/Deposit/components/DepositAddressBlock.tsx index fa63c70fd..08f73a647 100644 --- a/src/app/pages/Market/crypto-exchange/steps/Deposit/components/DepositAddressBlock.tsx +++ b/src/app/pages/Market/crypto-exchange/steps/Deposit/components/DepositAddressBlock.tsx @@ -9,7 +9,7 @@ import { toastSuccess } from 'app/toaster'; import { T } from 'lib/i18n'; import { useBooleanState } from 'lib/ui/hooks'; -import { useExchangeDataState } from '../../../context'; +import { useCryptoExchangeDataState } from '../../../context'; import { DepositQrCodeModal } from './DepositQrCodeModal'; @@ -18,7 +18,7 @@ interface Props { } export const DepositAddressBlock = memo(({ className }) => { - const { exchangeData } = useExchangeDataState(); + const { exchangeData } = useCryptoExchangeDataState(); const [isQrCodeModalOpened, openQrCodeModal, closeQrCodeModal] = useBooleanState(false); @@ -41,9 +41,9 @@ export const DepositAddressBlock = memo(({ className }) => { - {exchangeData.depositAddress} + {exchangeData.depositAddress} -
+
diff --git a/src/app/pages/Market/crypto-exchange/steps/Deposit/components/DepositQrCodeModal.tsx b/src/app/pages/Market/crypto-exchange/steps/Deposit/components/DepositQrCodeModal.tsx index 66ba6b61c..9e401bc76 100644 --- a/src/app/pages/Market/crypto-exchange/steps/Deposit/components/DepositQrCodeModal.tsx +++ b/src/app/pages/Market/crypto-exchange/steps/Deposit/components/DepositQrCodeModal.tsx @@ -4,31 +4,37 @@ import { QRCode } from 'app/atoms'; import { ActionModal, ActionModalBodyContainer } from 'app/atoms/action-modal'; import { T } from 'lib/i18n'; +import Money from '../../../../../../atoms/Money'; import { CurrencyIcon } from '../../../components/CurrencyIcon'; -import { useExchangeDataState } from '../../../context'; +import { useCryptoExchangeDataState } from '../../../context'; interface Props { onClose: EmptyFn; } export const DepositQrCodeModal = memo(({ onClose }) => { - const { exchangeData } = useExchangeDataState(); + const { exchangeData } = useCryptoExchangeDataState(); if (!exchangeData) return null; return ( -
+
- {`${exchangeData.amount} ${exchangeData.coinFrom.coinCode}`} + + + {exchangeData.amount} + {' '} + {exchangeData.coinFrom.coinCode} +
- + (({ className }) => { - const { exchangeData } = useExchangeDataState(); + const { exchangeData } = useCryptoExchangeDataState(); if (!exchangeData) return null; return ( -
-
+
+
- 44:30 +
-
+
- {`${exchangeData.amount} ${exchangeData.coinFrom.coinCode}`} + + 12 ? 2 : 6} + smallFractionFont={false} + tooltipPlacement="bottom" + > + {exchangeData.amount} + {' '} + {exchangeData.coinFrom.coinCode} +
- + ; -} +export const Deposit = memo(() => { + useTopUpUpdate(); + + const { setExchangeData, setStep } = useCryptoExchangeDataState(); + + const cancelOrder = useCallback(() => { + setStep(0); + setExchangeData(null); + }, [setExchangeData, setStep]); -export const Deposit = memo(() => { return (
@@ -29,13 +42,63 @@ export const Deposit = memo(() => { + + + +
- +
); }); + +const InfoBlock = memo(() => { + const { exchangeData } = useCryptoExchangeDataState(); + + const handleCopyTxId = useCallback(() => { + window.navigator.clipboard.writeText(exchangeData!.id); + toastSuccess('Copied'); + }, [exchangeData]); + + if (!exchangeData) return null; + + return ( + + +
+ + + {exchangeData.amount} + {' '} + {exchangeData.coinFrom.coinCode} + + +
+
+ + + {`1 ${exchangeData.coinFrom.coinCode} = `} + 12 ? 2 : 6} + smallFractionFont={false} + tooltipPlacement="bottom" + > + {exchangeData.rate} + {' '} + {exchangeData.coinTo.coinCode} + + + +
+ {exchangeData.id} + +
+
+
+ ); +}); diff --git a/src/app/pages/Market/crypto-exchange/steps/OrderCreation/components/FormContent.tsx b/src/app/pages/Market/crypto-exchange/steps/OrderCreation/components/FormContent.tsx index ddf7d052a..922fdd4d2 100644 --- a/src/app/pages/Market/crypto-exchange/steps/OrderCreation/components/FormContent.tsx +++ b/src/app/pages/Market/crypto-exchange/steps/OrderCreation/components/FormContent.tsx @@ -10,6 +10,7 @@ import { FadeTransition } from 'app/a11y/FadeTransition'; import AssetField from 'app/atoms/AssetField'; import { ActionsButtonsBox } from 'app/atoms/PageModal/actions-buttons-box'; import { StyledButton } from 'app/atoms/StyledButton'; +import { toastError } from 'app/toaster'; import { useFormAnalytics } from 'lib/analytics'; import { ExchangeDataStatusEnum } from 'lib/apis/exolix/types'; import { loadMinMaxExchangeValues, queryExchange, submitExchange } from 'lib/apis/exolix/utils'; @@ -18,9 +19,9 @@ import { useTypedSWR } from 'lib/swr'; import { useAccountAddressForEvm, useAccountAddressForTezos } from 'temple/front'; import { StepLabel } from '../../../components/StepLabel'; -import { Stepper, Steps } from '../../../components/Stepper'; +import { Stepper } from '../../../components/Stepper'; import { defaultModalHeaderConfig, ModalHeaderConfig } from '../../../config'; -import { useExchangeDataState } from '../../../context'; +import { useCryptoExchangeDataState } from '../../../context'; import { getCurrencyDisplayCode } from '../../../utils'; import { CryptoExchangeFormData } from '../types'; @@ -42,16 +43,15 @@ const DEFAULT_SWR_CONGIG = { interface Props { setModalHeaderConfig: SyncFn; setModalContent: SyncFn; - setExchangeStep: SyncFn; } -export const FormContent: FC = ({ setModalHeaderConfig, setModalContent, setExchangeStep }) => { +export const FormContent: FC = ({ setModalHeaderConfig, setModalContent }) => { const formAnalytics = useFormAnalytics('ExolixOrderCreationForm'); const evmAddress = useAccountAddressForEvm(); const tezosAddress = useAccountAddressForTezos(); - const { setExchangeData } = useExchangeDataState(); + const { setExchangeData, setStep } = useCryptoExchangeDataState(); const { control, watch, handleSubmit, formState, trigger } = useFormContext(); const { isSubmitting, submitCount, errors } = formState; @@ -151,9 +151,6 @@ export const FormContent: FC = ({ setModalHeaderConfig, setModalContent, const onSubmit = useCallback>( async ({ inputValue, inputCurrency, outputCurrency }) => { - console.log('Submitted'); - return; - try { if (isSubmitting || !withdrawalAddress) return; @@ -161,6 +158,19 @@ export const FormContent: FC = ({ setModalHeaderConfig, setModalContent, const amount = Number(inputValue) ?? 0; + console.log( + { + coinFrom: inputCurrency.code, + networkFrom: inputCurrency.network.code, + coinTo: outputCurrency.code, + networkTo: outputCurrency.network.code, + amount, + withdrawalAddress, + withdrawalExtraId: '' + }, + 'submitData' + ); + const data = await submitExchange({ coinFrom: inputCurrency.code, networkFrom: inputCurrency.network.code, @@ -171,25 +181,27 @@ export const FormContent: FC = ({ setModalHeaderConfig, setModalContent, withdrawalExtraId: '' }); setExchangeData(data); + console.log(data, 'data'); switch (data.status) { case ExchangeDataStatusEnum.WAIT: - setExchangeStep(1); + setStep(1); break; case ExchangeDataStatusEnum.CONFIRMATION: - setExchangeStep(2); + setStep(2); break; case ExchangeDataStatusEnum.EXCHANGING: - setExchangeStep(3); + setStep(3); } formAnalytics.trackSubmitSuccess(); } catch (e) { console.log(e); formAnalytics.trackSubmitFail(); + toastError('Something went wrong! Please try again later.'); } }, - [formAnalytics, isSubmitting, setExchangeData, setExchangeStep, withdrawalAddress] + [formAnalytics, isSubmitting, setExchangeData, setStep, withdrawalAddress] ); return ( @@ -263,6 +275,8 @@ export const FormContent: FC = ({ setModalHeaderConfig, setModalContent, ); }; +const COMMON_TEXT_CLASSNAME = 'cursor-pointer text-font-num-12 ml-0.5'; + interface MinMaxDisplayProps { currencyCode: string; error?: FieldError; @@ -274,14 +288,28 @@ const MinMaxDisplay = memo(({ currencyCode, error, min, max const isMinError = error?.message === MIN_ERROR; const ismMaxError = error?.message === MAX_ERROR; + const { setValue } = useFormContext(); + + const handleMinClick = useCallback( + () => min && setValue('inputValue', min.toString(), { shouldValidate: true }), + [min, setValue] + ); + const handleMaxClick = useCallback( + () => max && setValue('inputValue', max.toString(), { shouldValidate: true }), + [max, setValue] + ); + return (
{' '} - + {getMinMaxDisplayValue(currencyCode, min)} :{' '} - + {getMinMaxDisplayValue(currencyCode, max)}
diff --git a/src/app/pages/Market/crypto-exchange/steps/OrderCreation/components/InfoCard.tsx b/src/app/pages/Market/crypto-exchange/steps/OrderCreation/components/InfoCard.tsx index 71adabf7a..78532e47e 100644 --- a/src/app/pages/Market/crypto-exchange/steps/OrderCreation/components/InfoCard.tsx +++ b/src/app/pages/Market/crypto-exchange/steps/OrderCreation/components/InfoCard.tsx @@ -2,6 +2,7 @@ import React, { memo } from 'react'; import { T } from 'lib/i18n'; +import { InfoContainer, InfoRaw } from '../../../components/InfoBlock'; import { EXOLIX_PRIVICY_LINK, EXOLIX_TERMS_LINK } from '../../../config'; interface Props { @@ -9,13 +10,10 @@ interface Props { } export const InfoCard = memo(({ exchangeRate }) => ( -
-
-

- -

-

{exchangeRate}

-
+ + + {exchangeRate} +

@@ -48,5 +46,5 @@ export const InfoCard = memo(({ exchangeRate }) => (

-
+ )); diff --git a/src/app/pages/Market/crypto-exchange/steps/OrderCreation/index.tsx b/src/app/pages/Market/crypto-exchange/steps/OrderCreation/index.tsx index 656c8d456..02e112e62 100644 --- a/src/app/pages/Market/crypto-exchange/steps/OrderCreation/index.tsx +++ b/src/app/pages/Market/crypto-exchange/steps/OrderCreation/index.tsx @@ -6,7 +6,6 @@ import { dispatch } from 'app/store'; import { loadExolixCurrenciesActions } from 'app/store/crypto-exchange/actions'; import { useAccountAddressForTezos } from 'temple/front'; -import { Steps } from '../../components/Stepper'; import { INITIAL_EVM_ACC_OUTPUT_CURRENCY, INITIAL_INPUT_CURRENCY, @@ -22,10 +21,9 @@ type ModalContent = 'form' | SelectTokenContent; interface Props { setModalHeaderConfig: SyncFn; - setExchangeStep: SyncFn; } -export const OrderCreation: FC = ({ setModalHeaderConfig, setExchangeStep }) => { +export const OrderCreation: FC = ({ setModalHeaderConfig }) => { const [modalContent, setModalContent] = useState('form'); useEffect(() => void dispatch(loadExolixCurrenciesActions.submit()), []); @@ -52,11 +50,7 @@ export const OrderCreation: FC = ({ setModalHeaderConfig, setExchangeStep return ( {modalContent === 'form' ? ( - + ) : ( )} diff --git a/src/app/pages/Market/index.tsx b/src/app/pages/Market/index.tsx index cb9b5f37d..5e3168ddb 100644 --- a/src/app/pages/Market/index.tsx +++ b/src/app/pages/Market/index.tsx @@ -1,4 +1,4 @@ -import React, { FC, memo } from 'react'; +import React, { FC, memo, ReactNode } from 'react'; import { CaptionAlert, IconBase, PageTitle } from 'app/atoms'; import { IconBaseProps } from 'app/atoms/IconBase'; @@ -10,6 +10,8 @@ import { useBooleanState } from 'lib/ui/hooks'; import { Link } from 'lib/woozie'; import { CryptoExchange } from './crypto-exchange'; +import { ExchangeCountdown } from './crypto-exchange/components/ExchangeCountdown'; +import { CryptoExchangeDataProvider, useCryptoExchangeDataState } from './crypto-exchange/context'; export const Market = memo(() => { const [cryptoExchangeModalOpened, setCryptoExchangeModalOpen, setCryptoExchangeModalClosed] = useBooleanState(false); @@ -19,29 +21,51 @@ export const Market = memo(() => { } noScroll> - - + + + ); }); +const Timer = memo(() => { + const { exchangeData } = useCryptoExchangeDataState(); + + if (!exchangeData) return null; + + return ( +
+ + + + + +
+ ); +}); + interface OptionProps extends Pick { title: string; description: string; + extraInnerComponent?: ReactNode; onClick?: EmptyFn; } -const Option: FC = ({ Icon, title, description, onClick }) => ( +const Option: FC = ({ Icon, title, description, extraInnerComponent, onClick }) => (
= ({ Icon, title, description, onClick }) => (
- {title} +
+ {title} + {extraInnerComponent} +
{description}
diff --git a/src/lib/apis/exolix/utils.ts b/src/lib/apis/exolix/utils.ts index 1d321e26c..2f6edb547 100644 --- a/src/lib/apis/exolix/utils.ts +++ b/src/lib/apis/exolix/utils.ts @@ -13,6 +13,8 @@ const MAX_DOLLAR_VALUE = 10000; const MIN_ASSET_AMOUNT = 0.00001; const AVG_COMISSION = 300; +const COMMON_RETRY_CONFIG = { retries: 3, minTimeout: 250, maxTimeout: 1000 }; + const api = axios.create({ baseURL: 'https://exolix.com/api/v2', headers: { @@ -55,7 +57,7 @@ const getCurrencies = (page: number) => api .get('/currencies', { params: { size: currenciesLimit, page, withNetworks: true } }) .then(r => r.data), - { retries: 3, minTimeout: 250, maxTimeout: 1000 } + COMMON_RETRY_CONFIG ); const loadUSDTRate = async (coinTo: string, coinToNetwork: string) => { @@ -152,16 +154,20 @@ export const loadMinMaxExchangeValues = async ( }; export const queryExchange = (data: GetRateRequestData): Promise => - api.get('/rate', { params: { ...data, rateType: 'fixed' } }).then( - r => r.data, - (error: unknown) => { - if (axios.isAxiosError(error) && error.response && error.response.status === 422) { - const data = error.response.data; - if (data && data.error == null) return data; - } - console.error(error); - throw error; - } + retry( + () => + api.get('/rate', { params: { ...data, rateType: 'fixed' } }).then( + r => r.data, + (error: unknown) => { + if (axios.isAxiosError(error) && error.response && error.response.status === 422) { + const data = error.response.data; + if (data && data.error == null) return data; + } + console.error(error); + throw error; + } + ), + COMMON_RETRY_CONFIG ); export const submitExchange = (data: { @@ -172,7 +178,7 @@ export const submitExchange = (data: { amount: number; withdrawalAddress: string; withdrawalExtraId: string; -}) => api.post('/transactions', { ...data, rateType: 'fixed' }).then(r => r.data); +}) => retry(() => api.post('/transactions', { ...data, rateType: 'fixed' }).then(r => r.data), COMMON_RETRY_CONFIG); export const getExchangeData = (exchangeId: string) => - api.get(`/transactions/${exchangeId}`).then(r => r.data); + retry(() => api.get(`/transactions/${exchangeId}`).then(r => r.data), COMMON_RETRY_CONFIG); diff --git a/tailwind.config.js b/tailwind.config.js index 2d5cc58f6..5687a1b98 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -89,6 +89,7 @@ module.exports = { yellow: { 100: '#fffff0', 400: '#f6e05e', + 500: '#fed500', 600: '#d69e2e', 700: '#b7791f' },