diff --git a/.changeset/personalization-three-endpoints.md b/.changeset/personalization-three-endpoints.md new file mode 100644 index 00000000000..c9541b8079c --- /dev/null +++ b/.changeset/personalization-three-endpoints.md @@ -0,0 +1,13 @@ +--- +'@audius/sdk': minor +--- + +Add an optional `userId?: string` query parameter to three endpoints so the backend can personalize embedded users in `related.users` / collection owners (`does_current_user_follow`, etc.): + +- `events.getRemixContests` +- `users.getContestsByUser` +- `playlists.getPlaylistsNewReleases` + +Without this, the endpoints' handlers (`app.getMyId(c)`) resolved the requester id from the missing `?user_id=` query and got `0`, so embedded user objects came back with `does_current_user_follow: false` for everyone. Clients that primed those into a shared cache (e.g. via `primeRelatedData` / `primeCollectionData`) silently poisoned follow state for the surfaced artists. + +Existing callers continue to work unchanged — the field is optional. Pass `Id.parse(currentUserId)` (or whichever id your app treats as "me") to opt in. diff --git a/packages/common/src/api/tan-query/collection/useNewAlbumReleases.ts b/packages/common/src/api/tan-query/collection/useNewAlbumReleases.ts index 8b46674bb26..f4897486816 100644 --- a/packages/common/src/api/tan-query/collection/useNewAlbumReleases.ts +++ b/packages/common/src/api/tan-query/collection/useNewAlbumReleases.ts @@ -1,3 +1,4 @@ +import { Id } from '@audius/sdk' import { useQuery, useQueryClient } from '@tanstack/react-query' import { userCollectionMetadataFromSDK } from '~/adapters/collection' @@ -7,6 +8,7 @@ import { ID } from '~/models' import { QUERY_KEYS } from '../queryKeys' import { QueryKey, QueryOptions } from '../types' +import { useCurrentUserId } from '../users/account/useCurrentUserId' import { entityCacheOptions } from '../utils/entityCacheOptions' import { primeCollectionData } from '../utils/primeCollectionData' @@ -28,6 +30,7 @@ export const useNewAlbumReleases = ( const { limit = 10 } = args const { audiusSdk } = useQueryContext() const queryClient = useQueryClient() + const { data: currentUserId } = useCurrentUserId() const idQuery = useQuery({ queryKey: getNewAlbumReleasesQueryKey({ limit }), @@ -35,7 +38,11 @@ export const useNewAlbumReleases = ( const sdk = await audiusSdk() const { data = [] } = await sdk.playlists.getPlaylistsNewReleases({ limit, - type: 'album' + type: 'album', + // Requester id so the backend personalizes embedded album-owner users + // (e.g. does_current_user_follow). primeCollectionData fans these out + // into the shared user cache, so without this they'd poison it. + userId: currentUserId ? Id.parse(currentUserId) : undefined }) const collections = transformAndCleanList( data, diff --git a/packages/common/src/api/tan-query/events/useAllRemixContests.ts b/packages/common/src/api/tan-query/events/useAllRemixContests.ts index f7973d18985..22c2e41e101 100644 --- a/packages/common/src/api/tan-query/events/useAllRemixContests.ts +++ b/packages/common/src/api/tan-query/events/useAllRemixContests.ts @@ -2,6 +2,7 @@ import { EventEntityTypeEnum, EventEventTypeEnum, GetRemixContestsStatusEnum, + Id, OptionalHashId, Event as SDKEvent } from '@audius/sdk' @@ -16,6 +17,7 @@ import { removeNullable } from '~/utils' import { QUERY_KEYS } from '../queryKeys' import { QueryKey, QueryOptions } from '../types' +import { useCurrentUserId } from '../users/account/useCurrentUserId' import { getEventIdsByEntityIdQueryKey, getEventQueryKey } from './utils' @@ -61,6 +63,7 @@ export const useAllRemixContests = ( ) => { const { audiusSdk } = useQueryContext() const queryClient = useQueryClient() + const { data: currentUserId } = useCurrentUserId() return useInfiniteQuery({ queryKey: getAllRemixContestsQueryKey({ pageSize, status }), @@ -74,7 +77,11 @@ export const useAllRemixContests = ( const { data, related } = await sdk.events.getRemixContests({ limit: pageSize, offset: pageParam, - status + status, + // Requester id so the backend personalizes embedded related.users + // (e.g. does_current_user_follow). Without it the cache primes those + // users un-personalized and other surfaces read the bad state. + userId: currentUserId ? Id.parse(currentUserId) : undefined }) // Prime related tracks + users (full objects, delivered alongside the diff --git a/packages/common/src/api/tan-query/events/useUserRemixContests.ts b/packages/common/src/api/tan-query/events/useUserRemixContests.ts index b8c08df1093..8e210b87492 100644 --- a/packages/common/src/api/tan-query/events/useUserRemixContests.ts +++ b/packages/common/src/api/tan-query/events/useUserRemixContests.ts @@ -17,6 +17,7 @@ import { removeNullable } from '~/utils' import { QUERY_KEYS } from '../queryKeys' import { QueryKey, QueryOptions } from '../types' +import { useCurrentUserId } from '../users/account/useCurrentUserId' import { getEventIdsByEntityIdQueryKey, getEventQueryKey } from './utils' @@ -67,6 +68,7 @@ export const useUserRemixContests = ( ) => { const { audiusSdk } = useQueryContext() const queryClient = useQueryClient() + const { data: currentUserId } = useCurrentUserId() return useInfiniteQuery({ queryKey: getUserRemixContestsQueryKey({ userId, pageSize, status }), @@ -82,7 +84,11 @@ export const useUserRemixContests = ( id: Id.parse(userId), limit: pageSize, offset: pageParam, - status + status, + // Requester id so the backend personalizes embedded related.users + // (e.g. does_current_user_follow). Path `id` is the contest host; + // query `userId` is the current user viewing the page. + userId: currentUserId ? Id.parse(currentUserId) : undefined }) // Prime related tracks + users (full objects, delivered alongside the diff --git a/packages/sdk/src/sdk/api/generated/default/apis/EventsApi.ts b/packages/sdk/src/sdk/api/generated/default/apis/EventsApi.ts index 00c816adbec..2f1a7492ebe 100644 --- a/packages/sdk/src/sdk/api/generated/default/apis/EventsApi.ts +++ b/packages/sdk/src/sdk/api/generated/default/apis/EventsApi.ts @@ -97,6 +97,7 @@ export interface GetRemixContestsRequest { offset?: number; limit?: number; status?: GetRemixContestsStatusEnum; + userId?: string; } export interface UnfollowEventRequest { @@ -550,6 +551,10 @@ export class EventsApi extends runtime.BaseAPI { queryParameters['status'] = params.status; } + if (params.userId !== undefined) { + queryParameters['user_id'] = params.userId; + } + const headerParameters: runtime.HTTPHeaders = {}; if (!headerParameters["Authorization"] && this.configuration && this.configuration.accessToken) { diff --git a/packages/sdk/src/sdk/api/generated/default/apis/PlaylistsApi.ts b/packages/sdk/src/sdk/api/generated/default/apis/PlaylistsApi.ts index 1fe76d6f04d..4991dfb19b4 100644 --- a/packages/sdk/src/sdk/api/generated/default/apis/PlaylistsApi.ts +++ b/packages/sdk/src/sdk/api/generated/default/apis/PlaylistsApi.ts @@ -96,6 +96,7 @@ export interface GetPlaylistsNewReleasesRequest { offset?: number; limit?: number; type?: GetPlaylistsNewReleasesTypeEnum; + userId?: string; } export interface GetTrendingPlaylistsRequest { @@ -533,6 +534,10 @@ export class PlaylistsApi extends runtime.BaseAPI { queryParameters['type'] = params.type; } + if (params.userId !== undefined) { + queryParameters['user_id'] = params.userId; + } + const headerParameters: runtime.HTTPHeaders = {}; if (!headerParameters["Authorization"] && this.configuration && this.configuration.accessToken) { diff --git a/packages/sdk/src/sdk/api/generated/default/apis/UsersApi.ts b/packages/sdk/src/sdk/api/generated/default/apis/UsersApi.ts index c2b8fb2e693..db501182c5d 100644 --- a/packages/sdk/src/sdk/api/generated/default/apis/UsersApi.ts +++ b/packages/sdk/src/sdk/api/generated/default/apis/UsersApi.ts @@ -322,6 +322,7 @@ export interface GetContestsByUserRequest { offset?: number; limit?: number; status?: GetContestsByUserStatusEnum; + userId?: string; } export interface GetFollowersRequest { @@ -1739,6 +1740,10 @@ export class UsersApi extends runtime.BaseAPI { queryParameters['status'] = params.status; } + if (params.userId !== undefined) { + queryParameters['user_id'] = params.userId; + } + const headerParameters: runtime.HTTPHeaders = {}; if (!headerParameters["Authorization"] && this.configuration && this.configuration.accessToken) {