feat(discover-v2): add Predictions placement carousel#7420
feat(discover-v2): add Predictions placement carousel#7420DanielSinclair wants to merge 5 commits intodaniel/discover-v2-perps-sparklinesfrom
Conversation
06fc4c8 to
38cbab7
Compare
537c9f8 to
28edc23
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: 537c9f8a32
ℹ️ 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".
| import { getMarketColors } from '@/features/polymarket/utils/getMarketColor'; | ||
| import { getHighContrastColor } from '@/hooks/useAccountAccentColor'; | ||
| import { resolvePolymarketCardColor } from '@/features/polymarket/utils/getPolymarketCardColor'; |
There was a problem hiding this comment.
Re-add removed color helper imports
processRawPolymarketPosition and processRawPolymarketOptimizedEvent still call getImagePrimaryColor and getHighContrastColor, but this import block no longer brings those symbols into scope. In production Babel builds this becomes a runtime ReferenceError the first time those paths execute (e.g., loading Polymarket positions), so position fetching can fail for users with open/closed positions.
Useful? React with 👍 / 👎.
| if (placement.id === PLACEMENT_IDS.PERPS) { | ||
| items = placement.items.filter(item => item.ref.source === 'hyperliquid' && markets[item.ref.id] !== undefined); | ||
| } else if (placement.id === PLACEMENT_IDS.PREDICTIONS) { | ||
| items = placement.items.filter(item => item.ref.source === 'polymarket' && eventsById[item.ref.id] !== undefined); |
There was a problem hiding this comment.
Preserve prediction items until events resolve
Filtering predictions to eventsById[item.ref.id] !== undefined drops every card while event fetches are still in flight, so once placement loading flips to false the Discover screen hides the Predictions carousel entirely (showPredictionsPlacement depends on items.length). This makes the new per-card skeleton fallback effectively unreachable and can leave the section missing on slow or partially failed event hydration.
Useful? React with 👍 / 👎.
|
🧪 Flashlight Performance Report (AWS Device Farm) 🔀 Commit: 2ae611d
|
7550c1e to
828d6e5
Compare
38cbab7 to
4dfd3b0
Compare
Replaces single event-icon fetch with a color waterfall for accurate card colors: series color → market image → event image → seed color. Corrects closed/ended event filtering to run post-transform so the closed field is available.
Prevents image color extraction from blocking indefinitely by adding a 600ms timeout. Deduplicates the fetch when market and event share the same image URL by resolving once and reusing the result.
Fetches Polymarket events for Discover placement item IDs and extends the composition store to include predictions. - discoverPredictionsStore.ts: createQueryStore; keepPreviousData preserves events during refresh - discoverPlacementsStore.ts: adds eventsById composition and predictions item filtering - polymarketEventsStore.ts: removes dead-code prefetchPolymarketEvents() no-arg wrapper
Adds the Predictions carousel section to DiscoverHome using PolymarketEventsListItem with injected onPress. - PolymarketEventsListItem: accepts optional onPress override - DiscoverHome: adds Predictions carousel section, renderPredictionCard, showPredictionsPlacement guard
828d6e5 to
3c63e64
Compare
4dfd3b0 to
9fb11fe
Compare
ibrahimtaveras00
left a comment
There was a problem hiding this comment.
Looks good on both OS's ✅
| async function withTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T | null> { | ||
| let timeoutId: ReturnType<typeof setTimeout> | undefined; | ||
| const timeout = new Promise<null>(resolve => { | ||
| timeoutId = setTimeout(() => resolve(null), timeoutMs); | ||
| }); | ||
|
|
||
| return await Promise.race([promise, timeout]) | ||
| .catch(() => null) | ||
| .finally(() => { | ||
| if (timeoutId) clearTimeout(timeoutId); | ||
| }); | ||
| } |
There was a problem hiding this comment.
nit: given this isn't an uncommon pattern (same thing exists in LedgerSigner.ts) might be worth putting in its own util file.
There was a problem hiding this comment.
A few things here:
- Defaulting to using the
seriesColorof the first market in an event doesn't make a ton of sense. Firstly because most markets don't have a series color, and second because that color represents the color for that particular market for the event. We display the top two markets for every event, and the color for the first market doesn't have any meaningful connection to the event itself. Deriving the color from the event's image makes more sense visually and is the intention of the designs. - For the same reasons as above, we shouldn't be trying to use the first market's image color.
- The color thresholds are fine and that's a good addition.
- Could have been extracted to helper if needed in multiple places but could have made separate PR for changing how the color was calculated.
There was a problem hiding this comment.
Same feedback as perps carousel, would move all of this to dedicated component.
There was a problem hiding this comment.
Not understanding the changes here. We already don't fetched closed events and now we just do unnecessary transformations on the game events that are ended which get filtered out anyway.
| const filteredEvents = events.filter(event => event.ended !== true); | ||
| const processedEvents = await Promise.all(filteredEvents.map(event => processRawPolymarketEvent(event))); | ||
| const processedEvents = (await Promise.all(events.map(event => processRawPolymarketEvent(event)))).filter( | ||
| event => !event.closed && event.ended !== true | ||
| ); |
There was a problem hiding this comment.
Have some question from src/features/polymarket/stores/polymarketSportsEventsStore.ts comment
| import { time } from '@/utils/time'; | ||
| import { shallowEqual } from '@/worklets/comparisons'; | ||
|
|
||
| export type DiscoverPredictionsFetchData = { |
|
|
||
| return { | ||
| enabled: predictions && eventIds.length > 0, | ||
| eventIdsKey: eventIds.join(','), |
There was a problem hiding this comment.
I'm assuming this was done as some optimization for the equality checker? From my understanding this would be equivalent to just using the array directly. Regardless it's not worth transforming the data into a format we have to transform it back from just for that sake.
| }) | ||
| ); | ||
|
|
||
| async function fetchDiscoverPredictions({ eventIdsKey }: DiscoverPredictionsParams): Promise<DiscoverPredictionsFetchData> { |
There was a problem hiding this comment.
We should fetch the events directly from the /events endpoint here so that we only fire a single request rather than n requests all events (ie events?id=16167&id=16166).
Can abstract this into a generic fetchPolymarketEvents(eventIds: string[]) and then hook it into whatever transforms you need to do.

APP-3530, APP-3666
What changed
resolvePolymarketCardColor— replaces the prior single event-icon fetch with a waterfall: (1) marketseriesColorfield, (2)getImagePrimaryColoron the first active market's image, (3)getImagePrimaryColoron the event image, (4)getColorBySeedfallbackgetSharedImageColorThemesPolymarketEventobjectsuseDiscoverPredictionsStore— query store fetching fullPolymarketEventobjects for eachPlacementItemID viausePolymarketEventStore;keepPreviousDatapreserves cards between refreshesuseDiscoverPlacementsextended to composeeventsByIdfromuseDiscoverPredictionsStoreand filter predictionsPlacementItem[]to those with a resolved eventPolymarketEventsListItem— extended with an optionalonPressoverride so the Discover carousel injects placement analytics before delegating to the event screenDiscoverHomeadds a PredictionsMarketCarouselsection usingPolymarketEventsListItemas the card renderer; falls back toLoadingSkeletonwhile the event resolvesScreen recordings / screenshots
What to test