Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/common/src/api/tan-query/events/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export * from './useEventsByEntityId'
export * from './useFollowEvent'
export * from './useRemixContest'
export * from './useRemixContestWinners'
export * from './useUserContests'

// Mutations
export * from './useCreateEvent'
Expand Down
138 changes: 138 additions & 0 deletions packages/common/src/api/tan-query/events/useUserContests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import {
EventEntityTypeEnum,
EventEventTypeEnum,
GetContestsByUserStatusEnum,
Id,
OptionalHashId,
Event as SDKEvent
} from '@audius/sdk'
import { useInfiniteQuery, useQueryClient } from '@tanstack/react-query'

import { eventMetadataFromSDK } from '~/adapters/event'
import { getRemixesQueryKey } from '~/api/tan-query/remixes/useRemixes'
import { useQueryContext } from '~/api/tan-query/utils'
import { primeRelatedData } from '~/api/tan-query/utils/primeRelatedData'
import { ID } from '~/models'
import { removeNullable } from '~/utils'

import { QUERY_KEYS } from '../queryKeys'
import { QueryKey, QueryOptions } from '../types'

import { getEventIdsByEntityIdQueryKey, getEventQueryKey } from './utils'

const DEFAULT_PAGE_SIZE = 25

export type UserContestStatus = GetContestsByUserStatusEnum

type UseUserContestsArgs = {
userId: ID | null | undefined
pageSize?: number
/**
* Filter by contest status. Defaults to `'all'` (the backend's default),
* which returns active contests first (ordered by soonest-ending end_date)
* followed by ended contests (most-recently-ended first).
*/
status?: UserContestStatus
}

export const getUserContestsQueryKey = ({
userId,
pageSize = DEFAULT_PAGE_SIZE,
status = GetContestsByUserStatusEnum.All
}: UseUserContestsArgs) =>
[
QUERY_KEYS.userContests,
{ userId, pageSize, status }
] as unknown as QueryKey<ID[]>

/**
* Fetch the remix contests hosted by a specific user. Calls the dedicated
* discovery endpoint `GET /v1/users/{id}/contests` (SDK:
* `users.getContestsByUser`), which returns events ordered with
* currently-active contests first (by soonest-ending end_date) followed by
* ended contests.
*
* Each page is mapped to the contest's parent track ID (`event.entityId`) so
* consumers like `ContestCard` can take a `trackId` prop and resolve the event
* via `useRemixContest`.
*/
export const useUserContests = (
{
userId,
pageSize = DEFAULT_PAGE_SIZE,
status = GetContestsByUserStatusEnum.All
}: UseUserContestsArgs,
options?: QueryOptions
) => {
const { audiusSdk } = useQueryContext()
const queryClient = useQueryClient()

return useInfiniteQuery({
queryKey: getUserContestsQueryKey({ userId, pageSize, status }),
initialPageParam: 0,
getNextPageParam: (lastPage: ID[], allPages) => {
if (lastPage.length < pageSize) return undefined
return allPages.length * pageSize
},
queryFn: async ({ pageParam }) => {
const sdk = await audiusSdk()
const { data, related } = await sdk.users.getContestsByUser({
id: Id.parse(userId),
limit: pageSize,
offset: pageParam,
status
})

// Prime related tracks + users so ContestCard's useTrack / useUser are
// cache hits — same pattern as useAllRemixContests.
primeRelatedData({ related, queryClient })

// Prime useRemixes({ trackId, pageSize: 0, isContestEntry: true }) so
// ContestCard's entry-count badge doesn't fire a count-only request per
// card.
const entryCounts = related?.entryCounts ?? {}
for (const [hashedTrackId, count] of Object.entries(entryCounts)) {
const trackId = OptionalHashId.parse(hashedTrackId)
if (!trackId) continue
queryClient.setQueryData(
getRemixesQueryKey({
trackId,
pageSize: 0,
isContestEntry: true
}),
{
pages: [{ count, tracks: [] }],
pageParams: [0]
} as unknown as never
)
}

if (!data) return []

return data
.map((sdkEvent: SDKEvent) => {
const event = eventMetadataFromSDK(sdkEvent)
if (!event) return null
queryClient.setQueryData(getEventQueryKey(event.eventId), event)
if (
event.entityId &&
event.entityType === EventEntityTypeEnum.Track
) {
queryClient.setQueryData(
getEventIdsByEntityIdQueryKey({
entityId: event.entityId,
entityType: EventEntityTypeEnum.Track,
eventType: EventEventTypeEnum.RemixContest
}),
[event.eventId]
)
}
return event.entityId ?? null
})
.filter(removeNullable)
},
select: (data) => data.pages.flat(),
...options,
enabled: !!userId && options?.enabled !== false
})
}
1 change: 1 addition & 0 deletions packages/common/src/api/tan-query/queryKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export const QUERY_KEYS = {
events: 'events',
eventsByEntityId: 'eventsByEntityId',
remixContestsList: 'remixContestsList',
userContests: 'userContests',
walletOwner: 'walletOwner',
tokenPrice: 'tokenPrice',
usdcBalance: 'usdcBalance',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,15 @@ const SearchCategory = (props: SearchCategoryProps) => {
}

const filtersByCategory: Record<SearchCategoryType, SearchFilter[]> = {
all: [],
all: [
'genre',
'mood',
'key',
'bpm',
'isPremium',
'hasDownloads',
'isVerified'
],
users: ['genre', 'isVerified'],
tracks: [
'genre',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ apis/DeveloperAppsApi.ts
apis/EventsApi.ts
apis/ExploreApi.ts
apis/FanClubApi.ts
apis/FeedApi.ts
apis/NotificationsApi.ts
apis/PlaylistsApi.ts
apis/PrizesApi.ts
Expand Down
93 changes: 93 additions & 0 deletions packages/sdk/src/sdk/api/generated/default/apis/FeedApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/* tslint:disable */
// @ts-nocheck
/* eslint-disable */
/**
* Audius API
*
* The version of the OpenAPI document: 1.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/


import * as runtime from '../runtime';
import type {
Tracks,
} from '../models';
import {
TracksFromJSON,
TracksToJSON,
} from '../models';

export interface GetForYouFeedRequest {
userId: string;
limit?: number;
offset?: number;
maxPerArtist?: number;
}

/**
*
*/
export class FeedApi extends runtime.BaseAPI {

/**
* @hidden
* Returns a personalized For You feed using Twitter-style multi-source ranking (in-network, trending, underground, similar-artist candidates scored by recency decay × engagement × social affinity).
* Get For You feed
*/
async getForYouFeedRaw(params: GetForYouFeedRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<Tracks>> {
if (params.userId === null || params.userId === undefined) {
throw new runtime.RequiredError('userId','Required parameter params.userId was null or undefined when calling getForYouFeed.');
}

const queryParameters: any = {};

if (params.userId !== undefined) {
queryParameters['user_id'] = params.userId;
}

if (params.limit !== undefined) {
queryParameters['limit'] = params.limit;
}

if (params.offset !== undefined) {
queryParameters['offset'] = params.offset;
}

if (params.maxPerArtist !== undefined) {
queryParameters['max_per_artist'] = params.maxPerArtist;
}

const headerParameters: runtime.HTTPHeaders = {};

if (!headerParameters["Authorization"] && this.configuration && this.configuration.accessToken) {
const token = await this.configuration.accessToken("OAuth2", ["read"]);
if (token) {
headerParameters["Authorization"] = token;
}
}

const response = await this.request({
path: `/feed/for-you`,
method: 'GET',
headers: headerParameters,
query: queryParameters,
}, initOverrides);

return new runtime.JSONApiResponse(response, (jsonValue) => TracksFromJSON(jsonValue));
}

/**
* Returns a personalized For You feed using Twitter-style multi-source ranking (in-network, trending, underground, similar-artist candidates scored by recency decay × engagement × social affinity).
* Get For You feed
*/
async getForYouFeed(params: GetForYouFeedRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<Tracks> {
const response = await this.getForYouFeedRaw(params, initOverrides);
return await response.value();
}

}
71 changes: 71 additions & 0 deletions packages/sdk/src/sdk/api/generated/default/apis/UsersApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import type {
PurchasesCountResponse,
PurchasesResponse,
RelatedArtistResponse,
RemixContestsResponse,
RemixersCountResponse,
RemixersResponse,
Reposts,
Expand Down Expand Up @@ -139,6 +140,8 @@ import {
PurchasesResponseToJSON,
RelatedArtistResponseFromJSON,
RelatedArtistResponseToJSON,
RemixContestsResponseFromJSON,
RemixContestsResponseToJSON,
RemixersCountResponseFromJSON,
RemixersCountResponseToJSON,
RemixersResponseFromJSON,
Expand Down Expand Up @@ -314,6 +317,13 @@ export interface GetConnectedWalletsRequest {
id: string;
}

export interface GetContestsByUserRequest {
id: string;
offset?: number;
limit?: number;
status?: GetContestsByUserStatusEnum;
}

export interface GetFollowersRequest {
id: string;
offset?: number;
Expand Down Expand Up @@ -1705,6 +1715,58 @@ export class UsersApi extends runtime.BaseAPI {
return await response.value();
}

/**
* @hidden
* Gets the remix contests hosted by a user, ordered with currently-active contests first (by soonest-ending end_date), followed by ended contests (most-recently-ended first). Active contests are those whose end_date is null or in the future.
* Get contests hosted by user
*/
async getContestsByUserRaw(params: GetContestsByUserRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<RemixContestsResponse>> {
if (params.id === null || params.id === undefined) {
throw new runtime.RequiredError('id','Required parameter params.id was null or undefined when calling getContestsByUser.');
}

const queryParameters: any = {};

if (params.offset !== undefined) {
queryParameters['offset'] = params.offset;
}

if (params.limit !== undefined) {
queryParameters['limit'] = params.limit;
}

if (params.status !== undefined) {
queryParameters['status'] = params.status;
}

const headerParameters: runtime.HTTPHeaders = {};

if (!headerParameters["Authorization"] && this.configuration && this.configuration.accessToken) {
const token = await this.configuration.accessToken("OAuth2", ["read"]);
if (token) {
headerParameters["Authorization"] = token;
}
}

const response = await this.request({
path: `/users/{id}/contests`.replace(`{${"id"}}`, encodeURIComponent(String(params.id))),
method: 'GET',
headers: headerParameters,
query: queryParameters,
}, initOverrides);

return new runtime.JSONApiResponse(response, (jsonValue) => RemixContestsResponseFromJSON(jsonValue));
}

/**
* Gets the remix contests hosted by a user, ordered with currently-active contests first (by soonest-ending end_date), followed by ended contests (most-recently-ended first). Active contests are those whose end_date is null or in the future.
* Get contests hosted by user
*/
async getContestsByUser(params: GetContestsByUserRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<RemixContestsResponse> {
const response = await this.getContestsByUserRaw(params, initOverrides);
return await response.value();
}

/**
* @hidden
* All users that follow the provided user
Expand Down Expand Up @@ -5233,6 +5295,15 @@ export const GetAudioTransactionsSortDirectionEnum = {
Desc: 'desc'
} as const;
export type GetAudioTransactionsSortDirectionEnum = typeof GetAudioTransactionsSortDirectionEnum[keyof typeof GetAudioTransactionsSortDirectionEnum];
/**
* @export
*/
export const GetContestsByUserStatusEnum = {
Active: 'active',
Ended: 'ended',
All: 'all'
} as const;
export type GetContestsByUserStatusEnum = typeof GetContestsByUserStatusEnum[keyof typeof GetContestsByUserStatusEnum];
/**
* @export
*/
Expand Down
1 change: 1 addition & 0 deletions packages/sdk/src/sdk/api/generated/default/apis/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export * from './DeveloperAppsApi';
export * from './EventsApi';
export * from './ExploreApi';
export * from './FanClubApi';
export * from './FeedApi';
export * from './NotificationsApi';
export * from './PlaylistsApi';
export * from './PrizesApi';
Expand Down
Loading