Skip to content

feat(for-you): switch useForYouFeed to dedicated /users/{id}/feed/for-you endpoint - #14388

Merged
dylanjeffers merged 2 commits into
mainfrom
feat/for-you-feed-new-endpoint
May 22, 2026
Merged

feat(for-you): switch useForYouFeed to dedicated /users/{id}/feed/for-you endpoint#14388
dylanjeffers merged 2 commits into
mainfrom
feat/for-you-feed-new-endpoint

Conversation

@dylanjeffers

Copy link
Copy Markdown
Contributor

Summary

  • Adds getUserFeedForYou method to the generated UsersApi SDK (mirrors the pattern of getUserRecommendedTracks — same request shape minus timeRange, same Tracks response type)
  • Updates useForYouFeed to call the new method, dropping the now-unused timeRange param
  • The previous implementation fell back to the generic recommended-tracks endpoint; this wires the hook to the purpose-built pipeline introduced in feat(for-you): re-introduce /users/{id}/feed/for-you with lean 3-source pipeline api#817

Test plan

  • Feed page For You tab loads tracks from the new endpoint
  • Pagination (load more) works correctly
  • Unauthenticated users see an empty state (hook is disabled when currentUserId is null)
  • Explore page recommended tracks are unaffected (still use getUserRecommendedTracks)

🤖 Generated with Claude Code

…-you endpoint
The endpoint was re-introduced in AudiusProject/api#817 with a lean
3-source pipeline (in-network, trending, underground) + linear ranking +
diversity pass. Previous implementation fell back to the generic
recommended-tracks endpoint; this wires the hook to the purpose-built one.
Changes:
- Add GetUserFeedForYouRequest interface + getUserFeedForYou method to
the generated UsersApi (mirrors the pattern of getUserRecommendedTracks)
- Update useForYouFeed to call getUserFeedForYou, dropping the now-unused
timeRange param
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@changeset-bot

changeset-botBot commented May 22, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: d6c3742

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

@github-actions

github-actionsBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

🌐 Web preview ready

Preview URL:https://audius-web-preview-pr-14388.audius.workers.dev

Unique preview for this PR (deployed from this branch).
Workflow run

… hand-editing
The prior commit on this branch hand-added getUserFeedForYou to the generated
UsersApi.ts, which is the wrong layer — those files are produced by
openapi-generator from the API repo's swagger spec. Manual edits get wiped on
the next codegen run and the method name doesn't match the spec's operationId.
This re-runs `node ./src/sdk/api/generator/gen.js --spec <swagger>` against the
swagger from AudiusProject/api main (the for-you endpoint isn't in prod
swagger yet, so --env prod can't be used until it deploys). The generated
method is named `getUserForYouFeed` (from operationId "Get User For You Feed"),
not `getUserFeedForYou` as the hand-edit assumed — hook updated to match.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@dylanjeffers
dylanjeffers merged commit 57bf817 into mainMay 22, 2026
14 checks passed
@dylanjeffers
dylanjeffers deleted the feat/for-you-feed-new-endpoint branch May 22, 2026 23:42
dylanjeffers added a commit that referenced this pull request Jun 16, 2026
…ers -> .users)
authMiddleware backfills blockchainUserId/handle for users whose identity
row lacks them (the state of any guest / freshly signed-up user) by calling
the SDK. It used `req.app.get('audiusSdk').full.users.getUserAccount(...)`,
but the @audius/sdk instance has no `.full` namespace - `users` is a
top-level API. So `.full` is undefined and `.users` throws a synchronous
TypeError ("Cannot read properties of undefined (reading 'users')") on
EVERY new-user auth request (/users/update, /record_ip, etc).
Confirmed in prod logs:
TypeError: Cannot read properties of undefined (reading 'users')
at authMiddleware (build/src/authMiddleware.js:97:68)
msg: "Failed to update blockchainUserId/handle"
The bad accessor came in with the monorepo import (#14388) and only began
firing once #14474 (6/15) made loadAudiusSdk.cjs available so the SDK
actually initialized - matching the signup regression window. The
surrounding try/catch swallowed the error and called next(), so the
backfill silently never happened for new users.
Fix: use the correct accessor `audiusSdk.users.getUserAccount`. Also wrap
the now-live call in a 3s timeout (deferred into a promise so a
missing/misshapen sdk rejects instead of throwing synchronously), so that
once the call actually runs, a slow discovery lookup for a not-yet-indexed
new user degrades gracefully (logged, then next()) instead of stalling auth.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
dylanjeffers added a commit that referenced this pull request Jun 16, 2026
…ers -> .users)
authMiddleware backfills blockchainUserId/handle for users whose identity
row lacks them (the state of any guest / freshly signed-up user) by calling
the SDK. It used `req.app.get('audiusSdk').full.users.getUserAccount(...)`,
but the @audius/sdk instance has no `.full` namespace - `users` is a
top-level API. So `.full` is undefined and `.users` throws a synchronous
TypeError ("Cannot read properties of undefined (reading 'users')") on
EVERY new-user auth request (/users/update, /record_ip, etc).
Confirmed in prod logs:
TypeError: Cannot read properties of undefined (reading 'users')
at authMiddleware (build/src/authMiddleware.js:97:68)
msg: "Failed to update blockchainUserId/handle"
The bad accessor came in with the monorepo import (#14388) and only began
firing once #14474 (6/15) made loadAudiusSdk.cjs available so the SDK
actually initialized - matching the signup regression window. The
surrounding try/catch swallowed the error and called next(), so the
backfill silently never happened for new users.
Fix: use the correct accessor `audiusSdk.users.getUserAccount`.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
dylanjeffers added a commit that referenced this pull request Jun 16, 2026
…ers -> .users) (#14482)
## Problem
Users report failing/janky signups, with `POST
https://identityservice.audius.co/users/update` misbehaving ([Slack
thread](https://audius-internal.slack.com/archives/CA80RCL77/p1781637102823139)).
`authMiddleware` (which gates `/users/update`, `/record_ip`, and every
other authenticated endpoint) backfills `blockchainUserId`/`handle` for
any identity `Users` row that lacks them — i.e. **every guest / freshly
signed-up user**. It did this via:
```js
req.app.get('audiusSdk').full.users.getUserAccount({ ... })
```
But the `@audius/sdk` instance **has no `.full` namespace** — `users` is
a top-level API (`audiusSdk.users.getUserAccount`). So `.full` is
`undefined` and `.users` throws a **synchronous `TypeError`** on every
new-user auth request.
Confirmed in prod logs:
```
TypeError: Cannot read properties of undefined (reading 'users')
at authMiddleware (build/src/authMiddleware.js:97:68)
msg: "Failed to update blockchainUserId/handle"
```
The surrounding `try/catch` swallowed it and called `next()`, so the
request proceeded but the **backfill silently never happened** — new
identity rows never got `blockchainUserId`/`handle` set.
## Why it started now
The bad accessor came in with the **monorepo import** of
identity-service (#14388, 5/22), which rewrote `authMiddleware` to use
`@audius/sdk`. It was dormant until #14474 (6/15) shipped
`loadAudiusSdk.cjs` into the build, so the SDK actually initialized and
this line began firing — matching the regression window. (identity
hadn't been deployed in a while; 6/15 was the first monorepo image
promoted to prod.)
I verified prod is in the *benign* config otherwise:
`environment=production` is set in `identity-service-secret`, so the SDK
targets prod discovery — the issue is purely the wrong accessor, not SDK
misconfig.
## Fix
Use the correct accessor `audiusSdk.users.getUserAccount` in both
`authMiddleware` and `parameterizedAuthMiddleware`. One-token change per
call site.
## Notes
- Ray's `record_ip` hunch is a red herring: `/users/update` doesn't call
`recordIP`. (Though `/record_ip` is also gated by `authMiddleware`, so
it hit the same TypeError — likely the source of the confusion.)
- Same 6/15 batch also fixed a related latent crash: #14378 reads
`src/data/disposable_email_blocklist.conf` at signup via an unguarded
`fs.readFileSync`; that file wasn't copied into the build until #14474
(same one-liner that also added `loadAudiusSdk.cjs`). Worth hardening
that read separately.
- The batch's endpoint removals (#14458, #14472) are safe — the legacy
`packages/libs` methods that hit them aren't called by any current
client.
## Verification
- Confirmed `getUserAccount` lives on the top-level `UsersApi`
(`audiusSdk.users`) and there is no `.full` in the SDK instance shape
(`@audius/sdk` `index.d.ts`).
- Confirmed response shape `res.data.user` is still correct
(`UserAccountResponse.data: Account`, `Account.user: User`).
- Confirmed prod `environment=production`.
After deploy, the `TypeError ... reading 'users'` log should disappear
and `Failed to update blockchainUserId/handle` should drop to near-zero.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
dylanjeffers added a commit that referenced this pull request Jun 17, 2026
…k access (#14492)
## Problem
Opening the comment drawer on a track screen crashes the app with a
`TypeError` — **just by opening it, before any comment is sent**. This
is separate from the post-comment crash fixed in #14490.
## Root cause
The footer composer (`ComposerInput`) mounts on **every** comment-drawer
open and computes `timestamps` in a render-phase `useMemo`:
```ts
const { data: partialTrack } = useTrack(entityId, {
select: (track) => pick(track, ['duration', 'genre', 'release_date', 'access'])
})
...
const timestamps = useMemo(() => {
if (!partialTrack || !partialTrack.access.stream) return [] // 💥
...
}, [partialTrack, value])
```
`partialTrack` is a `pick(...)`, so it is **truthy even when the track
has no `access` field**. `access` is typed as required on `Track`, but
at runtime it is frequently absent — tracks hydrated from search,
lineup, or notification caches before gated-access enrichment have no
`access` yet. In that window `partialTrack.access.stream` dereferences
`undefined` and throws, crashing the app the instant the drawer opens.
Introduced in #14388 (`feat(for-you)`), which added this composer and
the unguarded `partialTrack.access.stream` read.
## Fix
Optional-chain the access read so it falls back to "no timestamps" when
access info isn't loaded yet — the same outcome as the gated /
no-stream-access path:
```ts
if (!partialTrack || !partialTrack.access?.stream) return []
```
This matches existing precedent in the codebase (e.g.
`track?.access?.download` in `DownloadSection.tsx`).
## Testing
- `eslint src/components/composer-input/ComposerInput.tsx` — passes
- `tsc --noEmit` (`@audius/mobile`) — passes, 0 errors
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
dylanjeffers added a commit that referenced this pull request Aug 20, 2026
…der or unfurl (#14570)
## Problem
Reported by Marcus:
[audius.co/rehoxx/just-for-tonight-wmellark-hoonds](https://audius.co/rehoxx/just-for-tonight-wmellark-hoonds)
still renders and plays, even though that artist's account is no longer
active.
The API already says it shouldn't. It returns `is_streamable: false`
whenever a track is deleted or its owner is inactive. But the shared
adapter listed the field in its **omit list** — introduced in #14388
under "Fields from API that are omitted in this model," simply because
`TrackMetadata` didn't have the field, not for any deliberate reason.
So the answer was computed by the API, sent over the wire, and deleted
on arrival. `is_streamable` appeared exactly twice in the entire client
codebase, and one of those was the line dropping it. With no signal, the
track page rendered normally, played normally, and SSR served the
track's title and artwork to crawlers and social unfurls.
## Change
- Stop omitting `is_streamable`; add it to `TrackMetadata` as optional.
- New `isTrackUnavailable` helper in common holds the semantics in one
place.
- Gate the track page on it across **web desktop, mobile web, and native
mobile**.
- SSR `+onRenderHtml` serves generic metadata, `noindex`, and no embed
player when the flag is false.
Two deliberate details:
- The check is an explicit `=== false`. Not every track source populates
the field, and an absent value must not read as unavailable.
- Deleted tracks are excluded from the helper, so they keep their
existing "deleted by artist" page.
## Copy
`This Track Isn't Available` / `This track can no longer be streamed on
Audius.`
Deliberately says nothing about the account. The same flag covers an
artist deactivating their own account *and* an account being suppressed
by moderation, and we shouldn't tell users an artist deleted their
account when that isn't what happened.
## Verification
SSR output for the reported URL now returns `robots: noindex`,
`og:title` "Track Unavailable • Audius", the default logo as `og:image`,
and `twitter:card: summary` — no track title, artist name, or artwork.
Desktop and mobile web checked against the live prod API; a normal
trending track still renders fully. `tsc` and eslint clean across
common/web/mobile.
⚠️ The **native mobile** change is typecheck- and lint-verified only —
it hasn't been run in a simulator. It mirrors the existing
`ProfileScreen` deactivated branch structurally, but the layout is
unproven.
## Related
The API-side half is
[AudiusProject/api#1023](AudiusProject/api#1023)
— the stream endpoint didn't enforce `is_streamable` either, so the raw
audio was reachable regardless of what the UI showed.
## Known gaps, not addressed here
- An inactive artist's **profile** page still reads "This Account No
Longer Exists / has been deleted" — same wrong-copy problem, keyed on
`is_deactivated`.
- Profile SSR still emits the artist's name, bio, and picture in og
tags.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for freeto 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

@dylanjeffers