From 30033aaece6fee79772aac70777cdacd692fe4f9 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Tue, 4 Feb 2025 00:33:02 -0300 Subject: [PATCH] feat: improve transaction fee selection with radio group --- .../systems/Core/components/Layout/Layout.tsx | 3 +- .../OverlayDialog/OverlayDialog.tsx | 2 +- .../components/TxFee/TxFeeRadio.tsx | 92 +++++++++++++++++++ .../Transaction/components/TxFee/styles.tsx | 25 +++++ .../components/TxFeeOptions/TxFeeOptions.tsx | 54 +++++++---- 5 files changed, 156 insertions(+), 20 deletions(-) create mode 100644 packages/app/src/systems/Transaction/components/TxFee/TxFeeRadio.tsx diff --git a/packages/app/src/systems/Core/components/Layout/Layout.tsx b/packages/app/src/systems/Core/components/Layout/Layout.tsx index efb7bbe7c4..1f95d9f722 100644 --- a/packages/app/src/systems/Core/components/Layout/Layout.tsx +++ b/packages/app/src/systems/Core/components/Layout/Layout.tsx @@ -149,8 +149,9 @@ export const styles = { padding: '$0 $4 $4 $4', flex: 1, '&[data-scrollable=true]:not([data-noborder])': { - padding: '$0', + padding: '$0 $0 $4 $4', ...coreStyles.scrollable(), + overflowY: 'scroll !important', }, '&[data-noborder]': { padding: '$0', diff --git a/packages/app/src/systems/Overlay/components/OverlayDialog/OverlayDialog.tsx b/packages/app/src/systems/Overlay/components/OverlayDialog/OverlayDialog.tsx index 4af658e09e..15adee00ae 100644 --- a/packages/app/src/systems/Overlay/components/OverlayDialog/OverlayDialog.tsx +++ b/packages/app/src/systems/Overlay/components/OverlayDialog/OverlayDialog.tsx @@ -56,7 +56,7 @@ const styles = { '.fuel_Dialog-description': { flex: 1, - overflowY: 'auto', + overflowY: 'auto !important', height: '100%', }, '.fuel_Dialog-heading': { diff --git a/packages/app/src/systems/Transaction/components/TxFee/TxFeeRadio.tsx b/packages/app/src/systems/Transaction/components/TxFee/TxFeeRadio.tsx new file mode 100644 index 0000000000..da2016351f --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxFee/TxFeeRadio.tsx @@ -0,0 +1,92 @@ +import { Box, Card, HStack, RadioGroupItem, Text } from '@fuel-ui/react'; +import { type BN, DEFAULT_PRECISION } from 'fuels'; +import { type FC, useEffect, useMemo, useState } from 'react'; + +import { cssObj } from '@fuel-ui/css'; +import type { AssetFuelData } from '@fuel-wallet/types'; +import { AssetsCache } from '~/systems/Asset/cache/AssetsCache'; +import { convertToUsd } from '~/systems/Core/utils/convertToUsd'; +import { useProvider } from '~/systems/Network/hooks/useProvider'; +import { formatTip } from '../TxFeeOptions/TxFeeOptions.utils'; +import { TxFee } from './TxFee'; +import { TxFeeLoader } from './TxFeeLoader'; +import { styles } from './styles'; + +export type TxFeeRadioProps = { + fee?: BN; + checked?: boolean; + title: string; +}; + +type TxFeeRadioComponent = FC; + +export const TxFeeRadio: TxFeeRadioComponent = ({ + fee, + checked, + title, +}: TxFeeRadioProps) => { + const [_flag, setFlag] = useState(false); + const provider = useProvider(); + const [baseAsset, setBaseAsset] = useState(); + useEffect(() => { + let abort = false; + const getBaseAsset = async () => { + const [baseAssetId, chainId] = await Promise.all([ + provider?.getBaseAssetId(), + provider?.getChainId(), + ]); + if (abort || baseAssetId == null || chainId == null) return; + const baseAsset = await AssetsCache.getInstance().getAsset({ + chainId: chainId, + assetId: baseAssetId, + dbAssets: [], + save: false, + }); + if (abort) return; + setBaseAsset(baseAsset); + }; + getBaseAsset(); + return () => { + abort = true; + }; + }, [provider]); + + const feeInUsd = useMemo(() => { + if (baseAsset?.rate == null || fee == null) return '$0'; + + return convertToUsd(fee, baseAsset.decimals, baseAsset.rate).formatted; + }, [baseAsset, fee]); + + const ready = !!fee && !!feeInUsd; + + // Horrible workaround to force re-render of this section. + useEffect(() => { + setTimeout(() => { + setFlag((prev) => !prev); + }, 500); + }, [ready]); + + if (!ready) return ; + + return ( + + + + + {fee + ? `${fee.format({ + minPrecision: DEFAULT_PRECISION, + precision: DEFAULT_PRECISION, + })} ETH` + : '--'} + + + ); +}; + +TxFee.Loader = TxFeeLoader; diff --git a/packages/app/src/systems/Transaction/components/TxFee/styles.tsx b/packages/app/src/systems/Transaction/components/TxFee/styles.tsx index aec2e72a0b..9762a9002b 100644 --- a/packages/app/src/systems/Transaction/components/TxFee/styles.tsx +++ b/packages/app/src/systems/Transaction/components/TxFee/styles.tsx @@ -51,4 +51,29 @@ export const styles = { wordWrap: 'break-word', minWidth: 0, }), + option: cssObj({ + alignItems: 'center', + backgroundColor: '$white', + border: '1px solid #e0e0e0', + borderRadius: '10px', + color: '#646464', + cursor: 'pointer', + fontSize: '13px', + gap: '$3', + justifyContent: 'space-between', + padding: '$3', + transition: 'all 0.2s ease', + + '&:hover': { + backgroundColor: '#f0f0f0', + }, + }), + optionContent: cssObj({ + color: '#202020', + }), + optionLabel: cssObj({ + color: '#202020', + fontSize: '13px', + fontWeight: '$medium', + }), }; diff --git a/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx b/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx index f8d160d655..067d924c25 100644 --- a/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx +++ b/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx @@ -1,7 +1,17 @@ import { cssObj } from '@fuel-ui/css'; -import { Box, Button, Form, HStack, Input, Text, VStack } from '@fuel-ui/react'; +import { + Box, + Button, + Form, + HStack, + Input, + RadioGroup, + RadioGroupItem, + Text, + VStack, +} from '@fuel-ui/react'; import { AnimatePresence } from 'framer-motion'; -import { type BN, bn } from 'fuels'; +import { type BN, DEFAULT_PRECISION, bn } from 'fuels'; import { useEffect, useMemo, useRef, useState } from 'react'; import { useController, useFormContext } from 'react-hook-form'; import { MotionFlex, MotionStack, animations } from '~/systems/Core'; @@ -9,6 +19,7 @@ import { createAmount } from '~/systems/Core/components/InputAmount/InputAmount' import { isAmountAllowed } from '~/systems/Core/components/InputAmount/InputAmount.utils'; import type { SendFormValues } from '~/systems/Send/hooks'; import { TxFee } from '../TxFee'; +import { TxFeeRadio } from '../TxFee/TxFeeRadio'; import { DECIMAL_UNITS, formatTip, @@ -178,22 +189,29 @@ export const TxFeeOptions = ({ ) : ( - {options.map((option) => ( - { - previousDefaultTip.current = option.tip; - setValue('fees.tip', { - amount: option.tip, - text: formatTip(option.tip), - }); - onRecalculate?.(option.tip); - }} - /> - ))} + o.tip.eq(tip.value.amount))?.name} + onValueChange={(value) => { + const option = options.find((o) => o.name === value); + if (!option) return; + + previousDefaultTip.current = option.tip; + setValue('fees.tip', { + amount: option.tip, + text: formatTip(option.tip), + }); + onRecalculate?.(option.tip); + }} + > + {options.map((option) => ( + + ))} + )}