From 68aa144f94ceeb86870bb2ec34b9bb359fdc8f2a Mon Sep 17 00:00:00 2001 From: Salim TOUBAL Date: Tue, 25 Nov 2025 09:06:40 +0100 Subject: [PATCH 1/9] feat: sites section (#23163) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** build sites section feature for trending ## **Changelog** CHANGELOG entry: build sites section for trending ## **Related issues** Fixes: ## **Manual testing steps** ```gherkin Feature: my feature name Scenario: user [verb for user action] Given [describe expected initial app state] When user [verb for user action] Then [describe expected outcome] ``` ## **Screenshots/Recordings** ### **Before** ### **After** https://github.com/user-attachments/assets/d22bb8e0-d225-40fb-9280-bc4da19ca40d ## **Pre-merge author checklist** - [ ] I’ve 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). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve 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've 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. --- > [!NOTE] > Introduces a new Sites section in Explore, with a full list view, in-app search (URL/Google shortcuts), API-backed data, navigation route, and supporting UI/tests; includes minor Predict UI and header tweaks. > > - **Explore/Trending**: > - **New `sites` section** integrated into `SECTIONS_CONFIG`, `HOME_SECTIONS_ARRAY`, `SECTIONS_ARRAY`, and `useSectionsData`. > - **API hook**: `useSitesData` fetches/normalizes sites from `portfolio.api.cx.metamask.io` with tests. > - **UI**: > - `SitesListView` screen with search, URL/Google footer actions, skeletons, and site rows. > - `SectionSites` components: `SiteRowItem`, `SiteRowItemWrapper` (navigates to `TrendingBrowser`), `SiteSkeleton`. > - Adds `Routes.SITES_LIST_VIEW` and stacks it in `TrendingHome` navigator. > - Search bar supports custom `placeholder`; Explore search results footer layout refined. > - **Search**: `ExploreSearchResults` and tests updated to include `sites` dataset/state. > - **Styling/UX tweaks**: > - Predict market components: adjust paddings, button sizes/variants, carousel layout. > - `TrendingTokensFullView` header text variant updated to `HeadingLG`. > - **Localization**: adds `trending.sites`, `trending.popular_sites`, `trending.search_sites` strings. > - **Tests**: comprehensive additions for sites hook, list view, row item, skeleton, and updated explore search tests. > > Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 024f04824dbb58f19f1c6363b7f9477762144a6b. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot). --- app/components/Nav/Main/MainNavigator.js | 21 + .../PredictMarketMultiple.styles.ts | 11 +- .../PredictMarketMultiple.tsx | 94 ++- .../PredictMarketSingle.styles.ts | 5 +- .../PredictMarketSingle.tsx | 18 +- .../TrendingTokensFullView.tsx | 2 +- .../ExploreSearchBar/ExploreSearchBar.tsx | 8 +- .../ExploreSearchResults.test.tsx | 26 + .../ExploreSearchResults.tsx | 46 +- .../SiteRowItem/SiteRowItem.test.tsx | 177 +++++ .../SectionSites/SiteRowItem/SiteRowItem.tsx | 85 +++ .../SectionSites/SiteRowItem/index.ts | 2 + .../SectionSites/SiteRowItemWrapper.test.tsx | 359 ++++++++++ .../SectionSites/SiteRowItemWrapper.tsx | 34 + .../SiteSkeleton/SiteSkeleton.test.tsx | 111 +++ .../SiteSkeleton/SiteSkeleton.tsx | 47 ++ .../SectionSites/SiteSkeleton/index.ts | 1 + .../TrendingView/SectionSites/hooks/index.ts | 1 + .../SectionSites/hooks/useSiteData.test.ts | 323 +++++++++ .../SectionSites/hooks/useSitesData.ts | 103 +++ .../Views/TrendingView/SectionSites/index.ts | 5 + .../SitesListView/SitesListView.test.tsx | 656 ++++++++++++++++++ .../SitesListView/SitesListView.tsx | 226 ++++++ .../Views/TrendingView/SitesListView/index.ts | 1 + .../TrendingView/config/sections.config.tsx | 42 +- app/constants/navigation/Routes.ts | 1 + locales/languages/en.json | 5 +- 27 files changed, 2324 insertions(+), 86 deletions(-) create mode 100644 app/components/Views/TrendingView/SectionSites/SiteRowItem/SiteRowItem.test.tsx create mode 100644 app/components/Views/TrendingView/SectionSites/SiteRowItem/SiteRowItem.tsx create mode 100644 app/components/Views/TrendingView/SectionSites/SiteRowItem/index.ts create mode 100644 app/components/Views/TrendingView/SectionSites/SiteRowItemWrapper.test.tsx create mode 100644 app/components/Views/TrendingView/SectionSites/SiteRowItemWrapper.tsx create mode 100644 app/components/Views/TrendingView/SectionSites/SiteSkeleton/SiteSkeleton.test.tsx create mode 100644 app/components/Views/TrendingView/SectionSites/SiteSkeleton/SiteSkeleton.tsx create mode 100644 app/components/Views/TrendingView/SectionSites/SiteSkeleton/index.ts create mode 100644 app/components/Views/TrendingView/SectionSites/hooks/index.ts create mode 100644 app/components/Views/TrendingView/SectionSites/hooks/useSiteData.test.ts create mode 100644 app/components/Views/TrendingView/SectionSites/hooks/useSitesData.ts create mode 100644 app/components/Views/TrendingView/SectionSites/index.ts create mode 100644 app/components/Views/TrendingView/SitesListView/SitesListView.test.tsx create mode 100644 app/components/Views/TrendingView/SitesListView/SitesListView.tsx create mode 100644 app/components/Views/TrendingView/SitesListView/index.ts diff --git a/app/components/Nav/Main/MainNavigator.js b/app/components/Nav/Main/MainNavigator.js index 47ee95f3fac3..9c56e4ff8310 100644 --- a/app/components/Nav/Main/MainNavigator.js +++ b/app/components/Nav/Main/MainNavigator.js @@ -52,6 +52,7 @@ import ContactForm from '../../Views/Settings/Contacts/ContactForm'; import ActivityView from '../../Views/ActivityView'; import RewardsNavigator from '../../UI/Rewards/RewardsNavigator'; import TrendingView from '../../Views/TrendingView/TrendingView'; +import SitesListView from '../../Views/TrendingView/SitesListView'; import SwapsAmountView from '../../UI/Swaps'; import SwapsQuotesView from '../../UI/Swaps/QuotesView'; import CollectiblesDetails from '../../UI/CollectibleModal'; @@ -291,6 +292,26 @@ const TrendingHome = () => ( component={TrendingView} options={{ headerShown: false }} /> + ({ + cardStyle: { + transform: [ + { + translateX: current.progress.interpolate({ + inputRange: [0, 1], + outputRange: [layouts.screen.width, 0], + }), + }, + ], + }, + }), + }} + /> ); diff --git a/app/components/UI/Predict/components/PredictMarketMultiple/PredictMarketMultiple.styles.ts b/app/components/UI/Predict/components/PredictMarketMultiple/PredictMarketMultiple.styles.ts index 4bbe5297621e..5d7258fe2b64 100644 --- a/app/components/UI/Predict/components/PredictMarketMultiple/PredictMarketMultiple.styles.ts +++ b/app/components/UI/Predict/components/PredictMarketMultiple/PredictMarketMultiple.styles.ts @@ -13,8 +13,13 @@ const styleSheet = (params: { ...(vars.isCarousel && { height: '100%' }), backgroundColor: theme.colors.background.section, borderRadius: 16, - padding: vars.isCarousel ? 12 : 16, + padding: 16, marginVertical: vars.isCarousel ? 0 : 8, + paddingVertical: vars.isCarousel ? 8 : 16, + ...(vars.isCarousel && { + flexDirection: 'column', + justifyContent: 'space-between', + }), }, buttonContainer: { flexDirection: 'row', @@ -25,12 +30,12 @@ const styleSheet = (params: { buttonYes: { color: theme.colors.success.default, backgroundColor: theme.colors.success.muted, - width: 68, + width: vars.isCarousel ? 60 : 68, }, buttonNo: { color: theme.colors.error.default, backgroundColor: theme.colors.error.muted, - width: 68, + width: vars.isCarousel ? 60 : 68, }, }); }; diff --git a/app/components/UI/Predict/components/PredictMarketMultiple/PredictMarketMultiple.tsx b/app/components/UI/Predict/components/PredictMarketMultiple/PredictMarketMultiple.tsx index 778591eb91d7..5374c0910ed5 100644 --- a/app/components/UI/Predict/components/PredictMarketMultiple/PredictMarketMultiple.tsx +++ b/app/components/UI/Predict/components/PredictMarketMultiple/PredictMarketMultiple.tsx @@ -157,7 +157,7 @@ const PredictMarketMultiple: React.FC = ({ }} > - + = ({ > = ({ {getOutcomePercentage( @@ -243,6 +235,7 @@ const PredictMarketMultiple: React.FC = ({ size={isCarousel ? ButtonSize.Sm : ButtonSize.Md} label={ = ({ width={ButtonWidthTypes.Full} label={ = ({ ); })} + + + + {filteredOutcomes.length > 3 + ? `+${filteredOutcomes.length - 3} ${ + filteredOutcomes.length - 3 === 1 + ? strings('predict.outcomes_singular') + : strings('predict.outcomes_plural') + }` + : ''} + - {filteredOutcomes.length > 3 - ? `+${filteredOutcomes.length - 3} ${ - filteredOutcomes.length - 3 === 1 - ? strings('predict.outcomes_singular') - : strings('predict.outcomes_plural') - }` - : ''} + ${totalVolumeDisplay} {strings('predict.volume_abbreviated')} - - - ${totalVolumeDisplay} {strings('predict.volume_abbreviated')} - - {market.recurrence && market.recurrence !== Recurrence.NONE && ( - + + - - - {strings( - `predict.recurrence.${market.recurrence.toLowerCase()}`, - )} - - - )} - + {strings( + `predict.recurrence.${market.recurrence.toLowerCase()}`, + )} + + + )} diff --git a/app/components/UI/Predict/components/PredictMarketSingle/PredictMarketSingle.styles.ts b/app/components/UI/Predict/components/PredictMarketSingle/PredictMarketSingle.styles.ts index be3b4ab523d8..65a4c6256b4f 100644 --- a/app/components/UI/Predict/components/PredictMarketSingle/PredictMarketSingle.styles.ts +++ b/app/components/UI/Predict/components/PredictMarketSingle/PredictMarketSingle.styles.ts @@ -16,8 +16,9 @@ const styleSheet = (params: { ...(vars.isCarousel && { height: '100%' }), backgroundColor: theme.colors.background.section, borderRadius: 16, - padding: vars.isCarousel ? 12 : 16, + padding: 16, marginVertical: vars.isCarousel ? 0 : 8, + paddingVertical: vars.isCarousel ? 8 : 16, }, marketHeader: { flexDirection: 'row', @@ -36,7 +37,7 @@ const styleSheet = (params: { justifyContent: 'flex-end', alignItems: 'flex-end', width: '100%', - marginTop: 8, + marginTop: vars.isCarousel ? 0 : 8, }, buttonContainer: { flexDirection: 'row', diff --git a/app/components/UI/Predict/components/PredictMarketSingle/PredictMarketSingle.tsx b/app/components/UI/Predict/components/PredictMarketSingle/PredictMarketSingle.tsx index a6ff0216c101..101529f8d632 100644 --- a/app/components/UI/Predict/components/PredictMarketSingle/PredictMarketSingle.tsx +++ b/app/components/UI/Predict/components/PredictMarketSingle/PredictMarketSingle.tsx @@ -235,10 +235,16 @@ const PredictMarketSingle: React.FC = ({