diff --git a/src/app/components/sendForm/fiatRow.tsx b/src/app/components/sendForm/fiatRow.tsx deleted file mode 100644 index 060c726dc..000000000 --- a/src/app/components/sendForm/fiatRow.tsx +++ /dev/null @@ -1,76 +0,0 @@ -/* eslint-disable import/prefer-default-export */ -import { ArrowsDownUp } from '@phosphor-icons/react'; -import { useTranslation } from 'react-i18next'; -import { NumericFormat } from 'react-number-format'; -import styled from 'styled-components'; - -const RowContainer = styled.div({ - display: 'flex', - flexDirection: 'row', - alignItems: 'center', -}); - -const SubText = styled.h1((props) => ({ - ...props.theme.body_xs, - display: 'flex', - flex: 1, - color: props.theme.colors.white_400, -})); - -const SwitchToFiatButton = styled.button((props) => ({ - backgroundColor: props.theme.colors.background.elevation0, - border: `1px solid ${props.theme.colors.background.elevation3}`, - borderRadius: 24, - display: 'flex', - padding: '8px 12px', - justifyContent: 'center', - alignItems: 'center', - color: props.theme.colors.white_0, -})); - -const SwitchToFiatText = styled.h1((props) => ({ - ...props.theme.body_xs, - marginLeft: props.theme.spacing(2), - color: props.theme.colors.white_0, -})); - -export function FiatRow({ - onClick, - showFiat, - tokenCurrency, - tokenAmount, - fiatCurrency, - fiatAmount, -}: { - onClick: (e: React.MouseEvent) => void; - showFiat: boolean; - tokenCurrency: string; - tokenAmount: string; - fiatCurrency: string; - fiatAmount: string; -}) { - const { t } = useTranslation('translation', { keyPrefix: 'SEND' }); - const renderText = (value: string) => `~ ${value} ${tokenCurrency}`; - return ( - - - {showFiat ? ( - - ) : ( - `~ $ ${fiatAmount} ${fiatCurrency}` - )} - - - - - {showFiat ? `${t('SWITCH_TO')} ${tokenCurrency}` : `${t('SWITCH_TO')} ${fiatCurrency}`} - - - - ); -} diff --git a/src/app/components/sendForm/index.styled.ts b/src/app/components/sendForm/index.styled.ts deleted file mode 100644 index 9ce42997a..000000000 --- a/src/app/components/sendForm/index.styled.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { InputFeedback } from '@ui-library/inputFeedback'; -import styled from 'styled-components'; - -interface ContainerProps { - error: boolean; -} - -export const ScrollContainer = styled.div` - display: flex; - flex: 1; - flex-direction: column; - overflow-y: auto; - &::-webkit-scrollbar { - display: none; - } - margin-left: 5%; - margin-right: 5%; -`; - -export const OuterContainer = styled.div((props) => ({ - display: 'flex', - flexDirection: 'column', - marginBottom: props.theme.spacing(32.5), - flex: 1, -})); - -export const RowContainer = styled.div({ - display: 'flex', - flexDirection: 'row', - alignItems: 'center', -}); - -export const Container = styled.div((props) => ({ - display: 'flex', - flexDirection: 'column', - marginTop: props.theme.spacing(16), -})); - -export const OrdinalInfoContainer = styled.div((props) => ({ - marginTop: props.theme.spacing(6), -})); - -export const MemoContainer = styled.div((props) => ({ - marginTop: props.theme.spacing(3), - marginBottom: props.theme.spacing(6), -})); - -export const ErrorText = styled.p((props) => ({ - ...props.theme.typography.body_s, - color: props.theme.colors.danger_medium, -})); - -export const InputFieldContainer = styled.div(() => ({ - flex: 1, -})); - -export const TitleText = styled.p((props) => ({ - ...props.theme.typography.body_medium_m, - flex: 1, - display: 'flex', -})); - -export const Text = styled.p((props) => ({ - ...props.theme.typography.body_medium_m, -})); - -export const SubText = styled.p((props) => ({ - ...props.theme.typography.body_s, - display: 'flex', - flex: 1, - color: props.theme.colors.white_400, -})); - -export const AssociatedText = styled.p((props) => ({ - ...props.theme.typography.body_s, - wordWrap: 'break-word', -})); - -export const BalanceText = styled.p((props) => ({ - ...props.theme.typography.body_medium_m, - color: props.theme.colors.white_400, - marginRight: props.theme.spacing(2), -})); - -export const InputField = styled.input((props) => ({ - ...props.theme.typography.body_m, - backgroundColor: props.theme.colors.elevation_n1, - color: props.theme.colors.white_0, - width: '100%', - border: 'transparent', -})); - -export const AmountInputContainer = styled.div((props) => ({ - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - marginTop: props.theme.spacing(4), - marginBottom: props.theme.spacing(4), - border: props.error - ? '1px solid rgba(211, 60, 60, 0.3)' - : `1px solid ${props.theme.colors.elevation3}`, - backgroundColor: props.theme.colors.elevation_n1, - borderRadius: props.theme.radius(1), - paddingLeft: props.theme.spacing(5), - paddingRight: props.theme.spacing(5), - height: 44, - ':focus-within': { - border: `1px solid ${props.theme.colors.elevation6}`, - }, -})); - -export const MemoInputContainer = styled.div((props) => ({ - display: 'flex', - flexDirection: 'row', - marginTop: props.theme.spacing(4), - marginBottom: props.theme.spacing(4), - border: props.error - ? '1px solid rgba(211, 60, 60, 0.3)' - : `1px solid ${props.theme.colors.elevation3}`, - backgroundColor: props.theme.colors.elevation_n1, - borderRadius: props.theme.radius(1), - padding: props.theme.spacing(7), - height: 76, - ':focus-within': { - border: `1px solid ${props.theme.colors.elevation6}`, - }, -})); - -export const SendButtonContainer = styled.div((props) => ({ - paddingBottom: props.theme.spacing(12), - paddingTop: props.theme.spacing(4), - marginLeft: '5%', - marginRight: '5%', -})); - -export const CurrencyFlag = styled.img((props) => ({ - marginLeft: props.theme.spacing(4), -})); - -export const TokenContainer = styled.div((props) => ({ - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - marginTop: props.theme.spacing(8), -})); - -export const StyledInputFeedback = styled(InputFeedback)((props) => ({ - marginBottom: props.theme.spacing(4), -})); diff --git a/src/app/components/sendForm/index.tsx b/src/app/components/sendForm/index.tsx deleted file mode 100644 index 2abbd8774..000000000 --- a/src/app/components/sendForm/index.tsx +++ /dev/null @@ -1,418 +0,0 @@ -import InfoContainer from '@components/infoContainer'; -import TokenImage from '@components/tokenImage'; -import { useBnsName, useBnsResolver } from '@hooks/queries/useBnsName'; -import useCoinRates from '@hooks/queries/useCoinRates'; -import useDebounce from '@hooks/useDebounce'; -import useWalletSelector from '@hooks/useWalletSelector'; -import { - getBtcEquivalent, - getFiatEquivalent, - getStxTokenEquivalent, - type FungibleToken, -} from '@secretkeylabs/xverse-core'; -import { InputFeedback } from '@ui-library/inputFeedback'; -import type { CurrencyTypes } from '@utils/constants'; -import { getCurrencyFlag } from '@utils/currency'; -import { getTicker } from '@utils/helper'; -import BigNumber from 'bignumber.js'; -import { useEffect, useState, type ReactNode, type SetStateAction } from 'react'; -import { useTranslation } from 'react-i18next'; -import { NumericFormat } from 'react-number-format'; -import { useNavigate } from 'react-router-dom'; -import { FiatRow } from './fiatRow'; -import useClearFormOnAccountSwitch from './useClearFormOnAccountSwitch'; - -import useSelectedAccount from '@hooks/useSelectedAccount'; -import Button from '@ui-library/button'; -import { - AmountInputContainer, - AssociatedText, - BalanceText, - Container, - CurrencyFlag, - ErrorText, - InputField, - InputFieldContainer, - MemoContainer, - MemoInputContainer, - OrdinalInfoContainer, - OuterContainer, - RowContainer, - ScrollContainer, - SendButtonContainer, - StyledInputFeedback, - SubText, - Text, - TitleText, - TokenContainer, -} from './index.styled'; - -type Props = { - onPressSend: (recipientID: string, amount: string, memo?: string) => void; - currencyType: CurrencyTypes; - amountError?: string; - recipientError?: string; - memoError?: string; - fungibleToken?: FungibleToken; - disableAmountInput?: boolean; - balance?: number; - hideMemo?: boolean; - hideTokenImage?: boolean; - hideDefaultWarning?: boolean; - buttonText?: string; - processing?: boolean; - children?: ReactNode; - recipient?: string; - amountToSend?: string; - stxMemo?: string; - onAddressInputChange?: (recipientAddress: string) => void; - warning?: string; - info?: string; -}; - -function SendForm({ - onPressSend, - currencyType, - amountError, - recipientError, - memoError, - fungibleToken, - disableAmountInput, - balance, - hideMemo = false, - hideTokenImage = false, - hideDefaultWarning = false, - buttonText, - processing, - children, - recipient, - amountToSend, - stxMemo, - onAddressInputChange, - warning, - info, -}: Props) { - const { t } = useTranslation('translation', { keyPrefix: 'SEND' }); - // TODO tim: use context instead of duplicated local state and parent state (as props) - const [amount, setAmount] = useState(amountToSend ?? ''); - const [recipientAddress, setRecipientAddress] = useState(recipient ?? ''); - const [memo, setMemo] = useState(stxMemo ?? ''); - const [fiatAmount, setFiatAmount] = useState('0'); - const [switchToFiat, setSwitchToFiat] = useState(false); - const [addressError, setAddressError] = useState(recipientError); - const navigate = useNavigate(); - - const selectedAccount = useSelectedAccount(); - const { fiatCurrency } = useWalletSelector(); - const { btcFiatRate, stxBtcRate } = useCoinRates(); - const debouncedSearchTerm = useDebounce(recipientAddress, 300); - const associatedBnsName = useBnsName(recipientAddress); - const associatedAddress = useBnsResolver( - debouncedSearchTerm, - selectedAccount.stxAddress, - currencyType, - ); - const { isAccountSwitched } = useClearFormOnAccountSwitch(); - - useEffect(() => { - if (isAccountSwitched) { - setAmount(''); - setRecipientAddress(''); - } - }, [selectedAccount, isAccountSwitched]); - - useEffect(() => { - if (recipientError) { - if (associatedAddress !== '' && recipientError.includes(t('ERRORS.ADDRESS_INVALID'))) { - setAddressError(''); - } else { - setAddressError(recipientError); - } - } - }, [recipientError, associatedAddress]); - - useEffect(() => { - const resultRegex = /^\d*\.?\d*$/; - - if (!amountToSend || !resultRegex.test(amountToSend)) { - return; - } - - setFiatAmount( - getFiatEquivalent( - Number(amountToSend), - currencyType, - BigNumber(stxBtcRate), - BigNumber(btcFiatRate), - fungibleToken, - ), - ); - }, [amountToSend]); - - const getTokenCurrency = (): string => { - if (fungibleToken) { - if (fungibleToken?.ticker) { - return fungibleToken.ticker.toUpperCase(); - } - if (fungibleToken?.name) { - return getTicker(fungibleToken.name).toUpperCase(); - } - } - return currencyType; - }; - - const onSwitchPress = () => { - setSwitchToFiat(!switchToFiat); - }; - - const onInputChange = (e: React.FormEvent) => { - const newValue = e.currentTarget.value; - - const resultRegex = /^\d*\.?\d*$/; - if (!resultRegex.test(newValue)) { - setAmount(''); - } else { - setAmount(newValue); - } - setFiatAmount( - getFiatEquivalent( - Number(amountToSend), - currencyType, - BigNumber(stxBtcRate), - BigNumber(btcFiatRate), - fungibleToken, - ), - ); - }; - - const getTokenEquivalent = (tokenAmount: string): string => { - if ((currencyType === 'FT' && !fungibleToken?.tokenFiatRate) || currencyType === 'NFT') { - return ''; - } - if (!tokenAmount) return '0'; - switch (currencyType) { - case 'STX': - return getStxTokenEquivalent( - new BigNumber(tokenAmount), - BigNumber(stxBtcRate), - BigNumber(btcFiatRate), - ) - .toFixed(6) - .toString(); - case 'BTC': - return getBtcEquivalent(new BigNumber(tokenAmount), BigNumber(btcFiatRate)) - .toFixed(8) - .toString(); - case 'FT': - if (fungibleToken?.tokenFiatRate) { - return new BigNumber(tokenAmount) - .dividedBy(fungibleToken.tokenFiatRate) - .toFixed(fungibleToken.decimals ?? 2) - .toString(); - } - return ''; - default: - return ''; - } - }; - - const getAmountLabel = () => { - if (switchToFiat) return fiatCurrency; - return getTokenCurrency(); - }; - - const renderEnterAmountSection = ( - - - {t('AMOUNT')} - {t('BALANCE')}: - {value}} - /> - - - - - - {getAmountLabel()} - {switchToFiat && } - - - - ); - - const handleAddressInputChange = (e: React.ChangeEvent) => { - setRecipientAddress(e.target.value); - onAddressInputChange?.(e.target.value); - }; - - const getAddressInputPlaceholder = () => { - if (currencyType === 'BTC') { - return t('BTC_RECIPIENT_PLACEHOLDER'); - } - if (currencyType === 'Ordinal' || currencyType === 'brc20-Ordinal') { - return t('ORDINAL_RECIPIENT_PLACEHOLDER'); - } - return t('RECIPIENT_PLACEHOLDER'); - }; - - const renderEnterRecipientSection = ( - - {t('RECIPIENT')} - - - - - - {associatedAddress && - currencyType !== 'BTC' && - currencyType !== 'Ordinal' && - currencyType !== 'brc20-Ordinal' && ( - <> - {t('ASSOCIATED_ADDRESS')} - {associatedAddress} - - )} - {associatedBnsName && - currencyType !== 'BTC' && - currencyType !== 'Ordinal' && - currencyType !== 'brc20-Ordinal' && ( - <> - {t('ASSOCIATED_BNS_DOMAIN')} - {associatedBnsName} - - )} - - ); - - const handleOnClickSend = () => { - onPressSend( - associatedAddress !== '' ? associatedAddress : debouncedSearchTerm || recipientAddress, - switchToFiat ? getTokenEquivalent(amount) : amount, - memo, - ); - }; - - const onBuyClick = () => { - navigate(`/buy/${currencyType}`); - }; - - const buyCryptoMessage = balance === 0 && (currencyType === 'STX' || currencyType === 'BTC') && ( - - ); - - const checkIfEnableButton = () => { - if (disableAmountInput) { - if (recipientAddress !== '' || associatedAddress !== '') { - return true; - } - } else if ((amount !== '' && recipientAddress !== '') || associatedAddress !== '') return true; - return false; - }; - - let displayedWarning = ''; - if (warning) { - displayedWarning = warning; - } else if (!hideDefaultWarning) { - switch (currencyType) { - case 'Ordinal': - displayedWarning = t('MAKE_SURE_THE_RECIPIENT'); - break; - case 'brc20-Ordinal': - displayedWarning = t('SEND_BRC20_ORDINAL_WALLET_WARNING'); - break; - default: - break; - } - } - - return ( - <> - - {currencyType !== 'NFT' && - currencyType !== 'Ordinal' && - currencyType !== 'brc20-Ordinal' && - !hideTokenImage && ( - - - - )} - - {!disableAmountInput && renderEnterAmountSection} - {amountError && } - {buyCryptoMessage} - {children} - {renderEnterRecipientSection} - {addressError && } - {info && } - {currencyType !== 'BTC' && - currencyType !== 'NFT' && - currencyType !== 'Ordinal' && - currencyType !== 'brc20-Ordinal' && - !hideMemo && ( - <> - - {t('MEMO')} - - - } }) => - setMemo(e.target.value) - } - /> - - - - - {memoError} - - - - )} - {displayedWarning && ( - - - - )} - - - -