diff --git a/src/app/components/sendForm/index.styled.ts b/src/app/components/sendForm/index.styled.ts new file mode 100644 index 000000000..545e98d01 --- /dev/null +++ b/src/app/components/sendForm/index.styled.ts @@ -0,0 +1,149 @@ +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 index 564404794..2083dc607 100644 --- a/src/app/components/sendForm/index.tsx +++ b/src/app/components/sendForm/index.tsx @@ -1,4 +1,3 @@ -import ActionButton from '@components/button'; import InfoContainer from '@components/infoContainer'; import TokenImage from '@components/tokenImage'; import { useBnsName, useBnsResolver } from '@hooks/queries/useBnsName'; @@ -20,161 +19,38 @@ import { ReactNode, SetStateAction, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { NumericFormat } from 'react-number-format'; import { useNavigate } from 'react-router-dom'; -import styled from 'styled-components'; import { FiatRow } from './fiatRow'; import useClearFormOnAccountSwitch from './useClearFormOnAccountSwitch'; -interface ContainerProps { - error: boolean; -} -const ScrollContainer = styled.div` - display: flex; - flex: 1; - flex-direction: column; - overflow-y: auto; - &::-webkit-scrollbar { - display: none; - } - margin-left: 5%; - margin-right: 5%; -`; - -const OuterContainer = styled.div((props) => ({ - display: 'flex', - flexDirection: 'column', - marginBottom: props.theme.spacing(32.5), - flex: 1, -})); - -const RowContainer = styled.div({ - display: 'flex', - flexDirection: 'row', - alignItems: 'center', -}); - -const Container = styled.div((props) => ({ - display: 'flex', - flexDirection: 'column', - marginTop: props.theme.spacing(16), -})); - -const OrdinalInfoContainer = styled.div((props) => ({ - marginTop: props.theme.spacing(6), -})); - -const MemoContainer = styled.div((props) => ({ - marginTop: props.theme.spacing(3), - marginBottom: props.theme.spacing(6), -})); - -const ErrorText = styled.p((props) => ({ - ...props.theme.typography.body_s, - color: props.theme.colors.danger_medium, -})); - -const InputFieldContainer = styled.div(() => ({ - flex: 1, -})); - -const TitleText = styled.p((props) => ({ - ...props.theme.typography.body_medium_m, - flex: 1, - display: 'flex', -})); - -const Text = styled.p((props) => ({ - ...props.theme.typography.body_medium_m, -})); - -const SubText = styled.p((props) => ({ - ...props.theme.typography.body_s, - display: 'flex', - flex: 1, - color: props.theme.colors.white_400, -})); - -const AssociatedText = styled.p((props) => ({ - ...props.theme.typography.body_s, - wordWrap: 'break-word', -})); - -const BalanceText = styled.p((props) => ({ - ...props.theme.typography.body_medium_m, - color: props.theme.colors.white_400, - marginRight: props.theme.spacing(2), -})); - -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', -})); - -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}`, - }, -})); - -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}`, - }, -})); - -const SendButtonContainer = styled.div((props) => ({ - paddingBottom: props.theme.spacing(12), - paddingTop: props.theme.spacing(4), - marginLeft: '5%', - marginRight: '5%', -})); - -const CurrencyFlag = styled.img((props) => ({ - marginLeft: props.theme.spacing(4), -})); - -const TokenContainer = styled.div((props) => ({ - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - marginTop: props.theme.spacing(8), -})); - -const StyledInputFeedback = styled(InputFeedback)((props) => ({ - marginBottom: props.theme.spacing(4), -})); +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'; interface Props { onPressSend: (recipientID: string, amount: string, memo?: string) => void; currencyType: CurrencyTypes; amountError?: string; - recepientError?: string; + recipientError?: string; memoError?: string; fungibleToken?: FungibleToken; disableAmountInput?: boolean; @@ -197,7 +73,7 @@ function SendForm({ onPressSend, currencyType, amountError, - recepientError, + recipientError, memoError, fungibleToken, disableAmountInput, @@ -222,7 +98,7 @@ function SendForm({ const [memo, setMemo] = useState(stxMemo ?? ''); const [fiatAmount, setFiatAmount] = useState('0'); const [switchToFiat, setSwitchToFiat] = useState(false); - const [addressError, setAddressError] = useState(recepientError); + const [addressError, setAddressError] = useState(recipientError); const navigate = useNavigate(); const { fiatCurrency, stxAddress, selectedAccount } = useWalletSelector(); @@ -240,14 +116,14 @@ function SendForm({ }, [selectedAccount, isAccountSwitched]); useEffect(() => { - if (recepientError) { - if (associatedAddress !== '' && recepientError.includes(t('ERRORS.ADDRESS_INVALID'))) { + if (recipientError) { + if (associatedAddress !== '' && recipientError.includes(t('ERRORS.ADDRESS_INVALID'))) { setAddressError(''); } else { - setAddressError(recepientError); + setAddressError(recipientError); } } - }, [recepientError, associatedAddress]); + }, [recipientError, associatedAddress]); useEffect(() => { const resultRegex = /^\d*\.?\d*$/; @@ -267,7 +143,7 @@ function SendForm({ ); }, [amountToSend]); - function getTokenCurrency(): string { + const getTokenCurrency = (): string => { if (fungibleToken) { if (fungibleToken?.ticker) { return fungibleToken.ticker.toUpperCase(); @@ -277,7 +153,7 @@ function SendForm({ } } return currencyType; - } + }; const onSwitchPress = () => { setSwitchToFiat(!switchToFiat); @@ -417,9 +293,9 @@ function SendForm({ ); - const handleOnPress = () => { + const handleOnClickSend = () => { onPressSend( - associatedAddress !== '' ? associatedAddress : debouncedSearchTerm, + associatedAddress !== '' ? associatedAddress : debouncedSearchTerm || recipientAddress, switchToFiat ? getTokenEquivalent(amount) : amount, memo, ); @@ -515,11 +391,11 @@ function SendForm({ - diff --git a/src/app/screens/sendFt/index.tsx b/src/app/screens/sendFt/index.tsx index 978ed5c3c..2c11cdd04 100644 --- a/src/app/screens/sendFt/index.tsx +++ b/src/app/screens/sendFt/index.tsx @@ -105,7 +105,7 @@ function SendFtScreen() { return fungibleToken?.balance; }; - function validateFields(associatedAddress: string, amount: string, memo: string): boolean { + const validateFields = (associatedAddress: string, amount: string, memo?: string): boolean => { if (!associatedAddress) { setAddressError(t('ERRORS.ADDRESS_REQUIRED')); return false; @@ -166,12 +166,12 @@ function SendFtScreen() { } return true; - } + }; const onPressSendSTX = async (associatedAddress: string, amount: string, memo?: string) => { const modifyAmount = replaceCommaByDot(amount); const addMemo = memo ?? ''; - if (validateFields(associatedAddress.trim(), modifyAmount, memo!)) { + if (validateFields(associatedAddress.trim(), modifyAmount, memo)) { setAddressError(''); setMemoError(''); setAmountError(''); @@ -186,7 +186,7 @@ function SendFtScreen() { processing={isLoading} currencyType="FT" amountError={amountError} - recepientError={addressError} + recipientError={addressError} memoError={memoError} fungibleToken={fungibleToken} balance={getBalance()} diff --git a/src/app/screens/sendStx/index.tsx b/src/app/screens/sendStx/index.tsx index 6cf359d4a..c6a0066a1 100644 --- a/src/app/screens/sendStx/index.tsx +++ b/src/app/screens/sendStx/index.tsx @@ -77,7 +77,7 @@ function SendStxScreen() { navigate('/'); }; - function validateFields(associatedAddress: string, amount: string, memo: string): boolean { + const validateFields = (associatedAddress: string, amount: string, memo?: string): boolean => { if (!associatedAddress) { setAddressError(t('ERRORS.ADDRESS_REQUIRED')); return false; @@ -127,12 +127,12 @@ function SendStxScreen() { } } return true; - } + }; const onPressSendSTX = async (associatedAddress: string, amount: string, memo?: string) => { const modifyAmount = replaceCommaByDot(amount); const addMemo = memo ?? ''; - if (validateFields(associatedAddress.trim(), modifyAmount, memo!)) { + if (validateFields(associatedAddress.trim(), modifyAmount, memo)) { setAddressError(''); setMemoError(''); setAmountError(''); @@ -147,13 +147,13 @@ function SendStxScreen() { processing={isLoading} currencyType="STX" amountError={amountError} - recepientError={addressError} + recipientError={addressError} memoError={memoError} balance={Number(microstacksToStx(new BigNumber(stxData?.availableBalance || 0)))} onPressSend={onPressSendSTX} - recipient={recipientAddress!} - amountToSend={amountToSend!} - stxMemo={stxMemo!} + recipient={recipientAddress} + amountToSend={amountToSend} + stxMemo={stxMemo} /> diff --git a/src/app/utils/helper.ts b/src/app/utils/helper.ts index 771a4e6fc..c79da8d50 100644 --- a/src/app/utils/helper.ts +++ b/src/app/utils/helper.ts @@ -22,26 +22,23 @@ import { const validUrl = require('valid-url'); -export function initBigNumber(num: string | number | BigNumber) { - return BigNumber.isBigNumber(num) ? num : new BigNumber(num); -} +export const initBigNumber = (num: string | number | BigNumber) => + BigNumber.isBigNumber(num) ? num : new BigNumber(num); -export function ftDecimals(value: number | string | BigNumber, decimals: number): string { +export const ftDecimals = (value: number | string | BigNumber, decimals: number): string => { const amount = initBigNumber(value); return amount.shiftedBy(-decimals).toString(); -} +}; -export function convertAmountToFtDecimalPlaces( +export const convertAmountToFtDecimalPlaces = ( value: number | string | BigNumber, decimals: number, -): number { +): number => { const amount = initBigNumber(value); return amount.shiftedBy(+decimals).toNumber(); -} +}; -export function replaceCommaByDot(amount: string) { - return amount.replace(/,/g, '.'); -} +export const replaceCommaByDot = (amount: string) => amount.replace(/,/g, '.'); export const microStxToStx = (mStx: number | string | BigNumber) => { const microStacks = initBigNumber(mStx); @@ -51,7 +48,7 @@ export const microStxToStx = (mStx: number | string | BigNumber) => { /** * get ticker from name */ -export function getTicker(name: string) { +export const getTicker = (name: string) => { if (name.includes('-')) { const parts = name.split('-'); if (parts.length >= 3) { @@ -63,22 +60,21 @@ export function getTicker(name: string) { return `${name[0]}${name[1]}${name[2]}`; } return name; -} +}; -export function getTruncatedAddress(address: string, lengthToShow = 4) { - return `${address.substring(0, lengthToShow)}...${address.substring( +export const getTruncatedAddress = (address: string, lengthToShow = 4) => + `${address.substring(0, lengthToShow)}...${address.substring( address.length - lengthToShow, address.length, )}`; -} -export function getShortTruncatedAddress(address: string) { +export const getShortTruncatedAddress = (address: string) => { if (address) { return `${address.substring(0, 8)}...${address.substring(address.length - 8, address.length)}`; } -} +}; -export function getAddressDetail(account: Account) { +export const getAddressDetail = (account: Account) => { if (account.btcAddress && account.stxAddress) { return `${getTruncatedAddress(account.btcAddress)} / ${getTruncatedAddress( account.stxAddress, @@ -89,24 +85,22 @@ export function getAddressDetail(account: Account) { return getTruncatedAddress(existingAddress); } return ''; -} +}; -export function getExplorerUrl(stxAddress: string): string { - return `https://explorer.stacks.co/address/${stxAddress}?chain=mainnet`; -} +export const getExplorerUrl = (stxAddress: string): string => + `https://explorer.stacks.co/address/${stxAddress}?chain=mainnet`; -export function getStxTxStatusUrl(transactionId: string, currentNetwork: SettingsNetwork): string { - return `${TRANSACTION_STATUS_URL}${transactionId}?chain=${currentNetwork.type.toLowerCase()}`; -} +export const getStxTxStatusUrl = (transactionId: string, currentNetwork: SettingsNetwork): string => + `${TRANSACTION_STATUS_URL}${transactionId}?chain=${currentNetwork.type.toLowerCase()}`; -export function getBtcTxStatusUrl(txId: string, network: SettingsNetwork) { +export const getBtcTxStatusUrl = (txId: string, network: SettingsNetwork) => { if (network.type === 'Testnet') { return `${BTC_TRANSACTION_TESTNET_STATUS_URL}${txId}`; } return `${BTC_TRANSACTION_STATUS_URL}${txId}`; -} +}; -export function getFetchableUrl(uri: string, protocol: string): string | undefined { +export const getFetchableUrl = (uri: string, protocol: string): string | undefined => { const publicIpfs = 'https://gamma.mypinata.cloud/ipfs'; if (protocol === 'http') return uri; if (protocol === 'ipfs') { @@ -114,17 +108,17 @@ export function getFetchableUrl(uri: string, protocol: string): string | undefin return `${publicIpfs}/${url[1]}`; } return undefined; -} +}; /** * check if nft transaction exists in pending transactions * @param pendingTransactions * @param nft * @returns true if nft exists, false otherwise */ -export function checkNftExists( +export const checkNftExists = ( pendingTransactions: StxMempoolTransactionData[], nft: NftData, -): boolean { +): boolean => { const principal: string[] = nft?.fully_qualified_token_id?.split('::'); const transaction = pendingTransactions.find( (tx) => @@ -133,9 +127,9 @@ export function checkNftExists( ); if (transaction) return true; return false; -} +}; -export async function isValidStacksApi(url: string, type: NetworkType): Promise { +export const isValidStacksApi = async (url: string, type: NetworkType): Promise => { const networkChainId = type === 'Mainnet' ? ChainID.Mainnet : ChainID.Testnet; if (!validUrl.isUri(url)) { @@ -156,9 +150,9 @@ export async function isValidStacksApi(url: string, type: NetworkType): Promise< } return false; -} +}; -export async function isValidBtcApi(url: string, network: NetworkType) { +export const isValidBtcApi = async (url: string, network: NetworkType) => { if (!validUrl.isUri(url)) { return false; } @@ -183,7 +177,7 @@ export async function isValidBtcApi(url: string, network: NetworkType) { } return false; -} +}; export const getNetworkType = (stxNetwork) => stxNetwork.chainId === ChainID.Mainnet ? 'Mainnet' : 'Testnet'; @@ -196,9 +190,8 @@ export const isLedgerAccount = (account: Account | null): boolean => export const isInOptions = (): boolean => !!window.location?.pathname?.match(/options.html$/); -export function formatNumber(value?: string | number) { - return value ? new Intl.NumberFormat().format(Number(value)) : '-'; -} +export const formatNumber = (value?: string | number) => + value ? new Intl.NumberFormat().format(Number(value)) : '-'; export const handleKeyDownFeeRateInput = (e: React.KeyboardEvent) => { // only allow positive integers