feat(client): surface Discover Weekly on Explore (web + native) - #14574
Conversation
Wires GET /users/:id/discover-weekly into the Explore page as a collection card, so the mix reads like a playlist even though it isn't one. sdk.users.getDiscoverWeekly is hand-written pending the next SDK regen, same as getSuggestedFollows in #14562, since npm run gen pulls the spec from a running node. The artwork is a checked-in asset rather than entity cover art: the mix has no playlist_id to hang an image on. Same reason there's no permalink -- clicking plays the mix in place (all 30 queued, starting at the top) rather than navigating. Rendered without a Carousel wrapper: there's exactly one card, so the scroll affordance is dead weight and a section heading would repeat the card's own title. Web only for now -- the native mobile app has a separate Explore screen. Depends on AudiusProject/api#1025 (merged). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Mirrors the web section. The native app composes its own Explore screen from a separate component set, so this is a parallel implementation rather than a shared one. Same shape as web: bundled artwork asset (the mix has no playlist_id to hang cover art on), press plays the mix in place with all 30 queued rather than navigating, and no ExploreSection wrapper since a heading would repeat the card's own title. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Promotes Discover Weekly from a card in the Explore stack to a proper
feature with its own destination.
- Dedicated page at /explore/discover-weekly (web) and a matching native
screen. Structured like a collection page -- artwork, title, play-all,
full track list -- but assembled from the History page's pieces, since
there's no collection entity to drive the real collection page.
- Full-width banner pinned to the top of Explore on both platforms,
replacing the card. This is a launch placement for a new feature, not
another content row, so it sits above the section stack.
- The same banner on the feed. Explore was the only entry point and it
takes a deliberate detour to reach; the feed is where people land.
- Analytics: banner view, banner click, page view, play-all. Every event
carries `surface` ('explore' | 'feed') so we can tell which entry point
actually drives listens, and `trackCount` so an impression on an empty
mix is distinguishable. Impressions fire once, and only after the mix
resolves, so a banner that then hides itself can't inflate the
click-through denominator.
The banner navigates rather than playing in place -- it's an entry point,
and the page it opens has the play-all.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds a play-all button to the native Discover Weekly screen, mirroring the web page: toggle when already on the first track, otherwise start the queue from the top. Web had one and native didn't. Also bounds the useDiscoverWeekly staleTime. It was Infinity with refetchOnMount: false, which meant a single failed or empty first fetch was permanent for the session -- nothing retried it, and the surfaces that hide themselves on an empty result stayed hidden until restart. The server already caches the mix for hours, so the infinite client staleTime bought nothing and only removed the recovery path. Verified: typecheck and lint clean; web play-all confirmed in the browser (starts playback, queues all 30). The native play-all is NOT verified on device -- the simulator has a Release-scheme build, which per AppDelegate.mm resolves its bundle through CodePush rather than Metro, so it kept executing a stale bundle. Verifying it needs a Dev-scheme build. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Verification status at mergeVerified in a running app:
Not verified:
Also in the last commit
Known gaps carried forward
|
Web Tests failed on responsiveCoverage.test.ts: the Discover Weekly page uses TrackTableLineup but had no entry in RESPONSIVE_TABLE_POLICIES, and that test governs the invariant that every shared-table consumer declares one. Adds a discoverWeeklyTracks policy, registers the page in the audited consumer list, and actually passes the policy to the table -- without that last part the entry would have satisfied the test while the table stayed non-responsive. Merge conflict in AppTabScreen.tsx was purely indentation: #14575 re-indented the Stack.Screen block. Took main's formatting and re-added the DiscoverWeeklyScreen registration at the matching depth. Full web suite passes locally (152 passed, 9 skipped). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Wires
GET /users/:id/discover-weeklyinto the Explore page on web and native, rendered as a collection card so the mix reads like a playlist.Depends on api#1025 and api#1026 — both merged and live in production.
What's here
useDiscoverWeekly— a plainuseQuery, not infinite. The mix is a fixed-size artifact, not a lineup you scroll; there is no page 2.sdk.users.getDiscoverWeekly— hand-written pending the next SDK regen, same asgetSuggestedFollowsin feat(client): personalize empty-feed follow suggestions #14562, sincenpm run genpulls the spec from a running node.packages/mobilecomposes its own Explore screen.Three decisions worth reviewing
The artwork is a checked-in asset. The mix has no
playlist_idto hang cover art on — Audius playlists are on-chain entities and the mix is computed per request. Styled to sit alongside the Hot & New playlist art.Clicking plays instead of navigating. No permalink to navigate to, so the card queues all 30 starting at the top. Closest thing to playlist behavior without a route.
No section heading or Carousel. One card, so the scroll affordance is dead weight and a heading would repeat the card's own title.
Known gaps
(user, year, week), which would also fix the mid-week drift noted in api#1025.Verification
tscand eslint clean across web, mobile, and sdk. The onecommonerror (getDiscoverWeeklynot onUsersApi) is the known SDK-dist parity issue — the builtdist/index.d.tstypessdk.usersas the generated class, so the already-mergeduseSuggestedFollowsproduces an identical error. Resolves on the next regen.🤖 Generated with Claude Code