Skip to content

Feed: three-tab UI (For You / Following / Uploads Only)#14237

Open
dylanjeffers wants to merge 3 commits intomainfrom
feed-improvements
Open

Feed: three-tab UI (For You / Following / Uploads Only)#14237
dylanjeffers wants to merge 3 commits intomainfrom
feed-improvements

Conversation

@dylanjeffers
Copy link
Copy Markdown
Contributor

Summary

Replaces the legacy "All Posts / Original Posts / Reposts" filter pills on the feed page with a three-tab UI:

Tab Source Filter
For You (default) new useForYouFeed composer n/a
Following existing useFeed FeedFilter.ALL
Uploads Only existing useFeed FeedFilter.ORIGINAL

Implemented on web (desktop + mobile-web) and React Native mobile. New FeedTab enum and feedTab Redux slot drive which lineup renders; the legacy FeedFilter enum stays for backwards-compat with persisted state and as the param the SDK feed call still accepts. Reposts-only is no longer a top-level option since it was niche and the social-graph view is already covered by Following.

Status

  • ✅ Three-tab UI complete on web and mobile, fully wired through Redux + TanStack Query
  • ✅ Following / Uploads Only — production-ready (just rebinds existing useFeed filter modes to the new tabs)
  • ⚠️ For You — interim client-side blending. See below.

For You — interim approach

Right now the "For You" feed is composed on the client by blending multiple existing SDK lineup hooks. Each 10-slot page has the following weights, interleaved with a fixed pattern:

Source Weight Signal
getUserRecommendedTracks 50% Server-side personalization (listen history, follows, likes — already computed by discovery-provider)
useFeed with FeedFilter.ORIGINAL 20% Explicit social graph — original uploads from people the user follows
getTrendingTracks (week) 10% Recency-decayed popularity
getUndergroundTrendingTracks 10% Discovery / serendipity, surfaces emerging artists (Audius-native indie signal)
Filler from Recommended 10%

Dedupes by track_id (Recommended wins ties) and filters out tracks the user has already favorited (via useFavoritedTracks) so saved music isn't re-recommended. loadNextPage advances every source whose hasNextPage is still true in parallel.

Why client-side as interim

  • Reuse the server's ranking — each source is already personalized/ranked by discovery-provider; we trust the per-source ordering and blend rather than re-score on the client. Re-ranking would need engagement features (plays, completion rate, recency-of-upload, view-through) that aren't currently exposed to the client.
  • Ships today on existing endpoints — no discovery-provider changes required, no new SDK methods, no migrations. The full UI delivers value immediately while the proper server-side endpoint is designed.
  • Easy to swap outuseForYouFeed exposes the same { trackIds, isPending, hasNextPage, loadNextPage } interface as useFeed. When the dedicated endpoint lands, the only change in the page components is which hook they call.

Why this is an interim — known limitations

  • N+1 query fan-out: every feed view hits four endpoints in parallel rather than one. Slower TTFB, higher discovery-provider load, more bandwidth on mobile.
  • Limited blending fidelity: a fixed-pattern interleave can't account for the user's actual engagement with the candidates we surface. A server endpoint can do proper diversity (per-artist, per-genre), recency decay tuned to the user's session cadence, exploration/exploitation tradeoff, and feature-store-backed ranking.
  • No cross-source ranking: we never compare "is this trending track better for this user than that recommended track" — we just round-robin. A server endpoint can score every candidate against a unified model.
  • Pagination is awkward: when one source runs dry, the slot collapses into Recommended; we can't ask the server to "give me the next 10 personalized items" because there's no single source of truth.
  • No serving-side caching: the same composition recomputes on every cold load.

Follow-up — proper implementation

A dedicated discovery-provider endpoint (e.g. GET /v1/users/<id>/feed/for-you) that does the candidate generation + ranking + diversity server-side, returning a single ranked list. The client hook becomes a thin pagination wrapper, identical in shape to useFeed, and this PR's useForYouFeed is replaced with that thin wrapper. The candidate generation can start with the same four sources (recommended / following / trending / underground) and grow into a feature-store + scoring model over time.

Test plan

  • Open feed page → defaults to "For You" tab
  • Switch between For You / Following / Uploads Only and confirm each renders distinct content
  • Confirm Uploads Only excludes reposts (only original uploads from followed users)
  • Confirm Following matches previous "All Posts" behavior (uploads + reposts)
  • Verify For You feed loads more on scroll without errors
  • Verify selected tab persists across reloads (redux-persist)
  • Mobile: confirm horizontal pill row scrolls and responds to taps
  • Confirm tracks the user has already favorited do not appear in the For You feed
  • Analytics: `FEED_CHANGE_VIEW` fires with the new `FeedTab` value on tab switch

🤖 Generated with Claude Code

dylanjeffers and others added 3 commits May 5, 2026 18:34
Introduce a FeedTab enum (FOR_YOU/FOLLOWING/UPLOADS_ONLY) separate
from the existing FeedFilter enum so the new tab UI can drive which
lineup is rendered without coupling to the SDK filter parameter. The
new feedTab Redux slot defaults to FOR_YOU and is persisted alongside
feedFilter.

useForYouFeed composes four candidate streams:
  - getUserRecommendedTracks (server-side personalization, 50%)
  - useFeed with FeedFilter.ORIGINAL (social-graph signal, 20%)
  - useTrending (week range, cultural-recency signal, 10%)
  - useTrendingUnderground (discovery, 10%)
Slots are interleaved with a fixed 10-item pattern. Tracks the user
has already favorited are filtered out so they're not recycled as
"new" recommendations. Pagination advances every source whose
hasNextPage is still true in parallel.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Render For You / Following / Uploads Only as the feed page's primary
selector on both desktop and mobile-web, driven by the new feedTab
Redux slot.

  - For You renders the new useForYouFeed lineup
  - Following renders useFeed with FeedFilter.ALL (uploads + reposts
    of followed users — current behavior)
  - Uploads Only renders useFeed with FeedFilter.ORIGINAL (no reposts)

The legacy "Reposts only" filter is retired from the UI; FeedFilter
itself stays for backwards-compat with persisted state and is still
the parameter the SDK feed call accepts under the hood. The unused
FeedFilters / FeedFilterDrawer / FeedFilterButton components are
removed. The FEED_CHANGE_VIEW analytics event is widened to accept
either FeedFilter or FeedTab as `view`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirror the web feed page: render a horizontal pill row with For You /
Following / Uploads Only above the lineup, dispatching setFeedTab on
change. The legacy FeedFilter drawer + FeedFilterButton are removed,
along with the 'FeedFilter' modal slot from the shared modals state
since nothing opens it anymore.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 6, 2026

⚠️ No Changeset found

Latest commit: f84df44

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant