feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag) - #14466

Merged
raymondjacobson merged 19 commits into
mainfrom
claude/collab-apps
Jun 16, 2026
Merged

feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag)#14466
raymondjacobson merged 19 commits into
mainfrom
claude/collab-apps

Conversation

@raymondjacobson

@raymondjacobsonraymondjacobson commented Jun 9, 2026

Copy link
Copy Markdown
Member

Phase 3 — the apps client for collaborative tracks, all behind the new collaborative_tracks feature flag (default off), so it ships inert. Pairs with go-openaudio #345/#346/#349 (ETL) and AudiusProject/api #932 (api). Reviewing as one PR per your request; commits are sliced by concern.

What's implemented (web + mobile)

DisplayTrackMetadata.collaborators + adapter; <TrackArtists> renders owner + comma-separated collaborators with ellipsis overflow on web tiles/track page and mobile TrackCard/TrackDetailsTile. Flag-off = the previous single owner link.

SDK + hooksTrackCollaborator EntityType; TracksApi.acceptTrackCollaboration / rejectTrackCollaboration (reject = decline or leave); collaborators in the upload metadata schema; upload adapter maps tagged users → numeric on-chain ids; tan-query useAccept/RejectTrackCollaboration.

Upload taggingwebCollaboratorsField (reuses SearchUsersModal/ArtistChip) and mobileCollaboratorField (self-contained inline search + chips), both in the edit-track form behind the flag.

Notifications + accept/declinetrack_collaborator_invite / track_collaborator_accept types, adapter, web + mobile components + routing. Web invite notification has inline Accept/Decline.

Leave (remove yourself after accepting) — web TrackMenu and mobile track-page overflow both gain "Remove Me as Collaborator", shown to accepted collaborators (not the owner).

Deferred follow-ups (called out)

  • Standalone pending-invites list page — accept/decline currently drives off the invite notification (covers the primary flow); a list can use GET /users/:id/collaboration_invites.
  • Editing drops pending invites — the edit form initializes from the API's accepted collaborators only, so re-saving a track with still-pending invites would drop them. Needs an owner-facing "all collaborators (incl. pending)" source.

Verification caveat

I could not run the apps typecheck/lint/build locally (the worktree's toolchain/deps don't match this environment). These changes are pattern-copies of the existing manager-invite/approve flow and are CI-pending — please let CI and your review be the gate. (Server-side: notification id hashing is already generic via trashid.HashifyJson, so the new types' data ids encode correctly with no api change.)

🤖 Generated with Claude Code

@changeset-bot

changeset-botBot commented Jun 9, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 0a970d9

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 Jun 9, 2026

Copy link
Copy Markdown
Contributor

🌐 Web preview ready

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

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

@raymondjacobsonraymondjacobson changed the title feat(web): collaborative tracks — comma-separated artist line (behind flag)feat(apps): collaborative tracks — comma-separated artist line, web + mobile (behind flag)Jun 9, 2026
@raymondjacobsonraymondjacobson changed the title feat(apps): collaborative tracks — comma-separated artist line, web + mobile (behind flag)feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag)Jun 9, 2026
raymondjacobsonand others added 18 commits June 16, 2026 15:09
… flag)
Display slice of the apps phase: renders a track's accepted collaborators as a
comma-separated artist line on web track tiles and the track page, behind a new
`collaborative_tracks` flag (default off). Ships inert.
- packages/common: `TrackMetadata.collaborators` (UserMetadata[]), mapped in
userTrackMetadataFromSDK and decoded like the owner. The field isn't in the
generated SDK Track type yet, so it's read via a structural extension pending
the SDK regen. New COLLABORATIVE_TRACKS feature flag.
- packages/web: <TrackArtists> renders the owner plus comma-separated
collaborators on one ellipsizing line. With the flag off (or no
collaborators) it returns a single owner <UserLink> — a safe drop-in. Wired
into desktop TrackTile, mobile-web TrackTile, and GiantTrackTile.
Next apps slices: mobile-native tiles, upload tagging UI, notifications +
accept UX, SDK write methods.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ind flag)
Mobile-native parity for the collaborator artist line, behind the
collaborative_tracks flag.
- <TrackArtists> (owner UserLink + collaborators) and <CollaboratorLinks>
(collaborators-only append), both flag-gated so they're no-ops when off.
- TrackCard: owner UserLink -> TrackArtists (collaborators added to the track
select). Wrapper centers to preserve the card's centered artist line.
- TrackDetailsTile: keeps the owner Text + UserBadges and appends
CollaboratorLinks, so the flag-off rendering is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- SDK: TrackCollaborator EntityType; TracksApi.acceptTrackCollaboration /
rejectTrackCollaboration (EntityManager Approve/Reject; reject = decline or
leave). collaborators added to UploadTrackMetadataSchema (numeric user ids).
- Upload adapter maps form collaborator user objects -> numeric ids on-chain.
- tan-query: useAcceptTrackCollaboration, useRejectTrackCollaboration.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- CollaboratorsField: search + add collaborators (reuses SearchUsersModal /
ArtistChip, like the invite-manager UI) with removable chips. Wired into
TrackMetadataFields behind the collaborative_tracks flag.
- collaborators added to the upload form schema (full user objects).
Mobile upload tagging deferred (needs a native user picker); data plumbing
already supports it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e (behind flag)
- Notification types track_collaborator_invite / track_collaborator_accept
(NotificationType + PushNotificationType + interfaces + union + adapter cases).
- Web: invite notification with inline Accept/Decline (useAccept/RejectTrack-
Collaboration), accept notification (display); routing in Notification.tsx.
TrackMenu gains a 'Remove Me as Collaborator' item shown to accepted
collaborators (not the owner) — the post-accept leave path.
- Mobile: invite/accept notification components + routing + navigation handlers
(navigate to the track).
Deferred follow-ups: mobile upload picker, mobile overflow leave action,
a pending-invites list page, and SDK read-type regen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Finishes the deferred mobile pieces.
- CollaboratorField: self-contained inline search (useSearchUserResults) +
removable chips, added to the mobile edit-track form behind the flag.
Selected users flow through the upload metadata adapter to numeric ids.
- "Remove Me as Collaborator" overflow action: new
OverflowAction.LEAVE_TRACK_COLLABORATION, row label + drawer callback
(rejectTrackCollaboration + toast), shown on the track page to accepted
collaborators (not the owner).
Known limitation: editing a track initializes collaborators from the API's
accepted list only, so re-saving could drop still-pending invites — needs an
owner-facing "all collaborators" source to fix.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rder
- SDK: the generated Notification union/parser threw on unknown types and the
adapter switch couldn't reference them. Add TrackCollaboratorNotification
(+ Action/ActionData) generated models and wire them into Notification.ts
(union + FromJSONTyped cases) so track_collaborator_invite/accept parse and
typecheck. Hand-authored pending an OpenAPI regen.
- Fix import/order lint in web Notification.tsx and TrackMenu.tsx, and mobile
TrackScreenDetailsTile.tsx.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ivate tracks
- Remove the collaborative_tracks feature flag and its gating everywhere; the
feature is always on now (display, upload tagging, notifications, leave).
- Fix the invite notification doing nothing on a private track: a pending
collaborator can't load an unlisted track (the API filters it), so the
`if (!track) return null` guard blanked the whole notification — including the
Accept/Decline buttons. Render off the inviter alone, fall back to "a track"
for the title, and act on the trackId carried by the notification.
- Add success/error toasts (and disable-while-submitting) to Accept/Decline so
it's no longer silent. Same null-guard relaxed on the accept notifications and
the mobile equivalents.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The API returns `collaborators` on track responses, but the generated SDK
Track/SearchTrack models' FromJSONTyped only copy known fields — so the field
was silently dropped during deserialization, and the adapter's structural cast
always read `undefined`. Result: accepted collaborators never rendered on track
pages/tiles even though the backend returned them.
Add `collaborators?: Array<User>` to the generated Track and SearchTrack models
(interface + FromJSONTyped + ToJSON, mirroring `user`) so the field survives,
and read it directly in the adapter.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Now that the API swagger declares `collaborators` (deployed), ran the real
generator (typescript-fetch v7.5.0) against api.audius.co's spec. The output for
Track.ts/SearchTrack.ts is byte-identical to the prior hand-edit (only a
generator-emitted whitespace differs), confirming the field is now genuinely
generated rather than hand-patched. Other models left untouched (unrelated spec
drift not pulled in); the hand-authored notification models remain as-is.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The edit form seeded its collaborators field from the track's accepted-only
`collaborators`, so saving dropped any still-pending invites (the ETL reconciles
the metadata set). Initialize the field from accepted + pending instead.
- SDK Track/SearchTrack: add `pendingCollaborators` (hand-edit stopgap; the API
swagger now declares `pending_collaborators` in AudiusProject/api#947, so the
next regen reproduces this).
- Track model + adapter: expose `pending_collaborators` (owner-only).
- Web + mobile edit screens: seed the collaborators field with
`[...collaborators, ...pending_collaborators]` so pending invites survive a
save. Display still uses accepted-only `collaborators` (no regression).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…modal
Match the "Add Collaborator" Figma flow and give collaborators its own box like
the other upload settings:
- CollaboratorsField is now a ContextualMenu box (like Visibility / Remix
Settings) rendered in EditTrackForm's settings list, showing tagged
collaborators as a preview and opening a modal.
- New CollaboratorsMenuFields modal: "Add Collaborator to this Track" + invite
copy, collaborator rows (artist + overflow menu to remove + "Invite Pending"),
and a full-width "Add Collaborator" button that reveals an inline artist
search with a results list.
- Removed the old inline field from TrackMetadataFields.
The form field shape (collaborators: UserMetadata[]) is unchanged, so the upload
adapter and mobile field stay compatible.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@raymondjacobson
raymondjacobson merged commit 77e25e4 into mainJun 16, 2026
19 checks passed
@raymondjacobson
raymondjacobson deleted the claude/collab-apps branch June 16, 2026 22:52
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

@raymondjacobson
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag) - #14466

Merged
raymondjacobson merged 19 commits into
mainfrom
claude/collab-apps
Jun 16, 2026
Merged

feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag)#14466
raymondjacobson merged 19 commits into
mainfrom
claude/collab-apps

Conversation

@raymondjacobson

@raymondjacobsonraymondjacobson commented Jun 9, 2026

Copy link
Copy Markdown
Member

Phase 3 — the apps client for collaborative tracks, all behind the new collaborative_tracks feature flag (default off), so it ships inert. Pairs with go-openaudio #345/#346/#349 (ETL) and AudiusProject/api #932 (api). Reviewing as one PR per your request; commits are sliced by concern.

What's implemented (web + mobile)

DisplayTrackMetadata.collaborators + adapter; <TrackArtists> renders owner + comma-separated collaborators with ellipsis overflow on web tiles/track page and mobile TrackCard/TrackDetailsTile. Flag-off = the previous single owner link.

SDK + hooksTrackCollaborator EntityType; TracksApi.acceptTrackCollaboration / rejectTrackCollaboration (reject = decline or leave); collaborators in the upload metadata schema; upload adapter maps tagged users → numeric on-chain ids; tan-query useAccept/RejectTrackCollaboration.

Upload taggingwebCollaboratorsField (reuses SearchUsersModal/ArtistChip) and mobileCollaboratorField (self-contained inline search + chips), both in the edit-track form behind the flag.

Notifications + accept/declinetrack_collaborator_invite / track_collaborator_accept types, adapter, web + mobile components + routing. Web invite notification has inline Accept/Decline.

Leave (remove yourself after accepting) — web TrackMenu and mobile track-page overflow both gain "Remove Me as Collaborator", shown to accepted collaborators (not the owner).

Deferred follow-ups (called out)

  • Standalone pending-invites list page — accept/decline currently drives off the invite notification (covers the primary flow); a list can use GET /users/:id/collaboration_invites.
  • Editing drops pending invites — the edit form initializes from the API's accepted collaborators only, so re-saving a track with still-pending invites would drop them. Needs an owner-facing "all collaborators (incl. pending)" source.

Verification caveat

I could not run the apps typecheck/lint/build locally (the worktree's toolchain/deps don't match this environment). These changes are pattern-copies of the existing manager-invite/approve flow and are CI-pending — please let CI and your review be the gate. (Server-side: notification id hashing is already generic via trashid.HashifyJson, so the new types' data ids encode correctly with no api change.)

🤖 Generated with Claude Code

@changeset-bot

changeset-botBot commented Jun 9, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 0a970d9

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 Jun 9, 2026

Copy link
Copy Markdown
Contributor

🌐 Web preview ready

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

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

@raymondjacobsonraymondjacobson changed the title feat(web): collaborative tracks — comma-separated artist line (behind flag)feat(apps): collaborative tracks — comma-separated artist line, web + mobile (behind flag)Jun 9, 2026
@raymondjacobsonraymondjacobson changed the title feat(apps): collaborative tracks — comma-separated artist line, web + mobile (behind flag)feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag)Jun 9, 2026
raymondjacobsonand others added 18 commits June 16, 2026 15:09
… flag)
Display slice of the apps phase: renders a track's accepted collaborators as a
comma-separated artist line on web track tiles and the track page, behind a new
`collaborative_tracks` flag (default off). Ships inert.
- packages/common: `TrackMetadata.collaborators` (UserMetadata[]), mapped in
userTrackMetadataFromSDK and decoded like the owner. The field isn't in the
generated SDK Track type yet, so it's read via a structural extension pending
the SDK regen. New COLLABORATIVE_TRACKS feature flag.
- packages/web: <TrackArtists> renders the owner plus comma-separated
collaborators on one ellipsizing line. With the flag off (or no
collaborators) it returns a single owner <UserLink> — a safe drop-in. Wired
into desktop TrackTile, mobile-web TrackTile, and GiantTrackTile.
Next apps slices: mobile-native tiles, upload tagging UI, notifications +
accept UX, SDK write methods.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ind flag)
Mobile-native parity for the collaborator artist line, behind the
collaborative_tracks flag.
- <TrackArtists> (owner UserLink + collaborators) and <CollaboratorLinks>
(collaborators-only append), both flag-gated so they're no-ops when off.
- TrackCard: owner UserLink -> TrackArtists (collaborators added to the track
select). Wrapper centers to preserve the card's centered artist line.
- TrackDetailsTile: keeps the owner Text + UserBadges and appends
CollaboratorLinks, so the flag-off rendering is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- SDK: TrackCollaborator EntityType; TracksApi.acceptTrackCollaboration /
rejectTrackCollaboration (EntityManager Approve/Reject; reject = decline or
leave). collaborators added to UploadTrackMetadataSchema (numeric user ids).
- Upload adapter maps form collaborator user objects -> numeric ids on-chain.
- tan-query: useAcceptTrackCollaboration, useRejectTrackCollaboration.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- CollaboratorsField: search + add collaborators (reuses SearchUsersModal /
ArtistChip, like the invite-manager UI) with removable chips. Wired into
TrackMetadataFields behind the collaborative_tracks flag.
- collaborators added to the upload form schema (full user objects).
Mobile upload tagging deferred (needs a native user picker); data plumbing
already supports it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e (behind flag)
- Notification types track_collaborator_invite / track_collaborator_accept
(NotificationType + PushNotificationType + interfaces + union + adapter cases).
- Web: invite notification with inline Accept/Decline (useAccept/RejectTrack-
Collaboration), accept notification (display); routing in Notification.tsx.
TrackMenu gains a 'Remove Me as Collaborator' item shown to accepted
collaborators (not the owner) — the post-accept leave path.
- Mobile: invite/accept notification components + routing + navigation handlers
(navigate to the track).
Deferred follow-ups: mobile upload picker, mobile overflow leave action,
a pending-invites list page, and SDK read-type regen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Finishes the deferred mobile pieces.
- CollaboratorField: self-contained inline search (useSearchUserResults) +
removable chips, added to the mobile edit-track form behind the flag.
Selected users flow through the upload metadata adapter to numeric ids.
- "Remove Me as Collaborator" overflow action: new
OverflowAction.LEAVE_TRACK_COLLABORATION, row label + drawer callback
(rejectTrackCollaboration + toast), shown on the track page to accepted
collaborators (not the owner).
Known limitation: editing a track initializes collaborators from the API's
accepted list only, so re-saving could drop still-pending invites — needs an
owner-facing "all collaborators" source to fix.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rder
- SDK: the generated Notification union/parser threw on unknown types and the
adapter switch couldn't reference them. Add TrackCollaboratorNotification
(+ Action/ActionData) generated models and wire them into Notification.ts
(union + FromJSONTyped cases) so track_collaborator_invite/accept parse and
typecheck. Hand-authored pending an OpenAPI regen.
- Fix import/order lint in web Notification.tsx and TrackMenu.tsx, and mobile
TrackScreenDetailsTile.tsx.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ivate tracks
- Remove the collaborative_tracks feature flag and its gating everywhere; the
feature is always on now (display, upload tagging, notifications, leave).
- Fix the invite notification doing nothing on a private track: a pending
collaborator can't load an unlisted track (the API filters it), so the
`if (!track) return null` guard blanked the whole notification — including the
Accept/Decline buttons. Render off the inviter alone, fall back to "a track"
for the title, and act on the trackId carried by the notification.
- Add success/error toasts (and disable-while-submitting) to Accept/Decline so
it's no longer silent. Same null-guard relaxed on the accept notifications and
the mobile equivalents.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The API returns `collaborators` on track responses, but the generated SDK
Track/SearchTrack models' FromJSONTyped only copy known fields — so the field
was silently dropped during deserialization, and the adapter's structural cast
always read `undefined`. Result: accepted collaborators never rendered on track
pages/tiles even though the backend returned them.
Add `collaborators?: Array<User>` to the generated Track and SearchTrack models
(interface + FromJSONTyped + ToJSON, mirroring `user`) so the field survives,
and read it directly in the adapter.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Now that the API swagger declares `collaborators` (deployed), ran the real
generator (typescript-fetch v7.5.0) against api.audius.co's spec. The output for
Track.ts/SearchTrack.ts is byte-identical to the prior hand-edit (only a
generator-emitted whitespace differs), confirming the field is now genuinely
generated rather than hand-patched. Other models left untouched (unrelated spec
drift not pulled in); the hand-authored notification models remain as-is.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The edit form seeded its collaborators field from the track's accepted-only
`collaborators`, so saving dropped any still-pending invites (the ETL reconciles
the metadata set). Initialize the field from accepted + pending instead.
- SDK Track/SearchTrack: add `pendingCollaborators` (hand-edit stopgap; the API
swagger now declares `pending_collaborators` in AudiusProject/api#947, so the
next regen reproduces this).
- Track model + adapter: expose `pending_collaborators` (owner-only).
- Web + mobile edit screens: seed the collaborators field with
`[...collaborators, ...pending_collaborators]` so pending invites survive a
save. Display still uses accepted-only `collaborators` (no regression).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…modal
Match the "Add Collaborator" Figma flow and give collaborators its own box like
the other upload settings:
- CollaboratorsField is now a ContextualMenu box (like Visibility / Remix
Settings) rendered in EditTrackForm's settings list, showing tagged
collaborators as a preview and opening a modal.
- New CollaboratorsMenuFields modal: "Add Collaborator to this Track" + invite
copy, collaborator rows (artist + overflow menu to remove + "Invite Pending"),
and a full-width "Add Collaborator" button that reveals an inline artist
search with a results list.
- Removed the old inline field from TrackMetadataFields.
The form field shape (collaborators: UserMetadata[]) is unchanged, so the upload
adapter and mobile field stay compatible.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@raymondjacobson
raymondjacobson merged commit 77e25e4 into mainJun 16, 2026
19 checks passed
@raymondjacobson
raymondjacobson deleted the claude/collab-apps branch June 16, 2026 22:52
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

@raymondjacobson
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag) - #14466

Merged
raymondjacobson merged 19 commits into
mainfrom
claude/collab-apps
Jun 16, 2026
Merged

feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag)#14466
raymondjacobson merged 19 commits into
mainfrom
claude/collab-apps

Conversation

@raymondjacobson

@raymondjacobsonraymondjacobson commented Jun 9, 2026

Copy link
Copy Markdown
Member

Phase 3 — the apps client for collaborative tracks, all behind the new collaborative_tracks feature flag (default off), so it ships inert. Pairs with go-openaudio #345/#346/#349 (ETL) and AudiusProject/api #932 (api). Reviewing as one PR per your request; commits are sliced by concern.

What's implemented (web + mobile)

DisplayTrackMetadata.collaborators + adapter; <TrackArtists> renders owner + comma-separated collaborators with ellipsis overflow on web tiles/track page and mobile TrackCard/TrackDetailsTile. Flag-off = the previous single owner link.

SDK + hooksTrackCollaborator EntityType; TracksApi.acceptTrackCollaboration / rejectTrackCollaboration (reject = decline or leave); collaborators in the upload metadata schema; upload adapter maps tagged users → numeric on-chain ids; tan-query useAccept/RejectTrackCollaboration.

Upload taggingwebCollaboratorsField (reuses SearchUsersModal/ArtistChip) and mobileCollaboratorField (self-contained inline search + chips), both in the edit-track form behind the flag.

Notifications + accept/declinetrack_collaborator_invite / track_collaborator_accept types, adapter, web + mobile components + routing. Web invite notification has inline Accept/Decline.

Leave (remove yourself after accepting) — web TrackMenu and mobile track-page overflow both gain "Remove Me as Collaborator", shown to accepted collaborators (not the owner).

Deferred follow-ups (called out)

  • Standalone pending-invites list page — accept/decline currently drives off the invite notification (covers the primary flow); a list can use GET /users/:id/collaboration_invites.
  • Editing drops pending invites — the edit form initializes from the API's accepted collaborators only, so re-saving a track with still-pending invites would drop them. Needs an owner-facing "all collaborators (incl. pending)" source.

Verification caveat

I could not run the apps typecheck/lint/build locally (the worktree's toolchain/deps don't match this environment). These changes are pattern-copies of the existing manager-invite/approve flow and are CI-pending — please let CI and your review be the gate. (Server-side: notification id hashing is already generic via trashid.HashifyJson, so the new types' data ids encode correctly with no api change.)

🤖 Generated with Claude Code

@changeset-bot

changeset-botBot commented Jun 9, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 0a970d9

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 Jun 9, 2026

Copy link
Copy Markdown
Contributor

🌐 Web preview ready

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

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

@raymondjacobsonraymondjacobson changed the title feat(web): collaborative tracks — comma-separated artist line (behind flag)feat(apps): collaborative tracks — comma-separated artist line, web + mobile (behind flag)Jun 9, 2026
@raymondjacobsonraymondjacobson changed the title feat(apps): collaborative tracks — comma-separated artist line, web + mobile (behind flag)feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag)Jun 9, 2026
raymondjacobsonand others added 18 commits June 16, 2026 15:09
… flag)
Display slice of the apps phase: renders a track's accepted collaborators as a
comma-separated artist line on web track tiles and the track page, behind a new
`collaborative_tracks` flag (default off). Ships inert.
- packages/common: `TrackMetadata.collaborators` (UserMetadata[]), mapped in
userTrackMetadataFromSDK and decoded like the owner. The field isn't in the
generated SDK Track type yet, so it's read via a structural extension pending
the SDK regen. New COLLABORATIVE_TRACKS feature flag.
- packages/web: <TrackArtists> renders the owner plus comma-separated
collaborators on one ellipsizing line. With the flag off (or no
collaborators) it returns a single owner <UserLink> — a safe drop-in. Wired
into desktop TrackTile, mobile-web TrackTile, and GiantTrackTile.
Next apps slices: mobile-native tiles, upload tagging UI, notifications +
accept UX, SDK write methods.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ind flag)
Mobile-native parity for the collaborator artist line, behind the
collaborative_tracks flag.
- <TrackArtists> (owner UserLink + collaborators) and <CollaboratorLinks>
(collaborators-only append), both flag-gated so they're no-ops when off.
- TrackCard: owner UserLink -> TrackArtists (collaborators added to the track
select). Wrapper centers to preserve the card's centered artist line.
- TrackDetailsTile: keeps the owner Text + UserBadges and appends
CollaboratorLinks, so the flag-off rendering is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- SDK: TrackCollaborator EntityType; TracksApi.acceptTrackCollaboration /
rejectTrackCollaboration (EntityManager Approve/Reject; reject = decline or
leave). collaborators added to UploadTrackMetadataSchema (numeric user ids).
- Upload adapter maps form collaborator user objects -> numeric ids on-chain.
- tan-query: useAcceptTrackCollaboration, useRejectTrackCollaboration.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- CollaboratorsField: search + add collaborators (reuses SearchUsersModal /
ArtistChip, like the invite-manager UI) with removable chips. Wired into
TrackMetadataFields behind the collaborative_tracks flag.
- collaborators added to the upload form schema (full user objects).
Mobile upload tagging deferred (needs a native user picker); data plumbing
already supports it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e (behind flag)
- Notification types track_collaborator_invite / track_collaborator_accept
(NotificationType + PushNotificationType + interfaces + union + adapter cases).
- Web: invite notification with inline Accept/Decline (useAccept/RejectTrack-
Collaboration), accept notification (display); routing in Notification.tsx.
TrackMenu gains a 'Remove Me as Collaborator' item shown to accepted
collaborators (not the owner) — the post-accept leave path.
- Mobile: invite/accept notification components + routing + navigation handlers
(navigate to the track).
Deferred follow-ups: mobile upload picker, mobile overflow leave action,
a pending-invites list page, and SDK read-type regen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Finishes the deferred mobile pieces.
- CollaboratorField: self-contained inline search (useSearchUserResults) +
removable chips, added to the mobile edit-track form behind the flag.
Selected users flow through the upload metadata adapter to numeric ids.
- "Remove Me as Collaborator" overflow action: new
OverflowAction.LEAVE_TRACK_COLLABORATION, row label + drawer callback
(rejectTrackCollaboration + toast), shown on the track page to accepted
collaborators (not the owner).
Known limitation: editing a track initializes collaborators from the API's
accepted list only, so re-saving could drop still-pending invites — needs an
owner-facing "all collaborators" source to fix.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rder
- SDK: the generated Notification union/parser threw on unknown types and the
adapter switch couldn't reference them. Add TrackCollaboratorNotification
(+ Action/ActionData) generated models and wire them into Notification.ts
(union + FromJSONTyped cases) so track_collaborator_invite/accept parse and
typecheck. Hand-authored pending an OpenAPI regen.
- Fix import/order lint in web Notification.tsx and TrackMenu.tsx, and mobile
TrackScreenDetailsTile.tsx.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ivate tracks
- Remove the collaborative_tracks feature flag and its gating everywhere; the
feature is always on now (display, upload tagging, notifications, leave).
- Fix the invite notification doing nothing on a private track: a pending
collaborator can't load an unlisted track (the API filters it), so the
`if (!track) return null` guard blanked the whole notification — including the
Accept/Decline buttons. Render off the inviter alone, fall back to "a track"
for the title, and act on the trackId carried by the notification.
- Add success/error toasts (and disable-while-submitting) to Accept/Decline so
it's no longer silent. Same null-guard relaxed on the accept notifications and
the mobile equivalents.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The API returns `collaborators` on track responses, but the generated SDK
Track/SearchTrack models' FromJSONTyped only copy known fields — so the field
was silently dropped during deserialization, and the adapter's structural cast
always read `undefined`. Result: accepted collaborators never rendered on track
pages/tiles even though the backend returned them.
Add `collaborators?: Array<User>` to the generated Track and SearchTrack models
(interface + FromJSONTyped + ToJSON, mirroring `user`) so the field survives,
and read it directly in the adapter.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Now that the API swagger declares `collaborators` (deployed), ran the real
generator (typescript-fetch v7.5.0) against api.audius.co's spec. The output for
Track.ts/SearchTrack.ts is byte-identical to the prior hand-edit (only a
generator-emitted whitespace differs), confirming the field is now genuinely
generated rather than hand-patched. Other models left untouched (unrelated spec
drift not pulled in); the hand-authored notification models remain as-is.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The edit form seeded its collaborators field from the track's accepted-only
`collaborators`, so saving dropped any still-pending invites (the ETL reconciles
the metadata set). Initialize the field from accepted + pending instead.
- SDK Track/SearchTrack: add `pendingCollaborators` (hand-edit stopgap; the API
swagger now declares `pending_collaborators` in AudiusProject/api#947, so the
next regen reproduces this).
- Track model + adapter: expose `pending_collaborators` (owner-only).
- Web + mobile edit screens: seed the collaborators field with
`[...collaborators, ...pending_collaborators]` so pending invites survive a
save. Display still uses accepted-only `collaborators` (no regression).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…modal
Match the "Add Collaborator" Figma flow and give collaborators its own box like
the other upload settings:
- CollaboratorsField is now a ContextualMenu box (like Visibility / Remix
Settings) rendered in EditTrackForm's settings list, showing tagged
collaborators as a preview and opening a modal.
- New CollaboratorsMenuFields modal: "Add Collaborator to this Track" + invite
copy, collaborator rows (artist + overflow menu to remove + "Invite Pending"),
and a full-width "Add Collaborator" button that reveals an inline artist
search with a results list.
- Removed the old inline field from TrackMetadataFields.
The form field shape (collaborators: UserMetadata[]) is unchanged, so the upload
adapter and mobile field stay compatible.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@raymondjacobson
raymondjacobson merged commit 77e25e4 into mainJun 16, 2026
19 checks passed
@raymondjacobson
raymondjacobson deleted the claude/collab-apps branch June 16, 2026 22:52
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

@raymondjacobson
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag) - #14466

Merged
raymondjacobson merged 19 commits into
mainfrom
claude/collab-apps
Jun 16, 2026
Merged

feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag)#14466
raymondjacobson merged 19 commits into
mainfrom
claude/collab-apps

Conversation

@raymondjacobson

@raymondjacobsonraymondjacobson commented Jun 9, 2026

Copy link
Copy Markdown
Member

Phase 3 — the apps client for collaborative tracks, all behind the new collaborative_tracks feature flag (default off), so it ships inert. Pairs with go-openaudio #345/#346/#349 (ETL) and AudiusProject/api #932 (api). Reviewing as one PR per your request; commits are sliced by concern.

What's implemented (web + mobile)

DisplayTrackMetadata.collaborators + adapter; <TrackArtists> renders owner + comma-separated collaborators with ellipsis overflow on web tiles/track page and mobile TrackCard/TrackDetailsTile. Flag-off = the previous single owner link.

SDK + hooksTrackCollaborator EntityType; TracksApi.acceptTrackCollaboration / rejectTrackCollaboration (reject = decline or leave); collaborators in the upload metadata schema; upload adapter maps tagged users → numeric on-chain ids; tan-query useAccept/RejectTrackCollaboration.

Upload taggingwebCollaboratorsField (reuses SearchUsersModal/ArtistChip) and mobileCollaboratorField (self-contained inline search + chips), both in the edit-track form behind the flag.

Notifications + accept/declinetrack_collaborator_invite / track_collaborator_accept types, adapter, web + mobile components + routing. Web invite notification has inline Accept/Decline.

Leave (remove yourself after accepting) — web TrackMenu and mobile track-page overflow both gain "Remove Me as Collaborator", shown to accepted collaborators (not the owner).

Deferred follow-ups (called out)

  • Standalone pending-invites list page — accept/decline currently drives off the invite notification (covers the primary flow); a list can use GET /users/:id/collaboration_invites.
  • Editing drops pending invites — the edit form initializes from the API's accepted collaborators only, so re-saving a track with still-pending invites would drop them. Needs an owner-facing "all collaborators (incl. pending)" source.

Verification caveat

I could not run the apps typecheck/lint/build locally (the worktree's toolchain/deps don't match this environment). These changes are pattern-copies of the existing manager-invite/approve flow and are CI-pending — please let CI and your review be the gate. (Server-side: notification id hashing is already generic via trashid.HashifyJson, so the new types' data ids encode correctly with no api change.)

🤖 Generated with Claude Code

@changeset-bot

changeset-botBot commented Jun 9, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 0a970d9

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 Jun 9, 2026

Copy link
Copy Markdown
Contributor

🌐 Web preview ready

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

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

@raymondjacobsonraymondjacobson changed the title feat(web): collaborative tracks — comma-separated artist line (behind flag)feat(apps): collaborative tracks — comma-separated artist line, web + mobile (behind flag)Jun 9, 2026
@raymondjacobsonraymondjacobson changed the title feat(apps): collaborative tracks — comma-separated artist line, web + mobile (behind flag)feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag)Jun 9, 2026
raymondjacobsonand others added 18 commits June 16, 2026 15:09
… flag)
Display slice of the apps phase: renders a track's accepted collaborators as a
comma-separated artist line on web track tiles and the track page, behind a new
`collaborative_tracks` flag (default off). Ships inert.
- packages/common: `TrackMetadata.collaborators` (UserMetadata[]), mapped in
userTrackMetadataFromSDK and decoded like the owner. The field isn't in the
generated SDK Track type yet, so it's read via a structural extension pending
the SDK regen. New COLLABORATIVE_TRACKS feature flag.
- packages/web: <TrackArtists> renders the owner plus comma-separated
collaborators on one ellipsizing line. With the flag off (or no
collaborators) it returns a single owner <UserLink> — a safe drop-in. Wired
into desktop TrackTile, mobile-web TrackTile, and GiantTrackTile.
Next apps slices: mobile-native tiles, upload tagging UI, notifications +
accept UX, SDK write methods.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ind flag)
Mobile-native parity for the collaborator artist line, behind the
collaborative_tracks flag.
- <TrackArtists> (owner UserLink + collaborators) and <CollaboratorLinks>
(collaborators-only append), both flag-gated so they're no-ops when off.
- TrackCard: owner UserLink -> TrackArtists (collaborators added to the track
select). Wrapper centers to preserve the card's centered artist line.
- TrackDetailsTile: keeps the owner Text + UserBadges and appends
CollaboratorLinks, so the flag-off rendering is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- SDK: TrackCollaborator EntityType; TracksApi.acceptTrackCollaboration /
rejectTrackCollaboration (EntityManager Approve/Reject; reject = decline or
leave). collaborators added to UploadTrackMetadataSchema (numeric user ids).
- Upload adapter maps form collaborator user objects -> numeric ids on-chain.
- tan-query: useAcceptTrackCollaboration, useRejectTrackCollaboration.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- CollaboratorsField: search + add collaborators (reuses SearchUsersModal /
ArtistChip, like the invite-manager UI) with removable chips. Wired into
TrackMetadataFields behind the collaborative_tracks flag.
- collaborators added to the upload form schema (full user objects).
Mobile upload tagging deferred (needs a native user picker); data plumbing
already supports it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e (behind flag)
- Notification types track_collaborator_invite / track_collaborator_accept
(NotificationType + PushNotificationType + interfaces + union + adapter cases).
- Web: invite notification with inline Accept/Decline (useAccept/RejectTrack-
Collaboration), accept notification (display); routing in Notification.tsx.
TrackMenu gains a 'Remove Me as Collaborator' item shown to accepted
collaborators (not the owner) — the post-accept leave path.
- Mobile: invite/accept notification components + routing + navigation handlers
(navigate to the track).
Deferred follow-ups: mobile upload picker, mobile overflow leave action,
a pending-invites list page, and SDK read-type regen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Finishes the deferred mobile pieces.
- CollaboratorField: self-contained inline search (useSearchUserResults) +
removable chips, added to the mobile edit-track form behind the flag.
Selected users flow through the upload metadata adapter to numeric ids.
- "Remove Me as Collaborator" overflow action: new
OverflowAction.LEAVE_TRACK_COLLABORATION, row label + drawer callback
(rejectTrackCollaboration + toast), shown on the track page to accepted
collaborators (not the owner).
Known limitation: editing a track initializes collaborators from the API's
accepted list only, so re-saving could drop still-pending invites — needs an
owner-facing "all collaborators" source to fix.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rder
- SDK: the generated Notification union/parser threw on unknown types and the
adapter switch couldn't reference them. Add TrackCollaboratorNotification
(+ Action/ActionData) generated models and wire them into Notification.ts
(union + FromJSONTyped cases) so track_collaborator_invite/accept parse and
typecheck. Hand-authored pending an OpenAPI regen.
- Fix import/order lint in web Notification.tsx and TrackMenu.tsx, and mobile
TrackScreenDetailsTile.tsx.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ivate tracks
- Remove the collaborative_tracks feature flag and its gating everywhere; the
feature is always on now (display, upload tagging, notifications, leave).
- Fix the invite notification doing nothing on a private track: a pending
collaborator can't load an unlisted track (the API filters it), so the
`if (!track) return null` guard blanked the whole notification — including the
Accept/Decline buttons. Render off the inviter alone, fall back to "a track"
for the title, and act on the trackId carried by the notification.
- Add success/error toasts (and disable-while-submitting) to Accept/Decline so
it's no longer silent. Same null-guard relaxed on the accept notifications and
the mobile equivalents.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The API returns `collaborators` on track responses, but the generated SDK
Track/SearchTrack models' FromJSONTyped only copy known fields — so the field
was silently dropped during deserialization, and the adapter's structural cast
always read `undefined`. Result: accepted collaborators never rendered on track
pages/tiles even though the backend returned them.
Add `collaborators?: Array<User>` to the generated Track and SearchTrack models
(interface + FromJSONTyped + ToJSON, mirroring `user`) so the field survives,
and read it directly in the adapter.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Now that the API swagger declares `collaborators` (deployed), ran the real
generator (typescript-fetch v7.5.0) against api.audius.co's spec. The output for
Track.ts/SearchTrack.ts is byte-identical to the prior hand-edit (only a
generator-emitted whitespace differs), confirming the field is now genuinely
generated rather than hand-patched. Other models left untouched (unrelated spec
drift not pulled in); the hand-authored notification models remain as-is.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The edit form seeded its collaborators field from the track's accepted-only
`collaborators`, so saving dropped any still-pending invites (the ETL reconciles
the metadata set). Initialize the field from accepted + pending instead.
- SDK Track/SearchTrack: add `pendingCollaborators` (hand-edit stopgap; the API
swagger now declares `pending_collaborators` in AudiusProject/api#947, so the
next regen reproduces this).
- Track model + adapter: expose `pending_collaborators` (owner-only).
- Web + mobile edit screens: seed the collaborators field with
`[...collaborators, ...pending_collaborators]` so pending invites survive a
save. Display still uses accepted-only `collaborators` (no regression).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…modal
Match the "Add Collaborator" Figma flow and give collaborators its own box like
the other upload settings:
- CollaboratorsField is now a ContextualMenu box (like Visibility / Remix
Settings) rendered in EditTrackForm's settings list, showing tagged
collaborators as a preview and opening a modal.
- New CollaboratorsMenuFields modal: "Add Collaborator to this Track" + invite
copy, collaborator rows (artist + overflow menu to remove + "Invite Pending"),
and a full-width "Add Collaborator" button that reveals an inline artist
search with a results list.
- Removed the old inline field from TrackMetadataFields.
The form field shape (collaborators: UserMetadata[]) is unchanged, so the upload
adapter and mobile field stay compatible.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@raymondjacobson
raymondjacobson merged commit 77e25e4 into mainJun 16, 2026
19 checks passed
@raymondjacobson
raymondjacobson deleted the claude/collab-apps branch June 16, 2026 22:52
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

@raymondjacobson
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag) - #14466

Merged
raymondjacobson merged 19 commits into
mainfrom
claude/collab-apps
Jun 16, 2026
Merged

feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag)#14466
raymondjacobson merged 19 commits into
mainfrom
claude/collab-apps

Conversation

@raymondjacobson

@raymondjacobsonraymondjacobson commented Jun 9, 2026

Copy link
Copy Markdown
Member

Phase 3 — the apps client for collaborative tracks, all behind the new collaborative_tracks feature flag (default off), so it ships inert. Pairs with go-openaudio #345/#346/#349 (ETL) and AudiusProject/api #932 (api). Reviewing as one PR per your request; commits are sliced by concern.

What's implemented (web + mobile)

DisplayTrackMetadata.collaborators + adapter; <TrackArtists> renders owner + comma-separated collaborators with ellipsis overflow on web tiles/track page and mobile TrackCard/TrackDetailsTile. Flag-off = the previous single owner link.

SDK + hooksTrackCollaborator EntityType; TracksApi.acceptTrackCollaboration / rejectTrackCollaboration (reject = decline or leave); collaborators in the upload metadata schema; upload adapter maps tagged users → numeric on-chain ids; tan-query useAccept/RejectTrackCollaboration.

Upload taggingwebCollaboratorsField (reuses SearchUsersModal/ArtistChip) and mobileCollaboratorField (self-contained inline search + chips), both in the edit-track form behind the flag.

Notifications + accept/declinetrack_collaborator_invite / track_collaborator_accept types, adapter, web + mobile components + routing. Web invite notification has inline Accept/Decline.

Leave (remove yourself after accepting) — web TrackMenu and mobile track-page overflow both gain "Remove Me as Collaborator", shown to accepted collaborators (not the owner).

Deferred follow-ups (called out)

  • Standalone pending-invites list page — accept/decline currently drives off the invite notification (covers the primary flow); a list can use GET /users/:id/collaboration_invites.
  • Editing drops pending invites — the edit form initializes from the API's accepted collaborators only, so re-saving a track with still-pending invites would drop them. Needs an owner-facing "all collaborators (incl. pending)" source.

Verification caveat

I could not run the apps typecheck/lint/build locally (the worktree's toolchain/deps don't match this environment). These changes are pattern-copies of the existing manager-invite/approve flow and are CI-pending — please let CI and your review be the gate. (Server-side: notification id hashing is already generic via trashid.HashifyJson, so the new types' data ids encode correctly with no api change.)

🤖 Generated with Claude Code

@changeset-bot

changeset-botBot commented Jun 9, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 0a970d9

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 Jun 9, 2026

Copy link
Copy Markdown
Contributor

🌐 Web preview ready

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

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

@raymondjacobsonraymondjacobson changed the title feat(web): collaborative tracks — comma-separated artist line (behind flag)feat(apps): collaborative tracks — comma-separated artist line, web + mobile (behind flag)Jun 9, 2026
@raymondjacobsonraymondjacobson changed the title feat(apps): collaborative tracks — comma-separated artist line, web + mobile (behind flag)feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag)Jun 9, 2026
raymondjacobsonand others added 18 commits June 16, 2026 15:09
… flag)
Display slice of the apps phase: renders a track's accepted collaborators as a
comma-separated artist line on web track tiles and the track page, behind a new
`collaborative_tracks` flag (default off). Ships inert.
- packages/common: `TrackMetadata.collaborators` (UserMetadata[]), mapped in
userTrackMetadataFromSDK and decoded like the owner. The field isn't in the
generated SDK Track type yet, so it's read via a structural extension pending
the SDK regen. New COLLABORATIVE_TRACKS feature flag.
- packages/web: <TrackArtists> renders the owner plus comma-separated
collaborators on one ellipsizing line. With the flag off (or no
collaborators) it returns a single owner <UserLink> — a safe drop-in. Wired
into desktop TrackTile, mobile-web TrackTile, and GiantTrackTile.
Next apps slices: mobile-native tiles, upload tagging UI, notifications +
accept UX, SDK write methods.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ind flag)
Mobile-native parity for the collaborator artist line, behind the
collaborative_tracks flag.
- <TrackArtists> (owner UserLink + collaborators) and <CollaboratorLinks>
(collaborators-only append), both flag-gated so they're no-ops when off.
- TrackCard: owner UserLink -> TrackArtists (collaborators added to the track
select). Wrapper centers to preserve the card's centered artist line.
- TrackDetailsTile: keeps the owner Text + UserBadges and appends
CollaboratorLinks, so the flag-off rendering is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- SDK: TrackCollaborator EntityType; TracksApi.acceptTrackCollaboration /
rejectTrackCollaboration (EntityManager Approve/Reject; reject = decline or
leave). collaborators added to UploadTrackMetadataSchema (numeric user ids).
- Upload adapter maps form collaborator user objects -> numeric ids on-chain.
- tan-query: useAcceptTrackCollaboration, useRejectTrackCollaboration.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- CollaboratorsField: search + add collaborators (reuses SearchUsersModal /
ArtistChip, like the invite-manager UI) with removable chips. Wired into
TrackMetadataFields behind the collaborative_tracks flag.
- collaborators added to the upload form schema (full user objects).
Mobile upload tagging deferred (needs a native user picker); data plumbing
already supports it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e (behind flag)
- Notification types track_collaborator_invite / track_collaborator_accept
(NotificationType + PushNotificationType + interfaces + union + adapter cases).
- Web: invite notification with inline Accept/Decline (useAccept/RejectTrack-
Collaboration), accept notification (display); routing in Notification.tsx.
TrackMenu gains a 'Remove Me as Collaborator' item shown to accepted
collaborators (not the owner) — the post-accept leave path.
- Mobile: invite/accept notification components + routing + navigation handlers
(navigate to the track).
Deferred follow-ups: mobile upload picker, mobile overflow leave action,
a pending-invites list page, and SDK read-type regen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Finishes the deferred mobile pieces.
- CollaboratorField: self-contained inline search (useSearchUserResults) +
removable chips, added to the mobile edit-track form behind the flag.
Selected users flow through the upload metadata adapter to numeric ids.
- "Remove Me as Collaborator" overflow action: new
OverflowAction.LEAVE_TRACK_COLLABORATION, row label + drawer callback
(rejectTrackCollaboration + toast), shown on the track page to accepted
collaborators (not the owner).
Known limitation: editing a track initializes collaborators from the API's
accepted list only, so re-saving could drop still-pending invites — needs an
owner-facing "all collaborators" source to fix.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rder
- SDK: the generated Notification union/parser threw on unknown types and the
adapter switch couldn't reference them. Add TrackCollaboratorNotification
(+ Action/ActionData) generated models and wire them into Notification.ts
(union + FromJSONTyped cases) so track_collaborator_invite/accept parse and
typecheck. Hand-authored pending an OpenAPI regen.
- Fix import/order lint in web Notification.tsx and TrackMenu.tsx, and mobile
TrackScreenDetailsTile.tsx.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ivate tracks
- Remove the collaborative_tracks feature flag and its gating everywhere; the
feature is always on now (display, upload tagging, notifications, leave).
- Fix the invite notification doing nothing on a private track: a pending
collaborator can't load an unlisted track (the API filters it), so the
`if (!track) return null` guard blanked the whole notification — including the
Accept/Decline buttons. Render off the inviter alone, fall back to "a track"
for the title, and act on the trackId carried by the notification.
- Add success/error toasts (and disable-while-submitting) to Accept/Decline so
it's no longer silent. Same null-guard relaxed on the accept notifications and
the mobile equivalents.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The API returns `collaborators` on track responses, but the generated SDK
Track/SearchTrack models' FromJSONTyped only copy known fields — so the field
was silently dropped during deserialization, and the adapter's structural cast
always read `undefined`. Result: accepted collaborators never rendered on track
pages/tiles even though the backend returned them.
Add `collaborators?: Array<User>` to the generated Track and SearchTrack models
(interface + FromJSONTyped + ToJSON, mirroring `user`) so the field survives,
and read it directly in the adapter.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Now that the API swagger declares `collaborators` (deployed), ran the real
generator (typescript-fetch v7.5.0) against api.audius.co's spec. The output for
Track.ts/SearchTrack.ts is byte-identical to the prior hand-edit (only a
generator-emitted whitespace differs), confirming the field is now genuinely
generated rather than hand-patched. Other models left untouched (unrelated spec
drift not pulled in); the hand-authored notification models remain as-is.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The edit form seeded its collaborators field from the track's accepted-only
`collaborators`, so saving dropped any still-pending invites (the ETL reconciles
the metadata set). Initialize the field from accepted + pending instead.
- SDK Track/SearchTrack: add `pendingCollaborators` (hand-edit stopgap; the API
swagger now declares `pending_collaborators` in AudiusProject/api#947, so the
next regen reproduces this).
- Track model + adapter: expose `pending_collaborators` (owner-only).
- Web + mobile edit screens: seed the collaborators field with
`[...collaborators, ...pending_collaborators]` so pending invites survive a
save. Display still uses accepted-only `collaborators` (no regression).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…modal
Match the "Add Collaborator" Figma flow and give collaborators its own box like
the other upload settings:
- CollaboratorsField is now a ContextualMenu box (like Visibility / Remix
Settings) rendered in EditTrackForm's settings list, showing tagged
collaborators as a preview and opening a modal.
- New CollaboratorsMenuFields modal: "Add Collaborator to this Track" + invite
copy, collaborator rows (artist + overflow menu to remove + "Invite Pending"),
and a full-width "Add Collaborator" button that reveals an inline artist
search with a results list.
- Removed the old inline field from TrackMetadataFields.
The form field shape (collaborators: UserMetadata[]) is unchanged, so the upload
adapter and mobile field stay compatible.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@raymondjacobson
raymondjacobson merged commit 77e25e4 into mainJun 16, 2026
19 checks passed
@raymondjacobson
raymondjacobson deleted the claude/collab-apps branch June 16, 2026 22:52
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

@raymondjacobson
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag) - #14466

Merged
raymondjacobson merged 19 commits into
mainfrom
claude/collab-apps
Jun 16, 2026
Merged

feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag)#14466
raymondjacobson merged 19 commits into
mainfrom
claude/collab-apps

Conversation

@raymondjacobson

@raymondjacobsonraymondjacobson commented Jun 9, 2026

Copy link
Copy Markdown
Member

Phase 3 — the apps client for collaborative tracks, all behind the new collaborative_tracks feature flag (default off), so it ships inert. Pairs with go-openaudio #345/#346/#349 (ETL) and AudiusProject/api #932 (api). Reviewing as one PR per your request; commits are sliced by concern.

What's implemented (web + mobile)

DisplayTrackMetadata.collaborators + adapter; <TrackArtists> renders owner + comma-separated collaborators with ellipsis overflow on web tiles/track page and mobile TrackCard/TrackDetailsTile. Flag-off = the previous single owner link.

SDK + hooksTrackCollaborator EntityType; TracksApi.acceptTrackCollaboration / rejectTrackCollaboration (reject = decline or leave); collaborators in the upload metadata schema; upload adapter maps tagged users → numeric on-chain ids; tan-query useAccept/RejectTrackCollaboration.

Upload taggingwebCollaboratorsField (reuses SearchUsersModal/ArtistChip) and mobileCollaboratorField (self-contained inline search + chips), both in the edit-track form behind the flag.

Notifications + accept/declinetrack_collaborator_invite / track_collaborator_accept types, adapter, web + mobile components + routing. Web invite notification has inline Accept/Decline.

Leave (remove yourself after accepting) — web TrackMenu and mobile track-page overflow both gain "Remove Me as Collaborator", shown to accepted collaborators (not the owner).

Deferred follow-ups (called out)

  • Standalone pending-invites list page — accept/decline currently drives off the invite notification (covers the primary flow); a list can use GET /users/:id/collaboration_invites.
  • Editing drops pending invites — the edit form initializes from the API's accepted collaborators only, so re-saving a track with still-pending invites would drop them. Needs an owner-facing "all collaborators (incl. pending)" source.

Verification caveat

I could not run the apps typecheck/lint/build locally (the worktree's toolchain/deps don't match this environment). These changes are pattern-copies of the existing manager-invite/approve flow and are CI-pending — please let CI and your review be the gate. (Server-side: notification id hashing is already generic via trashid.HashifyJson, so the new types' data ids encode correctly with no api change.)

🤖 Generated with Claude Code

@changeset-bot

changeset-botBot commented Jun 9, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 0a970d9

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 Jun 9, 2026

Copy link
Copy Markdown
Contributor

🌐 Web preview ready

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

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

@raymondjacobsonraymondjacobson changed the title feat(web): collaborative tracks — comma-separated artist line (behind flag)feat(apps): collaborative tracks — comma-separated artist line, web + mobile (behind flag)Jun 9, 2026
@raymondjacobsonraymondjacobson changed the title feat(apps): collaborative tracks — comma-separated artist line, web + mobile (behind flag)feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag)Jun 9, 2026
raymondjacobsonand others added 18 commits June 16, 2026 15:09
… flag)
Display slice of the apps phase: renders a track's accepted collaborators as a
comma-separated artist line on web track tiles and the track page, behind a new
`collaborative_tracks` flag (default off). Ships inert.
- packages/common: `TrackMetadata.collaborators` (UserMetadata[]), mapped in
userTrackMetadataFromSDK and decoded like the owner. The field isn't in the
generated SDK Track type yet, so it's read via a structural extension pending
the SDK regen. New COLLABORATIVE_TRACKS feature flag.
- packages/web: <TrackArtists> renders the owner plus comma-separated
collaborators on one ellipsizing line. With the flag off (or no
collaborators) it returns a single owner <UserLink> — a safe drop-in. Wired
into desktop TrackTile, mobile-web TrackTile, and GiantTrackTile.
Next apps slices: mobile-native tiles, upload tagging UI, notifications +
accept UX, SDK write methods.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ind flag)
Mobile-native parity for the collaborator artist line, behind the
collaborative_tracks flag.
- <TrackArtists> (owner UserLink + collaborators) and <CollaboratorLinks>
(collaborators-only append), both flag-gated so they're no-ops when off.
- TrackCard: owner UserLink -> TrackArtists (collaborators added to the track
select). Wrapper centers to preserve the card's centered artist line.
- TrackDetailsTile: keeps the owner Text + UserBadges and appends
CollaboratorLinks, so the flag-off rendering is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- SDK: TrackCollaborator EntityType; TracksApi.acceptTrackCollaboration /
rejectTrackCollaboration (EntityManager Approve/Reject; reject = decline or
leave). collaborators added to UploadTrackMetadataSchema (numeric user ids).
- Upload adapter maps form collaborator user objects -> numeric ids on-chain.
- tan-query: useAcceptTrackCollaboration, useRejectTrackCollaboration.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- CollaboratorsField: search + add collaborators (reuses SearchUsersModal /
ArtistChip, like the invite-manager UI) with removable chips. Wired into
TrackMetadataFields behind the collaborative_tracks flag.
- collaborators added to the upload form schema (full user objects).
Mobile upload tagging deferred (needs a native user picker); data plumbing
already supports it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e (behind flag)
- Notification types track_collaborator_invite / track_collaborator_accept
(NotificationType + PushNotificationType + interfaces + union + adapter cases).
- Web: invite notification with inline Accept/Decline (useAccept/RejectTrack-
Collaboration), accept notification (display); routing in Notification.tsx.
TrackMenu gains a 'Remove Me as Collaborator' item shown to accepted
collaborators (not the owner) — the post-accept leave path.
- Mobile: invite/accept notification components + routing + navigation handlers
(navigate to the track).
Deferred follow-ups: mobile upload picker, mobile overflow leave action,
a pending-invites list page, and SDK read-type regen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Finishes the deferred mobile pieces.
- CollaboratorField: self-contained inline search (useSearchUserResults) +
removable chips, added to the mobile edit-track form behind the flag.
Selected users flow through the upload metadata adapter to numeric ids.
- "Remove Me as Collaborator" overflow action: new
OverflowAction.LEAVE_TRACK_COLLABORATION, row label + drawer callback
(rejectTrackCollaboration + toast), shown on the track page to accepted
collaborators (not the owner).
Known limitation: editing a track initializes collaborators from the API's
accepted list only, so re-saving could drop still-pending invites — needs an
owner-facing "all collaborators" source to fix.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rder
- SDK: the generated Notification union/parser threw on unknown types and the
adapter switch couldn't reference them. Add TrackCollaboratorNotification
(+ Action/ActionData) generated models and wire them into Notification.ts
(union + FromJSONTyped cases) so track_collaborator_invite/accept parse and
typecheck. Hand-authored pending an OpenAPI regen.
- Fix import/order lint in web Notification.tsx and TrackMenu.tsx, and mobile
TrackScreenDetailsTile.tsx.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ivate tracks
- Remove the collaborative_tracks feature flag and its gating everywhere; the
feature is always on now (display, upload tagging, notifications, leave).
- Fix the invite notification doing nothing on a private track: a pending
collaborator can't load an unlisted track (the API filters it), so the
`if (!track) return null` guard blanked the whole notification — including the
Accept/Decline buttons. Render off the inviter alone, fall back to "a track"
for the title, and act on the trackId carried by the notification.
- Add success/error toasts (and disable-while-submitting) to Accept/Decline so
it's no longer silent. Same null-guard relaxed on the accept notifications and
the mobile equivalents.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The API returns `collaborators` on track responses, but the generated SDK
Track/SearchTrack models' FromJSONTyped only copy known fields — so the field
was silently dropped during deserialization, and the adapter's structural cast
always read `undefined`. Result: accepted collaborators never rendered on track
pages/tiles even though the backend returned them.
Add `collaborators?: Array<User>` to the generated Track and SearchTrack models
(interface + FromJSONTyped + ToJSON, mirroring `user`) so the field survives,
and read it directly in the adapter.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Now that the API swagger declares `collaborators` (deployed), ran the real
generator (typescript-fetch v7.5.0) against api.audius.co's spec. The output for
Track.ts/SearchTrack.ts is byte-identical to the prior hand-edit (only a
generator-emitted whitespace differs), confirming the field is now genuinely
generated rather than hand-patched. Other models left untouched (unrelated spec
drift not pulled in); the hand-authored notification models remain as-is.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The edit form seeded its collaborators field from the track's accepted-only
`collaborators`, so saving dropped any still-pending invites (the ETL reconciles
the metadata set). Initialize the field from accepted + pending instead.
- SDK Track/SearchTrack: add `pendingCollaborators` (hand-edit stopgap; the API
swagger now declares `pending_collaborators` in AudiusProject/api#947, so the
next regen reproduces this).
- Track model + adapter: expose `pending_collaborators` (owner-only).
- Web + mobile edit screens: seed the collaborators field with
`[...collaborators, ...pending_collaborators]` so pending invites survive a
save. Display still uses accepted-only `collaborators` (no regression).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…modal
Match the "Add Collaborator" Figma flow and give collaborators its own box like
the other upload settings:
- CollaboratorsField is now a ContextualMenu box (like Visibility / Remix
Settings) rendered in EditTrackForm's settings list, showing tagged
collaborators as a preview and opening a modal.
- New CollaboratorsMenuFields modal: "Add Collaborator to this Track" + invite
copy, collaborator rows (artist + overflow menu to remove + "Invite Pending"),
and a full-width "Add Collaborator" button that reveals an inline artist
search with a results list.
- Removed the old inline field from TrackMetadataFields.
The form field shape (collaborators: UserMetadata[]) is unchanged, so the upload
adapter and mobile field stay compatible.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@raymondjacobson
raymondjacobson merged commit 77e25e4 into mainJun 16, 2026
19 checks passed
@raymondjacobson
raymondjacobson deleted the claude/collab-apps branch June 16, 2026 22:52
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

@raymondjacobson
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag) - #14466

Merged
raymondjacobson merged 19 commits into
mainfrom
claude/collab-apps
Jun 16, 2026
Merged

feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag)#14466
raymondjacobson merged 19 commits into
mainfrom
claude/collab-apps

Conversation

@raymondjacobson

@raymondjacobsonraymondjacobson commented Jun 9, 2026

Copy link
Copy Markdown
Member

Phase 3 — the apps client for collaborative tracks, all behind the new collaborative_tracks feature flag (default off), so it ships inert. Pairs with go-openaudio #345/#346/#349 (ETL) and AudiusProject/api #932 (api). Reviewing as one PR per your request; commits are sliced by concern.

What's implemented (web + mobile)

DisplayTrackMetadata.collaborators + adapter; <TrackArtists> renders owner + comma-separated collaborators with ellipsis overflow on web tiles/track page and mobile TrackCard/TrackDetailsTile. Flag-off = the previous single owner link.

SDK + hooksTrackCollaborator EntityType; TracksApi.acceptTrackCollaboration / rejectTrackCollaboration (reject = decline or leave); collaborators in the upload metadata schema; upload adapter maps tagged users → numeric on-chain ids; tan-query useAccept/RejectTrackCollaboration.

Upload taggingwebCollaboratorsField (reuses SearchUsersModal/ArtistChip) and mobileCollaboratorField (self-contained inline search + chips), both in the edit-track form behind the flag.

Notifications + accept/declinetrack_collaborator_invite / track_collaborator_accept types, adapter, web + mobile components + routing. Web invite notification has inline Accept/Decline.

Leave (remove yourself after accepting) — web TrackMenu and mobile track-page overflow both gain "Remove Me as Collaborator", shown to accepted collaborators (not the owner).

Deferred follow-ups (called out)

  • Standalone pending-invites list page — accept/decline currently drives off the invite notification (covers the primary flow); a list can use GET /users/:id/collaboration_invites.
  • Editing drops pending invites — the edit form initializes from the API's accepted collaborators only, so re-saving a track with still-pending invites would drop them. Needs an owner-facing "all collaborators (incl. pending)" source.

Verification caveat

I could not run the apps typecheck/lint/build locally (the worktree's toolchain/deps don't match this environment). These changes are pattern-copies of the existing manager-invite/approve flow and are CI-pending — please let CI and your review be the gate. (Server-side: notification id hashing is already generic via trashid.HashifyJson, so the new types' data ids encode correctly with no api change.)

🤖 Generated with Claude Code

@changeset-bot

changeset-botBot commented Jun 9, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 0a970d9

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 Jun 9, 2026

Copy link
Copy Markdown
Contributor

🌐 Web preview ready

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

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

@raymondjacobsonraymondjacobson changed the title feat(web): collaborative tracks — comma-separated artist line (behind flag)feat(apps): collaborative tracks — comma-separated artist line, web + mobile (behind flag)Jun 9, 2026
@raymondjacobsonraymondjacobson changed the title feat(apps): collaborative tracks — comma-separated artist line, web + mobile (behind flag)feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag)Jun 9, 2026
raymondjacobsonand others added 18 commits June 16, 2026 15:09
… flag)
Display slice of the apps phase: renders a track's accepted collaborators as a
comma-separated artist line on web track tiles and the track page, behind a new
`collaborative_tracks` flag (default off). Ships inert.
- packages/common: `TrackMetadata.collaborators` (UserMetadata[]), mapped in
userTrackMetadataFromSDK and decoded like the owner. The field isn't in the
generated SDK Track type yet, so it's read via a structural extension pending
the SDK regen. New COLLABORATIVE_TRACKS feature flag.
- packages/web: <TrackArtists> renders the owner plus comma-separated
collaborators on one ellipsizing line. With the flag off (or no
collaborators) it returns a single owner <UserLink> — a safe drop-in. Wired
into desktop TrackTile, mobile-web TrackTile, and GiantTrackTile.
Next apps slices: mobile-native tiles, upload tagging UI, notifications +
accept UX, SDK write methods.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ind flag)
Mobile-native parity for the collaborator artist line, behind the
collaborative_tracks flag.
- <TrackArtists> (owner UserLink + collaborators) and <CollaboratorLinks>
(collaborators-only append), both flag-gated so they're no-ops when off.
- TrackCard: owner UserLink -> TrackArtists (collaborators added to the track
select). Wrapper centers to preserve the card's centered artist line.
- TrackDetailsTile: keeps the owner Text + UserBadges and appends
CollaboratorLinks, so the flag-off rendering is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- SDK: TrackCollaborator EntityType; TracksApi.acceptTrackCollaboration /
rejectTrackCollaboration (EntityManager Approve/Reject; reject = decline or
leave). collaborators added to UploadTrackMetadataSchema (numeric user ids).
- Upload adapter maps form collaborator user objects -> numeric ids on-chain.
- tan-query: useAcceptTrackCollaboration, useRejectTrackCollaboration.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- CollaboratorsField: search + add collaborators (reuses SearchUsersModal /
ArtistChip, like the invite-manager UI) with removable chips. Wired into
TrackMetadataFields behind the collaborative_tracks flag.
- collaborators added to the upload form schema (full user objects).
Mobile upload tagging deferred (needs a native user picker); data plumbing
already supports it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e (behind flag)
- Notification types track_collaborator_invite / track_collaborator_accept
(NotificationType + PushNotificationType + interfaces + union + adapter cases).
- Web: invite notification with inline Accept/Decline (useAccept/RejectTrack-
Collaboration), accept notification (display); routing in Notification.tsx.
TrackMenu gains a 'Remove Me as Collaborator' item shown to accepted
collaborators (not the owner) — the post-accept leave path.
- Mobile: invite/accept notification components + routing + navigation handlers
(navigate to the track).
Deferred follow-ups: mobile upload picker, mobile overflow leave action,
a pending-invites list page, and SDK read-type regen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Finishes the deferred mobile pieces.
- CollaboratorField: self-contained inline search (useSearchUserResults) +
removable chips, added to the mobile edit-track form behind the flag.
Selected users flow through the upload metadata adapter to numeric ids.
- "Remove Me as Collaborator" overflow action: new
OverflowAction.LEAVE_TRACK_COLLABORATION, row label + drawer callback
(rejectTrackCollaboration + toast), shown on the track page to accepted
collaborators (not the owner).
Known limitation: editing a track initializes collaborators from the API's
accepted list only, so re-saving could drop still-pending invites — needs an
owner-facing "all collaborators" source to fix.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rder
- SDK: the generated Notification union/parser threw on unknown types and the
adapter switch couldn't reference them. Add TrackCollaboratorNotification
(+ Action/ActionData) generated models and wire them into Notification.ts
(union + FromJSONTyped cases) so track_collaborator_invite/accept parse and
typecheck. Hand-authored pending an OpenAPI regen.
- Fix import/order lint in web Notification.tsx and TrackMenu.tsx, and mobile
TrackScreenDetailsTile.tsx.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ivate tracks
- Remove the collaborative_tracks feature flag and its gating everywhere; the
feature is always on now (display, upload tagging, notifications, leave).
- Fix the invite notification doing nothing on a private track: a pending
collaborator can't load an unlisted track (the API filters it), so the
`if (!track) return null` guard blanked the whole notification — including the
Accept/Decline buttons. Render off the inviter alone, fall back to "a track"
for the title, and act on the trackId carried by the notification.
- Add success/error toasts (and disable-while-submitting) to Accept/Decline so
it's no longer silent. Same null-guard relaxed on the accept notifications and
the mobile equivalents.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The API returns `collaborators` on track responses, but the generated SDK
Track/SearchTrack models' FromJSONTyped only copy known fields — so the field
was silently dropped during deserialization, and the adapter's structural cast
always read `undefined`. Result: accepted collaborators never rendered on track
pages/tiles even though the backend returned them.
Add `collaborators?: Array<User>` to the generated Track and SearchTrack models
(interface + FromJSONTyped + ToJSON, mirroring `user`) so the field survives,
and read it directly in the adapter.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Now that the API swagger declares `collaborators` (deployed), ran the real
generator (typescript-fetch v7.5.0) against api.audius.co's spec. The output for
Track.ts/SearchTrack.ts is byte-identical to the prior hand-edit (only a
generator-emitted whitespace differs), confirming the field is now genuinely
generated rather than hand-patched. Other models left untouched (unrelated spec
drift not pulled in); the hand-authored notification models remain as-is.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The edit form seeded its collaborators field from the track's accepted-only
`collaborators`, so saving dropped any still-pending invites (the ETL reconciles
the metadata set). Initialize the field from accepted + pending instead.
- SDK Track/SearchTrack: add `pendingCollaborators` (hand-edit stopgap; the API
swagger now declares `pending_collaborators` in AudiusProject/api#947, so the
next regen reproduces this).
- Track model + adapter: expose `pending_collaborators` (owner-only).
- Web + mobile edit screens: seed the collaborators field with
`[...collaborators, ...pending_collaborators]` so pending invites survive a
save. Display still uses accepted-only `collaborators` (no regression).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…modal
Match the "Add Collaborator" Figma flow and give collaborators its own box like
the other upload settings:
- CollaboratorsField is now a ContextualMenu box (like Visibility / Remix
Settings) rendered in EditTrackForm's settings list, showing tagged
collaborators as a preview and opening a modal.
- New CollaboratorsMenuFields modal: "Add Collaborator to this Track" + invite
copy, collaborator rows (artist + overflow menu to remove + "Invite Pending"),
and a full-width "Add Collaborator" button that reveals an inline artist
search with a results list.
- Removed the old inline field from TrackMetadataFields.
The form field shape (collaborators: UserMetadata[]) is unchanged, so the upload
adapter and mobile field stay compatible.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@raymondjacobson
raymondjacobson merged commit 77e25e4 into mainJun 16, 2026
19 checks passed
@raymondjacobson
raymondjacobson deleted the claude/collab-apps branch June 16, 2026 22:52
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

@raymondjacobson
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag) - #14466

Merged
raymondjacobson merged 19 commits into
mainfrom
claude/collab-apps
Jun 16, 2026
Merged

feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag)#14466
raymondjacobson merged 19 commits into
mainfrom
claude/collab-apps

Conversation

@raymondjacobson

@raymondjacobsonraymondjacobson commented Jun 9, 2026

Copy link
Copy Markdown
Member

Phase 3 — the apps client for collaborative tracks, all behind the new collaborative_tracks feature flag (default off), so it ships inert. Pairs with go-openaudio #345/#346/#349 (ETL) and AudiusProject/api #932 (api). Reviewing as one PR per your request; commits are sliced by concern.

What's implemented (web + mobile)

DisplayTrackMetadata.collaborators + adapter; <TrackArtists> renders owner + comma-separated collaborators with ellipsis overflow on web tiles/track page and mobile TrackCard/TrackDetailsTile. Flag-off = the previous single owner link.

SDK + hooksTrackCollaborator EntityType; TracksApi.acceptTrackCollaboration / rejectTrackCollaboration (reject = decline or leave); collaborators in the upload metadata schema; upload adapter maps tagged users → numeric on-chain ids; tan-query useAccept/RejectTrackCollaboration.

Upload taggingwebCollaboratorsField (reuses SearchUsersModal/ArtistChip) and mobileCollaboratorField (self-contained inline search + chips), both in the edit-track form behind the flag.

Notifications + accept/declinetrack_collaborator_invite / track_collaborator_accept types, adapter, web + mobile components + routing. Web invite notification has inline Accept/Decline.

Leave (remove yourself after accepting) — web TrackMenu and mobile track-page overflow both gain "Remove Me as Collaborator", shown to accepted collaborators (not the owner).

Deferred follow-ups (called out)

  • Standalone pending-invites list page — accept/decline currently drives off the invite notification (covers the primary flow); a list can use GET /users/:id/collaboration_invites.
  • Editing drops pending invites — the edit form initializes from the API's accepted collaborators only, so re-saving a track with still-pending invites would drop them. Needs an owner-facing "all collaborators (incl. pending)" source.

Verification caveat

I could not run the apps typecheck/lint/build locally (the worktree's toolchain/deps don't match this environment). These changes are pattern-copies of the existing manager-invite/approve flow and are CI-pending — please let CI and your review be the gate. (Server-side: notification id hashing is already generic via trashid.HashifyJson, so the new types' data ids encode correctly with no api change.)

🤖 Generated with Claude Code

@changeset-bot

changeset-botBot commented Jun 9, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 0a970d9

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 Jun 9, 2026

Copy link
Copy Markdown
Contributor

🌐 Web preview ready

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

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

@raymondjacobsonraymondjacobson changed the title feat(web): collaborative tracks — comma-separated artist line (behind flag)feat(apps): collaborative tracks — comma-separated artist line, web + mobile (behind flag)Jun 9, 2026
@raymondjacobsonraymondjacobson changed the title feat(apps): collaborative tracks — comma-separated artist line, web + mobile (behind flag)feat(apps): collaborative tracks — display, upload tagging, notifications, accept/leave (behind flag)Jun 9, 2026
raymondjacobsonand others added 18 commits June 16, 2026 15:09
… flag)
Display slice of the apps phase: renders a track's accepted collaborators as a
comma-separated artist line on web track tiles and the track page, behind a new
`collaborative_tracks` flag (default off). Ships inert.
- packages/common: `TrackMetadata.collaborators` (UserMetadata[]), mapped in
userTrackMetadataFromSDK and decoded like the owner. The field isn't in the
generated SDK Track type yet, so it's read via a structural extension pending
the SDK regen. New COLLABORATIVE_TRACKS feature flag.
- packages/web: <TrackArtists> renders the owner plus comma-separated
collaborators on one ellipsizing line. With the flag off (or no
collaborators) it returns a single owner <UserLink> — a safe drop-in. Wired
into desktop TrackTile, mobile-web TrackTile, and GiantTrackTile.
Next apps slices: mobile-native tiles, upload tagging UI, notifications +
accept UX, SDK write methods.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ind flag)
Mobile-native parity for the collaborator artist line, behind the
collaborative_tracks flag.
- <TrackArtists> (owner UserLink + collaborators) and <CollaboratorLinks>
(collaborators-only append), both flag-gated so they're no-ops when off.
- TrackCard: owner UserLink -> TrackArtists (collaborators added to the track
select). Wrapper centers to preserve the card's centered artist line.
- TrackDetailsTile: keeps the owner Text + UserBadges and appends
CollaboratorLinks, so the flag-off rendering is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- SDK: TrackCollaborator EntityType; TracksApi.acceptTrackCollaboration /
rejectTrackCollaboration (EntityManager Approve/Reject; reject = decline or
leave). collaborators added to UploadTrackMetadataSchema (numeric user ids).
- Upload adapter maps form collaborator user objects -> numeric ids on-chain.
- tan-query: useAcceptTrackCollaboration, useRejectTrackCollaboration.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- CollaboratorsField: search + add collaborators (reuses SearchUsersModal /
ArtistChip, like the invite-manager UI) with removable chips. Wired into
TrackMetadataFields behind the collaborative_tracks flag.
- collaborators added to the upload form schema (full user objects).
Mobile upload tagging deferred (needs a native user picker); data plumbing
already supports it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e (behind flag)
- Notification types track_collaborator_invite / track_collaborator_accept
(NotificationType + PushNotificationType + interfaces + union + adapter cases).
- Web: invite notification with inline Accept/Decline (useAccept/RejectTrack-
Collaboration), accept notification (display); routing in Notification.tsx.
TrackMenu gains a 'Remove Me as Collaborator' item shown to accepted
collaborators (not the owner) — the post-accept leave path.
- Mobile: invite/accept notification components + routing + navigation handlers
(navigate to the track).
Deferred follow-ups: mobile upload picker, mobile overflow leave action,
a pending-invites list page, and SDK read-type regen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Finishes the deferred mobile pieces.
- CollaboratorField: self-contained inline search (useSearchUserResults) +
removable chips, added to the mobile edit-track form behind the flag.
Selected users flow through the upload metadata adapter to numeric ids.
- "Remove Me as Collaborator" overflow action: new
OverflowAction.LEAVE_TRACK_COLLABORATION, row label + drawer callback
(rejectTrackCollaboration + toast), shown on the track page to accepted
collaborators (not the owner).
Known limitation: editing a track initializes collaborators from the API's
accepted list only, so re-saving could drop still-pending invites — needs an
owner-facing "all collaborators" source to fix.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rder
- SDK: the generated Notification union/parser threw on unknown types and the
adapter switch couldn't reference them. Add TrackCollaboratorNotification
(+ Action/ActionData) generated models and wire them into Notification.ts
(union + FromJSONTyped cases) so track_collaborator_invite/accept parse and
typecheck. Hand-authored pending an OpenAPI regen.
- Fix import/order lint in web Notification.tsx and TrackMenu.tsx, and mobile
TrackScreenDetailsTile.tsx.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ivate tracks
- Remove the collaborative_tracks feature flag and its gating everywhere; the
feature is always on now (display, upload tagging, notifications, leave).
- Fix the invite notification doing nothing on a private track: a pending
collaborator can't load an unlisted track (the API filters it), so the
`if (!track) return null` guard blanked the whole notification — including the
Accept/Decline buttons. Render off the inviter alone, fall back to "a track"
for the title, and act on the trackId carried by the notification.
- Add success/error toasts (and disable-while-submitting) to Accept/Decline so
it's no longer silent. Same null-guard relaxed on the accept notifications and
the mobile equivalents.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The API returns `collaborators` on track responses, but the generated SDK
Track/SearchTrack models' FromJSONTyped only copy known fields — so the field
was silently dropped during deserialization, and the adapter's structural cast
always read `undefined`. Result: accepted collaborators never rendered on track
pages/tiles even though the backend returned them.
Add `collaborators?: Array<User>` to the generated Track and SearchTrack models
(interface + FromJSONTyped + ToJSON, mirroring `user`) so the field survives,
and read it directly in the adapter.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Now that the API swagger declares `collaborators` (deployed), ran the real
generator (typescript-fetch v7.5.0) against api.audius.co's spec. The output for
Track.ts/SearchTrack.ts is byte-identical to the prior hand-edit (only a
generator-emitted whitespace differs), confirming the field is now genuinely
generated rather than hand-patched. Other models left untouched (unrelated spec
drift not pulled in); the hand-authored notification models remain as-is.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The edit form seeded its collaborators field from the track's accepted-only
`collaborators`, so saving dropped any still-pending invites (the ETL reconciles
the metadata set). Initialize the field from accepted + pending instead.
- SDK Track/SearchTrack: add `pendingCollaborators` (hand-edit stopgap; the API
swagger now declares `pending_collaborators` in AudiusProject/api#947, so the
next regen reproduces this).
- Track model + adapter: expose `pending_collaborators` (owner-only).
- Web + mobile edit screens: seed the collaborators field with
`[...collaborators, ...pending_collaborators]` so pending invites survive a
save. Display still uses accepted-only `collaborators` (no regression).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…modal
Match the "Add Collaborator" Figma flow and give collaborators its own box like
the other upload settings:
- CollaboratorsField is now a ContextualMenu box (like Visibility / Remix
Settings) rendered in EditTrackForm's settings list, showing tagged
collaborators as a preview and opening a modal.
- New CollaboratorsMenuFields modal: "Add Collaborator to this Track" + invite
copy, collaborator rows (artist + overflow menu to remove + "Invite Pending"),
and a full-width "Add Collaborator" button that reveals an inline artist
search with a results list.
- Removed the old inline field from TrackMetadataFields.
The form field shape (collaborators: UserMetadata[]) is unchanged, so the upload
adapter and mobile field stay compatible.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@raymondjacobson
raymondjacobson merged commit 77e25e4 into mainJun 16, 2026
19 checks passed
@raymondjacobson
raymondjacobson deleted the claude/collab-apps branch June 16, 2026 22:52
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

@raymondjacobson