feat(discover-v2): scaffold featured markets carousel placements#7417
feat(discover-v2): scaffold featured markets carousel placements#7417DanielSinclair wants to merge 4 commits intodaniel/placements-apifrom
Conversation
91c9a66 to
b5f5ba8
Compare
4eb7984 to
3d587fb
Compare
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 91c9a66f8d
ℹ️ 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 showPerpsPlacement = availability.perps && isLoading; | ||
| const showPredictionsPlacement = availability.predictions && isLoading; | ||
| const showPlacements = showPerpsPlacement || showPredictionsPlacement; |
There was a problem hiding this comment.
Keep placement sections rendered after fetch completes
showPerpsPlacement and showPredictionsPlacement are gated on isLoading, so both sections are unmounted as soon as usePlacementsStore reaches success (or when cached state hydrates as non-loading). In practice this means enabled placements are only visible during a short loading window and can disappear permanently for warm-cache users, which defeats the carousel placement surface this change is introducing.
Useful? React with 👍 / 👎.
| itemHeight={CARD_HEIGHT} | ||
| itemWidth={CARD_WIDTH} | ||
| renderItem={(_item, _helpers) => <Box />} | ||
| onSeeAll={() => undefined} |
There was a problem hiding this comment.
Remove dead “See all” CTA until navigation exists
Both placement carousels render a visible “See all” action but pass onSeeAll={() => undefined}, so taps do nothing besides analytics. For users with placements enabled, this exposes a non-functional primary affordance and creates a dead-end interaction; the button should be hidden/disabled until a destination is wired.
Useful? React with 👍 / 👎.
|
🧪 Flashlight Performance Report (AWS Device Farm) 🔀 Commit: 3133b38
|
Clears out the old Perps/Predictions feature card surfaces before the new placement carousels are introduced.
Pure path migration — isolated so reviewers see only the move.
Generic horizontal carousel component with shimmer skeleton loading, See All button, analytics trackPress slot, and variable-width item support. Used by both Perps and Predictions placements.
Adds Perps and Predictions carousel sections to DiscoverHome driven by the availability store. Renders skeleton placeholders while placement data loads; sections are hidden once loaded since composition stores land in the domain branches.
b5f5ba8 to
6bbfc95
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6bbfc95b47
ℹ️ 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 testNetwork = useSyncDiscoverPlacementAvailabilityNetwork(); | ||
| const availability = useDiscoverPlacementAvailability(); | ||
| const isLoading = usePlacementsStore(state => state.status === 'loading' || state.status === 'idle'); |
There was a problem hiding this comment.
Gate placements store subscription behind availability
usePlacementsStore is subscribed unconditionally, so createQueryStore will still kick off a placements fetch even when placements can never be shown (for example on testnet, or when discover_placements_enabled is off). This introduces unnecessary Firestore traffic and background work on every Discover visit in those environments; the subscription/fetch should be conditioned on placement availability instead of always mounting.
Useful? React with 👍 / 👎.
ibrahimtaveras00
left a comment
There was a problem hiding this comment.
Looks good on both OS's ✅
| analytics.track(event.discoverPlacementCardPressed, { | ||
| placementId, | ||
| placementScreen: placement?.screen, | ||
| placementTitle: title, | ||
| itemOrder: item.order, | ||
| marketId, | ||
| marketName, | ||
| marketSlug, | ||
| marketSymbol, | ||
| marketType: item.ref.source, | ||
| }); |
There was a problem hiding this comment.
I'm not sure much is gained here vs. just passing in the whole placement object as a "placement" field. If you want to query this data in amplitude you're going to want to separate out the top level type (perps vs. polymarket) anyway, so attempting to normalize the data here and committing to do that for all future iterations seems not necessary. Open to hearing if there is an argument for this shape though.
Would also allow us to have just one event "placementCardPressed" that would work on other future screens (since placements have a "screen" field).
| return <View style={{ width: itemWidths[index] ?? itemWidth }}>{renderItem(item, { trackPress })}</View>; | ||
| }, | ||
| [itemWidth, itemWidths, placement, placementId, renderItem, title] | ||
| ); |
There was a problem hiding this comment.
This whole trackPress pattern is not ideal imo. Would be much cleaner to let the individual components own the onPress or to just wrap the view here in a <ButtonPressAnimation> and adding a pressProps prop if you want to let consumers customize the scaleTo or anything else (which I don't think we even need).
| ))} | ||
| </View> | ||
| ) : ( | ||
| <Animated.FlatList |
There was a problem hiding this comment.
We're not using animated animated props, should just be a normal FlatList
| contentContainer: { | ||
| paddingHorizontal: CAROUSEL_HORIZONTAL_PADDING, | ||
| }, |
There was a problem hiding this comment.
You can just put gap: CARD_GAP here and remove all the ItemSeparatorComponent related logic.
| getItemWidth?: (item: T) => number; | ||
| keyExtractor: (item: T) => string; | ||
| loading?: boolean; | ||
| onSeeAll: () => void; |
There was a problem hiding this comment.
Would make the prop name clear that it's a press event (ie onPressSeeAll)
| itemHeight?: number; | ||
| itemWidth?: number; | ||
| getItemWidth?: (item: T) => number; | ||
| keyExtractor: (item: T) => string; |
There was a problem hiding this comment.
For any prop types that are meant to be directly drilled to the list, I would prefer using the list props directly through = Pick<FlatListProps<T>, 'data' | 'keyExtractor'> & { other props }
|
|
||
| export const HORIZONTAL_PADDING = 20; | ||
|
|
||
| const keyExtractor = (item: PlacementItem) => `${item.ref.source}:${item.ref.id}`; |
There was a problem hiding this comment.
prefer function keyExtractor declaration for module level definitions.
| {showPerpsPlacement && ( | ||
| <MarketCarousel | ||
| title={i18n.t(i18n.l.discover.placements.perps_title)} | ||
| placementId={PLACEMENT_IDS.PERPS} | ||
| data={[] as PlacementItem[]} | ||
| keyExtractor={keyExtractor} | ||
| itemHeight={CARD_HEIGHT} | ||
| itemWidth={CARD_WIDTH} | ||
| renderItem={(_item, _helpers) => <Box />} | ||
| onSeeAll={() => undefined} | ||
| loading={isLoading} | ||
| /> | ||
| )} |
There was a problem hiding this comment.
I know not the general pattern in this file (it's pretty old) but it would better to put the specific placement logic into its own components, so in this case PerpsMarketCarousel that subscribes to the state needed to decided if it should render, and just return null if not. Same thing for the predictions carousel.

APP-3670
What changed
MarketCarousel— a generic horizontalFlatListwith shimmer skeleton loading, a "See all" action, and variable-width item supportDiscoverHomesubscribes touseDiscoverPlacementAvailabilityStoreand renders aMarketCarouselsection for each active placement type; showsMarketCarouselskeleton slots while placement data is loadingScreen recordings / screenshots
What to test