Skip to content

Commit 3d1f94d

Browse files
authored
feat(homepage): reuse CollectiblesEmptyState in NFTs section (#26644)
## **Description** Replaces the custom inline empty state in the NFTs homepage section with the shared `CollectiblesEmptyState` component, ensuring consistent empty state UX between the homepage section and the NFTs full view. **Changes:** - Replace custom gem icon + import card with `CollectiblesEmptyState` from `app/components/UI/CollectiblesEmptyState` - Fix button centering in `TabEmptyState` by adding `self-center` to the action button (fixes centering for all consumers) - Remove unused imports (`TouchableOpacity`, `Image`, `SectionCard`, `gemIcon`, etc.) - Update test to match new empty state content ## **Changelog** CHANGELOG entry: null ## **Related issues** Refs: https://consensyssoftware.atlassian.net/browse/TMCU-407 ## **Manual testing steps** ```gherkin Feature: NFTs section empty state Scenario: user with no NFTs sees shared empty state Given user has no NFTs And user is on the Homepage When user scrolls to the NFTs section Then user sees the CollectiblesEmptyState with NFT illustration And user sees the Import NFTs button centered below the description And tapping Import NFTs navigates to the AddAsset screen Scenario: empty state button is centered Given user has no NFTs When the empty state is displayed Then the Import NFTs button is horizontally centered ``` ## **Screenshots/Recordings** ### **Before** <img width="300" src="https://github.com/user-attachments/assets/f33f9d39-c7d3-4d1c-9afe-3adf8b2a5480" /> <!-- Custom gem icon card with left-aligned layout --> ### **After** <img width="300" src="https://github.com/user-attachments/assets/19aafd5d-d7f5-4700-b6e9-6046fdc18bd5" /> <!-- Shared CollectiblesEmptyState with centered illustration and button --> ## **Pre-merge author checklist** - [x] I have followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I have completed the PR template to the best of my ability - [x] I have included tests if applicable - [x] I have documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I have applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I have manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Low risk UI refactor that swaps the NFTs homepage empty state to a shared component and adjusts related tests; minor behavior change adds brief button disabling to prevent repeat taps. > > **Overview** > Replaces the NFTs homepage section’s custom “import NFTs” card with the shared `CollectiblesEmptyState`, aligning the empty-state UX with the full NFTs view and removing the bespoke gem/icon layout. > > Adds a small guard to temporarily disable the empty-state action button after tap (re-enabled after 1s), and updates homepage/NFT section tests to stop asserting on the removed description text. > > Fixes `TabEmptyState` action button alignment by applying `twClassName="self-center"`, affecting all consumers of that component. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit b818f95. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 32ea2f3 commit 3d1f94d

5 files changed

Lines changed: 15 additions & 55 deletions

File tree

app/component-library/components-temp/TabEmptyState/TabEmptyState.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export const TabEmptyState: React.FC<TabEmptyStateProps> = ({
5151
<Button
5252
variant={ButtonVariant.Secondary}
5353
onPress={onAction}
54+
twClassName="self-center"
5455
{...actionButtonProps}
5556
>
5657
{actionButtonText}

app/components/Views/Homepage/Homepage.test.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,6 @@ describe('Homepage', () => {
113113
renderWithProvider(<Homepage />, { state: stateWithPreferences });
114114

115115
expect(screen.getByText('Import NFTs')).toBeOnTheScreen();
116-
expect(screen.getByText('Easily add your collectibles')).toBeOnTheScreen();
117116
});
118117

119118
it('exposes refresh function via ref', () => {

app/components/Views/Homepage/Sections/NFTs/NFTsSection.test.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,11 @@ describe('NFTsSection', () => {
7979
expect(screen.queryByText('Import NFTs')).not.toBeOnTheScreen();
8080
});
8181

82-
it('renders empty state card when user has no NFTs', () => {
82+
it('renders empty state when user has no NFTs', () => {
8383
renderWithProvider(<NFTsSection />);
8484

8585
expect(screen.getByText('NFTs')).toBeOnTheScreen();
8686
expect(screen.getByText('Import NFTs')).toBeOnTheScreen();
87-
expect(screen.getByText('Easily add your collectibles')).toBeOnTheScreen();
8887
});
8988

9089
it('navigates to AddAsset when import NFTs card is pressed', () => {

app/components/Views/Homepage/Sections/NFTs/NFTsSection.tsx

Lines changed: 13 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,25 @@ import React, {
33
useCallback,
44
useImperativeHandle,
55
useMemo,
6+
useState,
67
} from 'react';
7-
import { TouchableOpacity, Image, View } from 'react-native';
8+
import { View } from 'react-native';
89
import { useSelector } from 'react-redux';
910
import { useNavigation } from '@react-navigation/native';
1011
import SkeletonPlaceholder from 'react-native-skeleton-placeholder';
1112
import { useTailwind } from '@metamask/design-system-twrnc-preset';
1213
import { useTheme } from '../../../../../util/theme';
13-
import {
14-
Box,
15-
Text,
16-
BoxFlexDirection,
17-
BoxAlignItems,
18-
BoxJustifyContent,
19-
TextVariant,
20-
TextColor,
21-
FontWeight,
22-
} from '@metamask/design-system-react-native';
14+
import { Box, BoxFlexDirection } from '@metamask/design-system-react-native';
2315
import SectionTitle from '../../components/SectionTitle';
2416
import SectionRow from '../../components/SectionRow';
25-
import SectionCard from '../../components/SectionCard';
2617
import Routes from '../../../../../constants/navigation/Routes';
2718
import { useOwnedNfts } from './hooks';
2819
import NftGridItem from '../../../../UI/NftGrid/NftGridItem';
2920
import { useNftRefresh } from '../../../../UI/NftGrid/useNftRefresh';
21+
import { CollectiblesEmptyState } from '../../../../UI/CollectiblesEmptyState/CollectiblesEmptyState';
3022
import { SectionRefreshHandle } from '../../types';
3123
import { strings } from '../../../../../../locales/i18n';
3224
import { isNftFetchingProgressSelector } from '../../../../../reducers/collectibles';
33-
import gemIcon from './assets/gem.png';
3425

3526
const MAX_NFTS_DISPLAYED = 6;
3627
const NFTS_PER_ROW = 3;
@@ -91,8 +82,12 @@ const NFTsSection = forwardRef<SectionRefreshHandle>((_, ref) => {
9182
navigation.navigate(Routes.WALLET.NFTS_FULL_VIEW);
9283
}, [navigation]);
9384

85+
const [isAddNFTEnabled, setIsAddNFTEnabled] = useState(true);
86+
9487
const handleImportNfts = useCallback(() => {
88+
setIsAddNFTEnabled(false);
9589
navigation.navigate('AddAsset', { assetType: 'collectible' });
90+
setTimeout(() => setIsAddNFTEnabled(true), 1000);
9691
}, [navigation]);
9792

9893
return (
@@ -138,45 +133,11 @@ const NFTsSection = forwardRef<SectionRefreshHandle>((_, ref) => {
138133
<NftSkeletonRow />
139134
</SectionRow>
140135
) : (
141-
<SectionRow>
142-
<TouchableOpacity onPress={handleImportNfts} activeOpacity={0.7}>
143-
<SectionCard>
144-
<Box
145-
flexDirection={BoxFlexDirection.Row}
146-
alignItems={BoxAlignItems.Center}
147-
gap={4}
148-
>
149-
<Box
150-
alignItems={BoxAlignItems.Center}
151-
justifyContent={BoxJustifyContent.Center}
152-
twClassName="w-10 h-10"
153-
>
154-
<Image
155-
source={gemIcon}
156-
// eslint-disable-next-line react-native/no-inline-styles
157-
style={{ width: 40, height: 40 }}
158-
resizeMode="contain"
159-
/>
160-
</Box>
161-
<Box twClassName="flex-1">
162-
<Text
163-
variant={TextVariant.BodyMd}
164-
fontWeight={FontWeight.Medium}
165-
color={TextColor.TextDefault}
166-
>
167-
{strings('homepage.sections.import_nfts')}
168-
</Text>
169-
<Text
170-
variant={TextVariant.BodySm}
171-
color={TextColor.TextAlternative}
172-
>
173-
{strings('homepage.sections.import_nfts_description')}
174-
</Text>
175-
</Box>
176-
</Box>
177-
</SectionCard>
178-
</TouchableOpacity>
179-
</SectionRow>
136+
<CollectiblesEmptyState
137+
onAction={handleImportNfts}
138+
actionButtonProps={{ isDisabled: !isAddNFTEnabled }}
139+
twClassName="mx-auto mt-2"
140+
/>
180141
)}
181142
</Box>
182143
);
-3.6 KB
Binary file not shown.

0 commit comments

Comments
 (0)