Skip to content

Commit

Permalink
feat(suite): new token select component in send form
Browse files Browse the repository at this point in the history
  • Loading branch information
enjojoy authored and tomasklim committed Nov 6, 2024
1 parent 291bbd9 commit 5c9af90
Show file tree
Hide file tree
Showing 12 changed files with 647 additions and 105 deletions.
17 changes: 14 additions & 3 deletions packages/components/src/components/form/Select/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ type WrapperProps = TransientProps<
$isWithPlaceholder: boolean;
$hasBottomPadding: boolean;
$elevation: Elevation;
$focusEnabled: boolean;
};

const Wrapper = styled.div<WrapperProps>`
Expand Down Expand Up @@ -162,9 +163,16 @@ const Wrapper = styled.div<WrapperProps>`
}
&:focus-within {
.${reactSelectClassNamePrefix}__dropdown-indicator {
transform: rotate(180deg);
}
${({ $focusEnabled }) =>
$focusEnabled
? css`
.${reactSelectClassNamePrefix}__dropdown-indicator {
transform: rotate(180deg);
}
`
: css`
border-color: transparent;
`}
}
}
Expand Down Expand Up @@ -270,6 +278,7 @@ interface CommonProps extends Omit<ReactSelectProps<Option>, 'onChange' | 'menuI
* @description pass `null` if bottom text can be `undefined`
*/
bottomText?: ReactNode;
focusEnabled?: boolean;
hasBottomPadding?: boolean;
minValueWidth?: string; // TODO: should be probably removed
inputState?: InputState;
Expand All @@ -296,6 +305,7 @@ export const Select = ({
useKeyPressScroll,
isSearchable = false,
minValueWidth = 'initial',
focusEnabled = true,
isMenuOpen,
inputState,
components,
Expand Down Expand Up @@ -358,6 +368,7 @@ export const Select = ({
$minValueWidth={minValueWidth}
$isDisabled={isDisabled}
$isMenuOpen={isMenuOpen}
$focusEnabled={focusEnabled}
$isWithLabel={!!label}
$isWithPlaceholder={!!placeholder}
$hasBottomPadding={hasBottomPadding === true && bottomText === null}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,48 +1,58 @@
import { Column, Paragraph, Text } from '@trezor/components';
import { FormattedMessage } from 'react-intl';
import { NetworkFilterCategory, SelectAssetSearchCategory } from './SelectAssetModal';
import {
SelectAssetSearchCategory,
NetworkFilterCategories,
TokenFilterCategories,
NetworkFilterCategory,
} from './SelectAssetModal';
import { spacings } from '@trezor/theme';

interface AssetItemNotFoundProps {
searchCategory: SelectAssetSearchCategory;
networkCategories: NetworkFilterCategory[];
filterCategories: NetworkFilterCategories | TokenFilterCategories;
listHeight: string;
listMinHeight: number;
}

export const AssetItemNotFound = ({
searchCategory,
networkCategories,
filterCategories,
listHeight,
listMinHeight,
}: AssetItemNotFoundProps) => {
// TODO: resolve messages sharing https://github.com/trezor/trezor-suite/issues/5325
const translations = searchCategory
? {
heading: {
id: 'TR_TOKEN_NOT_FOUND_ON_NETWORK',
defaultMessage: 'Token not found on the {networkName} network',
values: {
networkName: networkCategories.find(
category => category.coingeckoId === searchCategory.coingeckoId,
)?.name,

const isNetworkCategory = filterCategories?.categoriesType === 'networks';

const translations =
searchCategory && isNetworkCategory
? {
heading: {
id: 'TR_TOKEN_NOT_FOUND_ON_NETWORK',
defaultMessage: 'Token not found on the {networkName} network',
values: {
networkName: filterCategories.categories.find(
(category: NetworkFilterCategory) =>
category.coingeckoId === searchCategory.coingeckoId,
)?.name,
},
},
paragraph: {
id: 'TR_TOKEN_TRY_DIFFERENT_SEARCH_OR_SWITCH',
defaultMessage: 'Please try a different search or switch to another network.',
},
}
: {
heading: {
id: 'TR_TOKEN_NOT_FOUND',
defaultMessage: 'Token not found',
},
paragraph: {
id: 'TR_TOKEN_TRY_DIFFERENT_SEARCH',
defaultMessage: 'Please try a different search.',
},
},
paragraph: {
id: 'TR_TOKEN_TRY_DIFFERENT_SEARCH_OR_SWITCH',
defaultMessage: 'Please try a different search or switch to another network.',
},
}
: {
heading: {
id: 'TR_TOKEN_NOT_FOUND',
defaultMessage: 'Token not found',
},
paragraph: {
id: 'TR_TOKEN_TRY_DIFFERENT_SEARCH',
defaultMessage: 'Please try a different search.',
},
};
};

return (
<Column
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ export const SelectAssetModal = ({
listHeight={LIST_HEIGHT}
listMinHeight={LIST_MIN_HEIGHT}
searchCategory={searchCategory}
networkCategories={filterCategories.categories as NetworkFilterCategory[]}
filterCategories={filterCategories}
/>
) : (
<ShadowContainer>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { AssetLogo, useElevation } from '@trezor/components';
import { getCoingeckoId, NetworkSymbol } from '@suite-common/wallet-config';
import { TokenInfo } from '@trezor/connect';
import { getContractAddressForNetwork } from '@suite-common/wallet-utils';

import styled, { css } from 'styled-components';
import { borders, Elevation, mapElevationToBackground, mapElevationToBorder } from '@trezor/theme';
Expand Down Expand Up @@ -57,7 +56,7 @@ export const TokenIconSet = ({ network, tokens }: TokenIconSetProps) => {
key={token.contract}
size={20}
coingeckoId={coingeckoId ?? ''}
contractAddress={getContractAddressForNetwork(network, token.contract)}
contractAddress={token.contract.toLowerCase()}
placeholder={token.symbol?.toUpperCase() ?? ''}
placeholderWithTooltip={false}
/>
Expand Down
2 changes: 1 addition & 1 deletion packages/suite/src/components/suite/CoinBalance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { FormattedCryptoAmount } from 'src/components/suite';

interface CoinBalanceProps {
value: string;
symbol: Account['symbol'];
symbol: Account['symbol'] | (string & {});
}

export const CoinBalance = ({ value, symbol }: CoinBalanceProps) => (
Expand Down
8 changes: 8 additions & 0 deletions packages/suite/src/support/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,10 @@ export default defineMessages({
defaultMessage: 'Search by name, symbol, network, or contract address',
id: 'TR_SELECT_NAME_OR_ADDRESS',
},
TR_SEARCH_TOKEN_IN_SEND_FORM_MODAL: {
defaultMessage: 'Search by name, symbol, or contract address',
id: 'TR_SEARCH_TOKEN_IN_SEND_FORM_MODAL',
},
TR_TOKEN_NOT_FOUND: {
defaultMessage: 'Token not found',
id: 'TR_TOKEN_NOT_FOUND',
Expand Down Expand Up @@ -9131,6 +9135,10 @@ export default defineMessages({
id: 'TR_PASSPHRASE_DESCRIPTION_ITEM3',
defaultMessage: 'No one can recover it, not even Trezor Support',
},
TR_UNRECOGNIZED: {
id: 'TR_UNRECOGNIZED',
defaultMessage: 'Unrecognized',
},
TR_CONNECT_DEVICE_SEND_PROMO_TITLE: {
id: 'TR_CONNECT_DEVICE_SEND_PROMO_TITLE',
defaultMessage: "Your Trezor isn't connected",
Expand Down
35 changes: 3 additions & 32 deletions packages/suite/src/views/wallet/send/Outputs/Amount/Amount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
getFiatRateKey,
} from '@suite-common/wallet-utils';

import { FiatValue, Translation, NumberInput, HiddenPlaceholder } from 'src/components/suite';
import { FiatValue, Translation, NumberInput } from 'src/components/suite';
import { useSendFormContext } from 'src/hooks/wallet';
import { useBitcoinAmountUnit } from 'src/hooks/wallet/useBitcoinAmountUnit';
import { useSelector, useTranslation } from 'src/hooks/suite';
Expand All @@ -29,8 +29,6 @@ import {
validateMin,
validateReserveOrBalance,
} from 'src/utils/suite/validation';
import { formatTokenSymbol } from 'src/utils/wallet/tokenUtils';
import { TokenSelect } from './TokenSelect';
import { FiatInput } from './FiatInput';
import { SendMaxSwitch } from './SendMaxSwitch';

Expand Down Expand Up @@ -62,16 +60,6 @@ const Left = styled.div`
flex: 1;
`;

const TokenBalance = styled.div`
font-size: ${variables.FONT_SIZE.TINY};
color: ${({ theme }) => theme.legacy.TYPE_LIGHT_GREY};
height: 18px;
`;

const TokenBalanceValue = styled.span`
font-weight: ${variables.FONT_WEIGHT.DEMI_BOLD};
`;

// eslint-disable-next-line local-rules/no-override-ds-component
const StyledTransferIcon = styled(Icon)`
margin: 0 20px 46px;
Expand Down Expand Up @@ -220,13 +208,6 @@ export const Amount = ({ output, outputId }: AmountProps) => {
composeTransaction(amountName);
};

const isTokenSelected = !!token;
const tokenBalance = isTokenSelected ? (
<HiddenPlaceholder>
<TokenBalanceValue>{`${token.balance} ${formatTokenSymbol(token?.symbol || token.contract)}`}</TokenBalanceValue>
</HiddenPlaceholder>
) : undefined;

const getSendMaxSwitchComponent = (
hideOnLargeScreens: boolean | undefined,
hideOnSmallScreens: boolean | undefined,
Expand Down Expand Up @@ -255,16 +236,6 @@ export const Amount = ({ output, outputId }: AmountProps) => {
labelLeft={
<Heading>
<Translation id="AMOUNT" />
{isTokenSelected && (
<TokenBalance>
(
<Translation
id="TOKEN_BALANCE"
values={{ balance: tokenBalance }}
/>
)
</TokenBalance>
)}
</Heading>
}
bottomText={bottomText || null}
Expand All @@ -276,8 +247,8 @@ export const Amount = ({ output, outputId }: AmountProps) => {
rules={cryptoAmountRules}
control={control}
innerAddon={
withTokens ? (
<TokenSelect output={output} outputId={outputId} />
withTokens && token ? (
<Symbol>{token?.symbol?.toUpperCase()}</Symbol>
) : (
<Symbol>{symbolToUse}</Symbol>
)
Expand Down
34 changes: 8 additions & 26 deletions packages/suite/src/views/wallet/send/Outputs/Outputs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@ import { motion } from 'framer-motion';
import { Card, motionEasing } from '@trezor/components';
import { motionEasingStrings } from '@trezor/components/src/config/motion';
import { spacingsPx } from '@trezor/theme';
import { networks } from '@suite-common/wallet-config';

import { useSendFormContext } from 'src/hooks/wallet';
import { useLayoutSize } from 'src/hooks/suite';
import { Translation } from 'src/components/suite';
import { Address } from './Address';
import { Amount } from './Amount/Amount';
import { OpReturn } from './OpReturn';
import { CoinLogo } from '@trezor/product-components';
import { TokenSelect } from './TokenSelect/TokenSelect';
import { getNetworkFeatures } from '@suite-common/wallet-config';

const Container = styled.div<{ $height: number }>`
height: ${({ $height }) => ($height ? `${$height}px` : 'auto')};
Expand All @@ -26,13 +25,6 @@ const Container = styled.div<{ $height: number }>`
}
`;

const StyledEvmExplanation = styled.div`
display: flex;
flex-direction: row;
align-items: center;
gap: ${spacingsPx.sm};
`;

interface OutputsProps {
disableAnim?: boolean; // used in tests, with animations enabled react-testing-library can't find output fields
}
Expand All @@ -45,8 +37,9 @@ export const Outputs = ({ disableAnim }: OutputsProps) => {
const {
outputs,
formState: { errors },
account,
account: { symbol },
} = useSendFormContext();

const ref = useRef<HTMLDivElement>(null);

useLayoutEffect(() => {
Expand All @@ -61,6 +54,8 @@ export const Outputs = ({ disableAnim }: OutputsProps) => {
}
}, [outputs]);

const areTokensSupported = getNetworkFeatures(symbol).includes('tokens') ?? false;

return (
<Container $height={height || 0}>
<div ref={ref}>
Expand All @@ -79,21 +74,8 @@ export const Outputs = ({ disableAnim }: OutputsProps) => {
ease: motionEasing.transition,
}}
>
<Card
label={
account.networkType === 'ethereum' && (
<StyledEvmExplanation>
<CoinLogo symbol={account.symbol} size={16} />
<Translation
id="TR_EVM_EXPLANATION_SEND_DESCRIPTION"
values={{
network: networks[account.symbol].name,
}}
/>
</StyledEvmExplanation>
)
}
>
{areTokensSupported && <TokenSelect outputId={index} />}
<Card>
{output.type === 'opreturn' ? (
<OpReturn outputId={index} />
) : (
Expand Down
Loading

0 comments on commit 5c9af90

Please sign in to comment.