Skip to content

feat(discover-v2): add featured perps markets carousel#7418

Open
DanielSinclair wants to merge 4 commits intodaniel/discover-v2-market-carouselsfrom
daniel/discover-v2-perps
Open

feat(discover-v2): add featured perps markets carousel#7418
DanielSinclair wants to merge 4 commits intodaniel/discover-v2-market-carouselsfrom
daniel/discover-v2-perps

Conversation

@DanielSinclair
Copy link
Copy Markdown
Contributor

@DanielSinclair DanielSinclair commented Apr 30, 2026

APP-3530, APP-3670

What changed

  • useDiscoverPlacements — composition store subscribing to usePlacementsStore and useHyperliquidMarketsStore; produces a filtered PlacementItem[] where only items with a loaded HyperliquidMarket are included, preventing broken cards before market data arrives
  • PerpMarketCard — displays HyperliquidMarket.baseSymbol, leverage badge from HyperliquidMarket.maxLeverage, and H1 percentChange (number) from useCandlestickStore; card width is computed dynamically from symbol character length via computePerpCardWidth
  • Deep-link navigation required three changes: PerpsNavigator now reads initialPerpsPage from route params via useStableValue to land directly on any page; navigateToPerpsSearch calls PerpsNavigation.navigate first (handles the already-open case) then triggers the app-level navigate; SheetFooter removes an InteractionManager delay that was blocking immediate Open Position response

Screen recordings / screenshots

What to test

  • Discover shows Perps carousel with cards for each enabled placement item
  • Tap a card → perp detail sheet opens for that market
  • Tap "See all" → Perps search screen opens directly
  • Open Position on the detail sheet responds without delay

Copy link
Copy Markdown
Contributor Author

DanielSinclair commented Apr 30, 2026

@github-actions
Copy link
Copy Markdown

Launch in simulator or device for e84f1db

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 637a37a1cd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const showPredictionsPlacement = availability.predictions && isLoading;
const showPlacements = showPerpsPlacement || showPredictionsPlacement;
const perpsPlacement = placements.find(placement => placement.id === PLACEMENT_IDS.PERPS);
const showPerpsPlacement = availability.perps && (isLoading || Boolean(perpsPlacement?.items.length));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep perps carousel mounted until market hydration completes

showPerpsPlacement only checks the placements store loading flag, but useDiscoverPlacements filters out all items until useHyperliquidMarketsStore has loaded matching markets. In the common case where placements finish first, isLoading becomes false while perpsPlacement.items is still empty, so the carousel disappears entirely instead of staying in a loading state. This causes visible flicker/blanking on slower market fetches and can make the featured perps section appear missing until a later re-render.

Useful? React with 👍 / 👎.

@github-actions
Copy link
Copy Markdown

Launch in simulator or device for b72ce46

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 30, 2026

🧪 Flashlight Performance Report (AWS Device Farm)

🔀 Commit: bda05d4

📎 View Artifacts

Metric Current Δ vs Baseline
Time to Interactive (TTI) 5638 ms
Average FPS 56.49
Average RAM 405.8 MB

Derives filtered Perps placement items from the raw placements store, excluding hyperliquid markets not yet loaded.
Perps market card with token icon, leverage badge, symbol, and percent-change arrow. Sized without a sparkline slot — sparklines land in the sparklines branch.
Deep-link from Discover Perps card to market detail (with explain sheet gate), and See All to search screen without going through account home. PerpsNavigator reads initialPerpsPage from route params; PerpsSearchScreen skips delayed mount when markets are already cached.
Connects PerpMarketCard as the renderItem for the Perps placement carousel, using the composition store for data and per-item width sizing.
@DanielSinclair DanielSinclair force-pushed the daniel/discover-v2-market-carousels branch from b5f5ba8 to 6bbfc95 Compare April 30, 2026 11:42
@DanielSinclair DanielSinclair force-pushed the daniel/discover-v2-perps branch from 637a37a to 7e10dd7 Compare April 30, 2026 11:42
@github-actions
Copy link
Copy Markdown

Launch in simulator or device for bda05d4

@DanielSinclair DanielSinclair requested review from maxbbb and removed request for akc2267 April 30, 2026 23:24
Copy link
Copy Markdown
Contributor

@ibrahimtaveras00 ibrahimtaveras00 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good on both OS's ✅

Comment on lines +40 to +48
const renderPerpCard = (item: PlacementItem, { trackPress }: { trackPress: PerpMarketCardProps['onPressTracked'] }) => (
<PerpMarketCard item={item} onPressTracked={trackPress} />
);

const getPerpCardWidth = (item: PlacementItem) => {
const symbol = useHyperliquidMarketsStore.getState().getMarket(item.ref.id)?.baseSymbol ?? item.ref.id;
return computePerpCardWidth({ symbol });
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mentioned in the previous PR in the stack moving the specific carousels to their own components, and this logic being needed I think is another good reason for that.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we just extend formatPriceChange in src/features/perps/utils.ts to work for both of these cases? Is the isFinite check needed?

market,
});

const hasSeenExplainSheet = device.get(['hasSeenPerpsExplainSheet']);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: since this is used in multiple files now, should go in a constant like HAS_SEEN_PERPS_EXPLAIN_KEY

Could also be cleaner to add a helper maybeNavigateToPerpsExplainSheet(navigationAction: () => void) so that this pattern is centralized instead of duplicated here and in navigateToPerps

Also I know this was already here, but probably makes more sense for all the perps nav utils to be in the navigateToPerps file.

.map(id => state.getPlacement(id))
.filter((placement): placement is Placement => placement !== undefined)
);
const isLoading = $(usePlacementsStore, state => state.getStatus('isInitialLoad')) as boolean;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as boolean is not needed

Comment on lines +35 to +42
export function computePerpCardWidth({ symbol }: { symbol?: string }): number {
const estimatedTextWidth = symbol
? symbol.length * ESTIMATED_SYMBOL_CHARACTER_WIDTH + SYMBOL_TEXT_BUFFER
: PERP_MARKET_NO_CHART_TEXT_MIN_WIDTH;
const textWidth = Math.max(PERP_MARKET_NO_CHART_TEXT_MIN_WIDTH, estimatedTextWidth);

return Math.min(PERP_MARKET_CARD_MAX_WIDTH, Math.ceil(PERP_MARKET_NO_CHART_FIXED_WIDTH + textWidth));
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was only added a couple days ago, but we do now have a measureTextSync that can get you the exact text width given some style. Using that here would make this cleaner.

</View>

<View style={styles.badgePositioner}>
<View
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing here with the borders


const accentColor = market.metadata?.colors?.color || market.metadata?.colors?.fallbackColor || '#3ECFAD';
const percentChange =
candlestickPercentChange ?? convertStoredPerpPriceChangeToPercent(market.priceChange['1h'] || market.priceChange['24h']);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 is valid here I assume, should be ??


if (!market) return null;

const accentColor = market.metadata?.colors?.color || market.metadata?.colors?.fallbackColor || '#3ECFAD';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use HYPERLIQUID_COLORS.green as the fallback here

);

const getPerpCardWidth = (item: PlacementItem) => {
const symbol = useHyperliquidMarketsStore.getState().getMarket(item.ref.id)?.baseSymbol ?? item.ref.id;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should use hyperliquidMarketsActions.getMarket(). A couple other places where you should use hyperliquidMarketsActions over useHyperliquidMarketsStore.getState().action()

flexDirection: 'row',
gap: 2,
minHeight: 20,
minWidth: 0,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you do not need any of the minWidth: 0 in this file. I assume this is from css conventions, but in react native it's not required.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants