Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 2 additions & 7 deletions app/components/UI/Predict/constants/feedConfig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ describe('feedConfig', () => {
'crypto',
'live',
'trending',
'popular-today',
]);
expect(isPredictFeedId('world-cup')).toBe(false);
expect(isPredictFeedId('popular-today')).toBe(false);
});

it.each(PREDICT_FEED_IDS)('resolves known feed id %s', (feedId) => {
Expand Down Expand Up @@ -95,9 +95,7 @@ describe('feedConfig', () => {
});

it('represents hidden-tab feeds with exactly one tab', () => {
(
['politics', 'crypto', 'live', 'trending', 'popular-today'] as const
).forEach((feedId) => {
(['politics', 'crypto', 'live', 'trending'] as const).forEach((feedId) => {
expect(PREDICT_FEED_REGISTRY[feedId].tabs).toHaveLength(1);
});
});
Expand Down Expand Up @@ -128,9 +126,6 @@ describe('feedConfig', () => {
});

it('resolves the dynamic filter config for a feed from the registry', () => {
expect(resolvePredictFeedDynamicFilterConfig('popular-today')).toBe(
PREDICT_FEED_REGISTRY['popular-today'].tabs[0].filters.dynamic,
);
expect(resolvePredictFeedDynamicFilterConfig('trending')).toBe(
PREDICT_FEED_REGISTRY.trending.tabs[0].filters.dynamic,
);
Expand Down
34 changes: 0 additions & 34 deletions app/components/UI/Predict/constants/feedConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export const PREDICT_FEED_IDS = [
'crypto',
'live',
'trending',
'popular-today',
] as const;

export type PredictFeedId = (typeof PREDICT_FEED_IDS)[number];
Expand Down Expand Up @@ -246,39 +245,6 @@ export const PREDICT_FEED_REGISTRY: Record<PredictFeedId, PredictFeedConfig> = {
},
],
},
'popular-today': {
id: 'popular-today',
titleKey: 'predict.feed.popular_today',
header: {
showBackButton: true,
showSearchButton: true,
},
tabs: [
{
id: 'all',
titleKey: 'predict.feed.popular_today',
defaultFilterId: 'all',
filters: {
static: [
createAllFilter({
status: 'open',
order: 'volume24hr',
limit: 12,
}),
],
dynamic: {
source: 'related-tags',
baseTagSlug: 'all',
baseParams: {
status: 'open',
order: 'volume24hr',
limit: 12,
},
},
},
},
],
},
};

const PREDICT_FEED_ID_SET = new Set<string>(PREDICT_FEED_IDS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,6 @@ describe('usePredictFeedConfig', () => {
['crypto', 'crypto'],
['sports', 'sports'],
['trending', 'all'],
['popular-today', 'all'],
])(
'passes baseTagSlug "%s" -> "%s" to usePredictFilterOptions',
(feedId, expectedSlug) => {
Expand Down
2 changes: 1 addition & 1 deletion app/components/UI/Predict/types/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export interface PredictMarketListRouteParams {
* Generic Predict feed route parameters.
*
* Consumed by the config-driven `PredictFeedView` (powers Sports / Politics /
* Crypto / Live / Trending / Popular Today). Carries stable IDs only — the
* Crypto / Live / Trending). Carries stable IDs only — the
* view resolves them into a render-ready config via `usePredictFeedConfig`.
* The route registration + deeplink parsing that populates these params lands
* separately (route + deeplinks ticket); the view reads them via `useRoute`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -593,10 +593,10 @@ describe('PredictFeedView', () => {
});

it('delays trackFeedViewed until a dynamic initialFilterId resolves', () => {
// Simulate entry from a Popular-Today home chip: initialFilterId targets a
// Simulate entry from a Popular Today home chip: initialFilterId targets a
// dynamic filter that hasn't loaded yet.
mockRouteParams = {
feedId: 'popular-today',
feedId: 'trending',
initialFilterId: 'soccer',
entryPoint: 'home_chip',
};
Expand All @@ -605,7 +605,7 @@ describe('PredictFeedView', () => {
// default ('all') because the dynamic option hasn't resolved yet.
mockUsePredictFeedConfig.mockReturnValue(
feedConfigResult({
feedId: 'popular-today',
feedId: 'trending',
dynamicFilters: { status: 'loading' },
activeFilterId: 'all',
}),
Expand All @@ -619,7 +619,7 @@ describe('PredictFeedView', () => {
// Dynamic filters resolve; the pending filter is now applied.
mockUsePredictFeedConfig.mockReturnValue(
feedConfigResult({
feedId: 'popular-today',
feedId: 'trending',
dynamicFilters: { status: 'ready' },
activeFilterId: 'soccer',
filters: [
Expand Down Expand Up @@ -649,7 +649,7 @@ describe('PredictFeedView', () => {

expect(mockTrackFeedViewed).toHaveBeenCalledTimes(1);
expect(mockTrackFeedViewed).toHaveBeenCalledWith({
feedId: 'popular-today',
feedId: 'trending',
tabId: 'basketball',
filterId: 'soccer',
trackingMode: 'focus',
Expand All @@ -659,15 +659,15 @@ describe('PredictFeedView', () => {

it('fires trackFeedViewed with fallback filter when dynamic loading fails (unavailable)', () => {
mockRouteParams = {
feedId: 'popular-today',
feedId: 'trending',
initialFilterId: 'soccer',
entryPoint: 'home_chip',
};

// Initially loading.
mockUsePredictFeedConfig.mockReturnValue(
feedConfigResult({
feedId: 'popular-today',
feedId: 'trending',
dynamicFilters: { status: 'loading' },
activeFilterId: 'all',
}),
Expand All @@ -679,7 +679,7 @@ describe('PredictFeedView', () => {
// Dynamic filters fail — status becomes unavailable, filter stays 'all'.
mockUsePredictFeedConfig.mockReturnValue(
feedConfigResult({
feedId: 'popular-today',
feedId: 'trending',
dynamicFilters: { status: 'unavailable' },
activeFilterId: 'all',
}),
Expand All @@ -698,14 +698,14 @@ describe('PredictFeedView', () => {
// response. The previous condition only fell back on 'unavailable', so
// isFilterSettled stayed false permanently.
mockRouteParams = {
feedId: 'popular-today',
feedId: 'trending',
initialFilterId: 'soccer',
entryPoint: 'home_chip',
};

mockUsePredictFeedConfig.mockReturnValue(
feedConfigResult({
feedId: 'popular-today',
feedId: 'trending',
dynamicFilters: { status: 'loading' },
activeFilterId: 'all',
}),
Expand All @@ -717,7 +717,7 @@ describe('PredictFeedView', () => {
// Dynamic filters loaded successfully but 'soccer' is not in the list.
mockUsePredictFeedConfig.mockReturnValue(
feedConfigResult({
feedId: 'popular-today',
feedId: 'trending',
dynamicFilters: { status: 'ready' },
activeFilterId: 'all',
// Only 'trending' is in the response — 'soccer' is absent.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ const INITIAL_SKELETON_COUNT = 4;
/**
* Generic, config-driven Predict feed screen.
*
* Powers every configured feed (Sports / Politics / Crypto / Live / Trending /
* Popular Today) from one component. Presentational: it consumes
* Powers every configured feed (Sports / Politics / Crypto / Live / Trending)
* from one component. Presentational: it consumes
* `usePredictFeedConfig` for the render-ready config + tab/filter selection
* state and `usePredictMarketList` for the active tab/filter's market data —
* it owns no hydration/dedup/fallback logic itself.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ describe('PredictPopularTodaySection', () => {
).toBeNull();
});

it('navigates to the Popular Today feed when the header is pressed', () => {
it('navigates to the Trending feed when the header is pressed', () => {
setFilterOptions({
filterOptions: [createFilterOption('elections', 'Elections')],
});
Expand All @@ -205,7 +205,7 @@ describe('PredictPopularTodaySection', () => {
expect(mockNavigate).toHaveBeenCalledWith(Routes.PREDICT.ROOT, {
screen: Routes.PREDICT.FEED,
params: {
feedId: 'popular-today',
feedId: 'trending',
entryPoint: PredictEventValues.ENTRY_POINT.HOME_SECTION,
},
});
Expand All @@ -216,7 +216,7 @@ describe('PredictPopularTodaySection', () => {
});
});

it('navigates to the Popular Today feed with the selected filter when a chip is pressed', () => {
it('navigates to the Trending feed with the selected filter when a chip is pressed', () => {
setFilterOptions({
filterOptions: [createFilterOption('elections', 'Elections')],
});
Expand All @@ -232,7 +232,7 @@ describe('PredictPopularTodaySection', () => {
expect(mockNavigate).toHaveBeenCalledWith(Routes.PREDICT.ROOT, {
screen: Routes.PREDICT.FEED,
params: {
feedId: 'popular-today',
feedId: 'trending',
initialFilterId: 'elections',
entryPoint: PredictEventValues.ENTRY_POINT.HOME_SECTION,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,25 +28,24 @@ import type {
import { PredictHomeSelectorsIDs } from '../../../../Predict.testIds';

/**
* Max number of filter chips rendered on the home section. The full
* `popular-today` feed shows the complete list; the home rail shows a compact
* prefix of the same ordered list.
* Max number of filter chips rendered on the home section. The home rail shows
* a compact prefix of the same ordered list as the Trending feed.
*/
const POPULAR_TODAY_FILTER_LIMIT = 10;
const SKELETON_COUNT = 8;
const DEFAULT_CHIP_ROW_COUNT = 2;

/**
* Derive the section's filter-options params from the canonical `popular-today`
* feed registry so there is a single source of truth. Crucially, we do NOT pass
* Derive the section's filter-options params from the canonical `trending` feed
* registry so there is a single source of truth. Crucially, we do NOT pass
* a top-level `limit` here: that keeps the React Query cache key identical to
* the full feed's `usePredictFilterOptions` call (which omits `limit`), so the
* home section and the feed share the same cached related-tags request instead
* of firing two. The display cap is applied client-side via
* `POPULAR_TODAY_FILTER_LIMIT` when slicing the returned options.
*/
const POPULAR_TODAY_FILTER_PARAMS: PredictFilterOptionsParams = (() => {
const dynamicConfig = resolvePredictFeedDynamicFilterConfig('popular-today');
const dynamicConfig = resolvePredictFeedDynamicFilterConfig('trending');

return {
source: dynamicConfig?.source ?? 'related-tags',
Expand Down Expand Up @@ -100,9 +99,9 @@ interface PredictPopularTodaySectionProps {
/**
* Predict home "Popular today" tag rail (PRED-917).
*
* Uses the same related-tag source as the full `popular-today` feed. The header
* opens the all-up Popular Today feed, while each chip opens that feed with the
* selected related-tag filter preselected.
* Uses the same related-tag source as the Trending feed. The header opens the
* all-up Trending feed, while each chip opens that feed with the selected
* related-tag filter preselected.
*/
const PredictPopularTodaySection: React.FC<PredictPopularTodaySectionProps> = ({
testID = PREDICT_POPULAR_TODAY_SECTION_TEST_IDS.SECTION,
Expand Down Expand Up @@ -137,10 +136,10 @@ const PredictPopularTodaySection: React.FC<PredictPopularTodaySectionProps> = ({
[rowCount],
);

const navigateToPopularToday = useCallback(
const navigateToTrending = useCallback(
(initialFilterId?: string) => {
const params: PredictFeedRouteParams = {
feedId: 'popular-today',
feedId: 'trending',
entryPoint: PredictEventValues.ENTRY_POINT.HOME_SECTION,
...(initialFilterId ? { initialFilterId } : {}),
};
Expand All @@ -159,8 +158,8 @@ const PredictPopularTodaySection: React.FC<PredictPopularTodaySectionProps> = ({
actionType: PredictEventValues.ACTION_TYPE.SEE_ALL,
entryPoint: PredictEventValues.ENTRY_POINT.HOME_SECTION,
});
navigateToPopularToday();
}, [navigateToPopularToday]);
navigateToTrending();
}, [navigateToTrending]);

const handleChipPress = useCallback(
(option: PredictFilterOption) => {
Expand All @@ -173,9 +172,9 @@ const PredictPopularTodaySection: React.FC<PredictPopularTodaySectionProps> = ({
isDynamicFilter: true,
entryPoint: PredictEventValues.ENTRY_POINT.HOME_SECTION,
});
navigateToPopularToday(option.id);
navigateToTrending(option.id);
},
[navigateToPopularToday],
[navigateToTrending],
);

if (!isLoading && chips.length === 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -428,15 +428,15 @@ describe('handlePredictUrl', () => {
});
});

it('parses filter separately from tab', async () => {
it('redirects Popular Today links to the selected Trending filter', async () => {
await handlePredictUrl({
predictPath: '?feed=popular-today&filter=elections',
});

expect(mockNavigate).toHaveBeenCalledWith(Routes.PREDICT.ROOT, {
screen: Routes.PREDICT.FEED,
params: {
feedId: 'popular-today',
feedId: 'trending',
initialFilterId: 'elections',
entryPoint: 'deeplink',
},
Expand Down Expand Up @@ -506,6 +506,21 @@ describe('handlePredictUrl', () => {
});
});

it('keeps the Popular Today redirect behind the home redesign flag', async () => {
jest.mocked(selectPredictHomeRedesignEnabledFlag).mockReturnValue(false);

await handlePredictUrl({
predictPath: '?feed=popular-today&filter=elections',
});

expect(mockNavigate).toHaveBeenCalledWith(Routes.PREDICT.ROOT, {
screen: Routes.PREDICT.MARKET_LIST,
params: {
entryPoint: 'deeplink',
},
});
});

it('prioritizes market parameter over a generic feed', async () => {
await handlePredictUrl({ predictPath: '?feed=sports&market=123' });

Expand Down
14 changes: 7 additions & 7 deletions app/core/DeeplinkManager/handlers/legacy/handlePredictUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ const marketTarget = (
* - https://link.metamask.io/predict?feed=world-cup&tab=live
* - https://link.metamask.io/predict?feed=sports
* - https://link.metamask.io/predict?feed=sports&tab=all&filter=live
* - https://link.metamask.io/predict?feed=popular-today&filter=elections
* - https://link.metamask.io/predict?feed=popular-today&filter=elections (redirects to Trending)
* - https://link.metamask.io/predict?feed=trending&q=bitcoin
*
* Origin/EntryPoint handling:
Expand All @@ -248,7 +248,8 @@ const marketTarget = (
* - No market param: Navigate to market list
* - market=X or marketId=X: Navigate directly to market details for market X
* - feed=world-cup: Navigate to the dedicated World Cup feed when enabled
* - feed=<known generic id> (sports/politics/crypto/live/trending/popular-today): Navigate to the generic PredictFeedView when the predictHomeRedesign flag is enabled (tab -> initialTabId, filter -> initialFilterId)
* - feed=<known generic id> (sports/politics/crypto/live/trending): Navigate to the generic PredictFeedView when the predictHomeRedesign flag is enabled (tab -> initialTabId, filter -> initialFilterId)
* - feed=popular-today: Redirect to the Trending feed while preserving its filter
* - Unknown feed (or flag disabled): Fall back to the Predict market list
* - Optional tab param when no market: Open feed on a specific tab
* - query=X or q=X: Open feed with search overlay showing results for X
Expand All @@ -260,6 +261,8 @@ const resolvePredictTarget = ({
// Parse navigation parameters from URL
const navParams = parsePredictNavigationParams(predictPath);
DevLogger.log('[handlePredictUrl] Parsed navigation parameters:', navParams);
const genericFeed =
navParams.feed === 'popular-today' ? 'trending' : navParams.feed;

// Determine entry point:
// - Base is origin if provided, otherwise 'deeplink'
Expand All @@ -281,12 +284,9 @@ const resolvePredictTarget = ({
requestedTab: navParams.worldCupTab,
entryPoint,
});
} else if (
isPredictFeedId(navParams.feed) &&
getPredictHomeRedesignEnabled()
) {
} else if (isPredictFeedId(genericFeed) && getPredictHomeRedesignEnabled()) {
return genericFeedTarget({
feedId: navParams.feed,
feedId: genericFeed,
// worldCupTab holds the raw (unvalidated) tab value; the generic feed's
// sub-tab ids (e.g. basketball/all/live) are resolved by the view.
initialTabId: navParams.worldCupTab,
Expand Down
Loading