Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Sort/Import Tokens in Mobile #11618

Open
wants to merge 68 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 67 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
0248c74
feat: Add sortAssets utility function
gambinish Sep 27, 2024
6a2e8c7
chore: Breakup monolithic Tokens/index.tsx into smaller components
gambinish Sep 27, 2024
7a78717
fix: Correctly handle null check for buyable footer button
gambinish Sep 27, 2024
57295ee
fix: Tweak boolean logic for rendering footer components
gambinish Sep 27, 2024
d00354f
chore: Update snapshot
gambinish Sep 27, 2024
be87f81
chore: Type out tokens as TokenI[]
gambinish Sep 27, 2024
68c7b2e
chore: Remove unecessary comments around import statements in Networt…
gambinish Sep 27, 2024
a02e139
Merge branch 'main' into chore/componetize-tokens-screen
gambinish Sep 27, 2024
de5bbed
Merge branch 'main' into chore/componetize-tokens-screen
gambinish Sep 27, 2024
0fd9ad5
Merge branch 'main' into chore/componetize-tokens-screen
gambinish Sep 27, 2024
032618c
chore: Add unit tests to utility file, provide exports from index file
gambinish Sep 30, 2024
a58f1ba
chore: Rename handleBalance to me more descriptive of what its logic …
gambinish Sep 30, 2024
1dd31e3
chore: rename Networth to PortfolioBalance for clarity
gambinish Sep 30, 2024
16ded6a
chore: Cleanup styles extra whitespace
gambinish Sep 30, 2024
52d3d85
Merge branch 'main' into chore/componetize-tokens-screen
gambinish Sep 30, 2024
dd3ffdf
fix: Update snapshot tests
gambinish Sep 30, 2024
da640bc
Merge branch 'chore/componetize-tokens-screen' of github.com:MetaMask…
gambinish Sep 30, 2024
ebcbcf6
fix: Remove unused component
gambinish Sep 30, 2024
31fad19
Merge branch 'main' into chore/componetize-tokens-screen
gambinish Sep 30, 2024
4a8d927
chore: Move PotfolioBalance above TabView
gambinish Oct 1, 2024
a7b4c17
fix: Wrap PortfolioBalance and ScrollableView in fragment for token r…
gambinish Oct 1, 2024
658fcf6
fix: Padding tweak below tab bar for consistency of token list
gambinish Oct 1, 2024
18035d4
fix: Reorganize test suite for PortfolioBalance
gambinish Oct 1, 2024
c570c2c
fix: Revert changes to wallet test
gambinish Oct 1, 2024
a97128b
fix: Update fiatBalance text style to DisplayMD
gambinish Oct 1, 2024
8036a0a
chore: Patch PreferencesController with tokenSortConfig
gambinish Oct 1, 2024
1f0ac51
chore: Patch preferences controller with type
gambinish Oct 1, 2024
34d987c
feat: Build token sorting UI, patch PreferencesController
gambinish Oct 4, 2024
d57558b
chore: Merge main, address merge conflicts
gambinish Oct 7, 2024
c5c954c
fix: ListHeaderComponent not needed in FlatList anymore
gambinish Oct 7, 2024
4f635e8
fix: Update snapshots and lint
gambinish Oct 7, 2024
469270a
chore: Merge develop, address conflicts
gambinish Oct 8, 2024
82eb1a2
chore: Remove destructive index
gambinish Oct 9, 2024
f18b820
Merge branch 'main' into feat/mmassets_357-sort-import-tokens-mobile-…
gambinish Oct 10, 2024
d3a79fc
chore: Merge main, address conflicts
gambinish Oct 10, 2024
5fc979c
chore: Repatch preferences controller with tokenSortConfig
gambinish Oct 10, 2024
2890148
fix: Declning balance sort
gambinish Oct 10, 2024
25a7238
fix: Export raw value from deriveBalanceFromAssetMarketDetails
gambinish Oct 10, 2024
6637303
fix: deriveBalanceFromAssetMarketDetails.test.ts
gambinish Oct 10, 2024
0415b73
fix: Update unit tests and snapshots
gambinish Oct 10, 2024
ff32cc4
fix: Lint unused imports from Tokens/index.tsx
gambinish Oct 10, 2024
f4f2519
chore: Update testIds and locale strings
gambinish Oct 11, 2024
bd61eff
fix: Update WalletView import selector
gambinish Oct 11, 2024
f28b6fe
fix: Update snapshots
gambinish Oct 11, 2024
7c9d5cc
chore: Cleanup
gambinish Oct 11, 2024
e747848
fix: Add navigation type
gambinish Oct 11, 2024
71ce22f
fix: fix snapshot
salimtb Oct 11, 2024
6aa6f3f
Merge branch 'main' into feat/mmassets_357-sort-import-tokens-mobile-…
salimtb Oct 15, 2024
fe21f40
chore: Update snapshots
gambinish Oct 15, 2024
6f52163
Merge branch 'feat/mmassets_357-sort-import-tokens-mobile--reorg-port…
gambinish Oct 15, 2024
579f012
fix: Update placeholders in switch statement
gambinish Oct 15, 2024
68338a3
Merge branch 'feat/mmassets_357-sort-import-tokens-mobile--token-sort…
gambinish Oct 15, 2024
ff95c72
chore: Add additional e2e test for import token footer link
gambinish Oct 15, 2024
1d929ce
chore: Update snapshots
gambinish Oct 15, 2024
a926d64
fix: Break out of switch statement rather than returning arbitrary st…
gambinish Oct 16, 2024
e925da3
fix: Remove arbitrary 8 second delays from import-tokens.spec
gambinish Oct 16, 2024
0c9ca35
fix: Use ButtonBase component to customize button text color more dir…
gambinish Oct 16, 2024
63bb1a2
fix: Update snapshot
gambinish Oct 16, 2024
a5d8b71
fix: Mock initial state that PortfolioBalance component needs
gambinish Oct 16, 2024
0b617f2
fix: Mock tabs in snapshot
gambinish Oct 16, 2024
96436be
fix: Consider Error boundary during Wallet rendering process
gambinish Oct 16, 2024
1365f4d
fix: Update localized string
gambinish Oct 16, 2024
bd9ad0d
Merge branch 'feat/mmassets_357-sort-import-tokens-mobile--reorg-port…
gambinish Oct 16, 2024
da5862d
Merge branch 'main' into feat/mmassets_357-sort-import-tokens-mobile-…
gambinish Oct 17, 2024
c0a28bc
Merge branch 'feat/mmassets_357-sort-import-tokens-mobile--reorg-port…
gambinish Oct 17, 2024
2934811
Merge branch 'main' into feat/mmassets_357-sort-import-tokens-mobile-…
gambinish Oct 17, 2024
8cb67f7
Merge branch 'feat/mmassets_357-sort-import-tokens-mobile--reorg-port…
gambinish Oct 17, 2024
990067f
Merge branch 'main' into feat/mmassets_357-sort-import-tokens-mobile-…
gambinish Oct 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions app/actions/settings/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,10 @@ export function toggleDeviceNotification(deviceNotificationEnabled) {
deviceNotificationEnabled,
};
}

export function setTokenSortConfig(tokenSortConfig) {
return {
type: 'SET_TOKEN_SORT_CONFIG',
tokenSortConfig,
};
}
141 changes: 141 additions & 0 deletions app/components/UI/Tokens/TokenList/PortfolioBalance/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import React from 'react';
import { fireEvent } from '@testing-library/react-native';
import { BN } from 'ethereumjs-util';
import renderWithProvider from '../../../../../util/test/renderWithProvider';
import { backgroundState } from '../../../../../util/test/initial-root-state';
import AppConstants from '../../../../../../app/core/AppConstants';
import Routes from '../../../../../../app/constants/navigation/Routes';
import { WalletViewSelectorsIDs } from '../../../../../../e2e/selectors/wallet/WalletView.selectors';
import { PortfolioBalance } from '.';

jest.mock('../../../../../core/Engine', () => ({
getTotalFiatAccountBalance: jest.fn(),
context: {
TokensController: {
ignoreTokens: jest.fn(() => Promise.resolve()),
},
},
}));

const initialState = {
engine: {
backgroundState: {
...backgroundState,
TokensController: {
tokens: [
{
name: 'Ethereum',
symbol: 'ETH',
address: '0x0',
decimals: 18,
isETH: true,

balanceFiat: '< $0.01',
iconUrl: '',
},
{
name: 'Bat',
symbol: 'BAT',
address: '0x01',
decimals: 18,
balanceFiat: '$0',
iconUrl: '',
},
{
name: 'Link',
symbol: 'LINK',
address: '0x02',
decimals: 18,
balanceFiat: '$0',
iconUrl: '',
},
],
},
TokenRatesController: {
marketData: {
'0x1': {
'0x0': { price: 0.005 },
'0x01': { price: 0.005 },
'0x02': { price: 0.005 },
},
},
},
CurrencyRateController: {
currentCurrency: 'USD',
currencyRates: {
ETH: {
conversionRate: 1,
},
},
},
TokenBalancesController: {
contractBalances: {
'0x00': new BN(2),
'0x01': new BN(2),
'0x02': new BN(0),
},
},
},
},
settings: {
primaryCurrency: 'usd',
hideZeroBalanceTokens: true,
},
security: {
dataCollectionForMarketing: true,
},
};

const mockNavigate = jest.fn();
const mockPush = jest.fn();

jest.mock('@react-navigation/native', () => {
const actualReactNavigation = jest.requireActual('@react-navigation/native');
return {
...actualReactNavigation,
useNavigation: () => ({
navigate: mockNavigate,
push: mockPush,
}),
};
});

// TODO: Replace "any" with type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const renderPortfolioBalance = (state: any = {}) =>
renderWithProvider(<PortfolioBalance />, { state });

describe('PortfolioBalance', () => {
afterEach(() => {
mockNavigate.mockClear();
mockPush.mockClear();
});

it('fiat balance must be defined', () => {
const { getByTestId } = renderPortfolioBalance(initialState);
expect(
getByTestId(WalletViewSelectorsIDs.TOTAL_BALANCE_TEXT),
).toBeDefined();
});

it('portfolio button should render correctly', () => {
const { getByTestId } = renderPortfolioBalance(initialState);

expect(getByTestId(WalletViewSelectorsIDs.PORTFOLIO_BUTTON)).toBeDefined();
});

it('navigates to Portfolio url when portfolio button is pressed', () => {
const { getByTestId } = renderPortfolioBalance(initialState);

const expectedUrl = `${AppConstants.PORTFOLIO.URL}/?metamaskEntry=mobile&metricsEnabled=false&marketingEnabled=${initialState.security.dataCollectionForMarketing}`;

fireEvent.press(getByTestId(WalletViewSelectorsIDs.PORTFOLIO_BUTTON));
expect(mockNavigate).toHaveBeenCalledWith(Routes.BROWSER.HOME, {
params: {
newTabUrl: expectedUrl,
timestamp: 123,
},
screen: Routes.BROWSER.VIEW,
});
});
});
6 changes: 4 additions & 2 deletions app/components/UI/Tokens/TokenList/PortfolioBalance/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import Button, {
ButtonSize,
ButtonWidthTypes,
} from '../../../../../component-library/components/Buttons/Button';
import Text from '../../../../../component-library/components/Texts/Text';
import Text, {
TextVariant,
} from '../../../../../component-library/components/Texts/Text';
import AggregatedPercentage from '../../../../../component-library/components-temp/Price/AggregatedPercentage';
import { IconName } from '../../../../../component-library/components/Icons/Icon';
import { BrowserTab } from '../../types';
Expand Down Expand Up @@ -110,8 +112,8 @@ export const PortfolioBalance = () => {
<View style={styles.portfolioBalance}>
<View>
<Text
style={styles.fiatBalance}
testID={WalletViewSelectorsIDs.TOTAL_BALANCE_TEXT}
variant={TextVariant.DisplayMD}
>
{fiatBalance}
</Text>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@ import { TokenI } from '../../types';

interface TokenListFooterProps {
tokens: TokenI[];
isAddTokenEnabled: boolean;
goToAddToken: () => void;
showDetectedTokens: () => void;
isAddTokenEnabled: boolean;
}

export const TokenListFooter = ({
tokens,
isAddTokenEnabled,
goToAddToken,
showDetectedTokens,
isAddTokenEnabled,
}: TokenListFooterProps) => {
const navigation = useNavigation();
const { colors } = useTheme();
Expand Down Expand Up @@ -106,7 +106,7 @@ export const TokenListFooter = ({
style={styles.add}
onPress={goToAddToken}
disabled={!isAddTokenEnabled}
testID={WalletViewSelectorsIDs.IMPORT_TOKEN_BUTTON}
gambinish marked this conversation as resolved.
Show resolved Hide resolved
testID={WalletViewSelectorsIDs.IMPORT_TOKEN_FOOTER_LINK}
>
<Text style={styles.centered}>
<Text style={styles.emptyText}>
Expand Down
21 changes: 7 additions & 14 deletions app/components/UI/Tokens/TokenList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@ import createStyles from '../styles';
import Text from '../../../../component-library/components/Texts/Text';
import { TokenI } from '../types';
import { strings } from '../../../../../locales/i18n';
import { PortfolioBalance } from './PortfolioBalance';
import { TokenListFooter } from './TokenListFooter';
import { TokenListItem } from './TokenListItem';

interface TokenListProps {
tokens: TokenI[];
refreshing: boolean;
isAddTokenEnabled: boolean;
onRefresh: () => void;
showRemoveMenu: (arg: TokenI) => void;
goToAddToken: () => void;
setIsAddTokenEnabled: (arg: boolean) => void;
}

interface TokenListNavigationParamList {
Expand All @@ -35,8 +37,11 @@ interface TokenListNavigationParamList {
export const TokenList = ({
tokens,
refreshing,
isAddTokenEnabled,
onRefresh,
showRemoveMenu,
goToAddToken,
setIsAddTokenEnabled,
}: TokenListProps) => {
const navigation =
useNavigation<
Expand All @@ -49,20 +54,9 @@ export const TokenList = ({
const detectedTokens = useSelector(selectDetectedTokens);

const [showScamWarningModal, setShowScamWarningModal] = useState(false);
const [isAddTokenEnabled, setIsAddTokenEnabled] = useState(true);

const styles = createStyles(colors);

const goToAddToken = () => {
setIsAddTokenEnabled(false);
navigation.push('AddAsset', { assetType: 'token' });
trackEvent(MetaMetricsEvents.TOKEN_IMPORT_CLICKED, {
source: 'manual',
chain_id: getDecimalChainId(chainId),
});
setIsAddTokenEnabled(true);
};

const showDetectedTokens = () => {
navigation.navigate(...createDetectedTokensNavDetails());
trackEvent(MetaMetricsEvents.TOKEN_IMPORT_CLICKED, {
Expand All @@ -77,7 +71,6 @@ export const TokenList = ({

return tokens?.length ? (
<FlatList
ListHeaderComponent={<PortfolioBalance />}
data={tokens}
renderItem={({ item }) => (
<TokenListItem
Expand All @@ -91,9 +84,9 @@ export const TokenList = ({
ListFooterComponent={
<TokenListFooter
tokens={tokens}
isAddTokenEnabled={isAddTokenEnabled}
goToAddToken={goToAddToken}
showDetectedTokens={showDetectedTokens}
isAddTokenEnabled={isAddTokenEnabled}
/>
}
refreshControl={
Expand Down
Loading
Loading