import { useCallback, useEffect, useRef } from "react"; import Decimal from "decimal.js"; import { useTranslation } from "react-i18next"; import starkString from "starkstring"; import { usePostOtcV1PublicOtcConvertPreview } from "../../services"; import { useAssets } from "../useAssets"; import { useCharge } from "../useCharge"; import { useDisplayDecimalDigits } from "../useDisplayDecimalDigits"; import { BuySellContext } from "./context"; import { useBuySellAmountValidation } from "./useBuySellAmountValidation"; import debounce from "lodash-es/debounce"; import { useAuth } from "react-oidc-js"; const REFRESH_TIME = 8000; const MAX_REFRESH_COUNT = 5; export const useUpdatePreview = ({ mode, type, }: { mode: "from" | "to"; type: "buy" | "sell"; }) => { const { userData } = useAuth(); const { setValue, setError, getValues, getFieldState } = BuySellContext.useFormContext(); const { lastChangedField, fromAsset } = BuySellContext.useWatch(); const lastUpdated = useRef(null); const refreshCount = useRef(0); const { t } = useTranslation(); const { minDeposit, getValidAmount, maxDeposit } = useCharge({ currency: fromAsset, }); const { truncFix, truncFixToCurrency } = useDisplayDecimalDigits(); const { getAssetBySymbol } = useAssets(); const { maximumFrom, minimumFrom, maximumTo, minimumTo } = useBuySellAmountValidation(); const { mutate: requestPreview } = usePostOtcV1PublicOtcConvertPreview({ onSuccess: ({ finalDestinationQuantity, finalSourceQuantity, sourceCurrencySymbol, destinationCurrencySymbol, }) => { lastUpdated.current = Date.now(); const availableRemain = sourceCurrencySymbol ? getAssetBySymbol(sourceCurrencySymbol)?.availableRemain || 0 : 0; const fromAmount = truncFix( finalSourceQuantity, sourceCurrencySymbol, ).toString(); const toAmount = truncFix( finalDestinationQuantity, destinationCurrencySymbol, ).toString(); if (mode === "from") { setValue("toAmount", toAmount); } else { setValue("fromAmount", fromAmount); } if (type === "buy") { if (availableRemain < finalSourceQuantity) { const chargeAmount = getValidAmount( new Decimal(finalSourceQuantity).minus(availableRemain).toNumber(), ); const hasError = !!getFieldState("fromAmount").error; if (hasError) return; if (maxDeposit && chargeAmount > maxDeposit) { setValue("shouldCharge", false); return setError("fromAmount", { message: t("insufficientBalanceAndLessThanMaxDeposit"), }); } else if (chargeAmount < minDeposit) { setValue("shouldCharge", false); return setError("fromAmount", { message: t("insufficientBalanceAndLessThanMinDeposit"), }); } else if ( userData !== null && mode === "from" && Number(fromAmount) <= maximumFrom && Number(fromAmount) > minimumFrom ) { setValue("shouldCharge", true); setValue("chargeAmount", chargeAmount); return setError("fromAmount", { message: t("insufficientBalance"), }); } else if ( userData !== null && mode === "to" && starkString(toAmount).toNumber() <= maximumTo && starkString(toAmount).toNumber() >= minimumTo && availableRemain < finalSourceQuantity ) { setValue("shouldCharge", true); setValue("chargeAmount", chargeAmount); return setError("toAmount", { message: t("insufficientBalance"), }); } } else { return setValue("shouldCharge", false); } } if (type === "sell") { if ( userData !== null && mode === "to" && starkString(toAmount).toNumber() <= maximumTo && starkString(toAmount).toNumber() >= minimumTo && availableRemain < finalSourceQuantity ) { return setError("toAmount", { message: t("insufficientBalance"), }); } if ( userData !== null && mode === "from" && starkString(fromAmount).toNumber() <= maximumFrom && starkString(fromAmount).toNumber() >= minimumFrom && availableRemain < finalSourceQuantity ) { return setError("fromAmount", { message: t("insufficientBalance"), }); } else { return; } } }, }); const request = useCallback(() => { const { isPreviewOpen, toAsset, fromAsset, lastChangedField, fromAmount, toAmount, } = getValues(); const amount = new Decimal( (mode === "from" ? fromAmount : toAmount) || 0, ).toNumber(); if ( fromAsset && toAsset && lastChangedField === mode && !isPreviewOpen && amount > 0 ) { requestPreview({ requestBody: { validityDurationInSecond: 10, sourceQuantity: lastChangedField === "from" ? amount : undefined, destinationQunatity: lastChangedField === "to" ? amount : undefined, destinationCurrencySymbol: toAsset, sourceCurrencySymbol: fromAsset, }, }); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [mode]); const resetRefreshCount = useCallback(() => (refreshCount.current = 0), []); const debounceRequest = debounce(request, 500); useEffect(() => { const interval = setInterval(() => { if (refreshCount.current >= MAX_REFRESH_COUNT) { return; } debounceRequest(); refreshCount.current = refreshCount.current + 1; }, REFRESH_TIME); return () => { if (interval) clearInterval(interval); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return { resetRefreshCount, request: debounceRequest, }; };