Skip to content

Commit 06ed20a

Browse files
Add trending winners section (#13725)
<img width="1512" height="864" alt="image" src="https://github.com/user-attachments/assets/d491139f-1c3b-4f37-9222-35d57ece7e58" />
1 parent 0129871 commit 06ed20a

20 files changed

Lines changed: 553 additions & 10 deletions

File tree

packages/common/src/api/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export * from './tan-query/lineups/useProfileReposts'
3838
export * from './tan-query/lineups/useProfileTracks'
3939
export * from './tan-query/lineups/useTrending'
4040
export * from './tan-query/lineups/useTrendingUnderground'
41+
export * from './tan-query/lineups/useTrendingWinners'
4142
export * from './tan-query/lineups/useTrackPageLineup'
4243
export * from './tan-query/lineups/useLineupQuery'
4344
export * from './tan-query/lineups/useExclusiveTracks'
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import type { full, Track } from '@audius/sdk'
2+
import { OptionalId } from '@audius/sdk'
3+
import { useQuery, useQueryClient } from '@tanstack/react-query'
4+
5+
import { userTrackMetadataFromSDK } from '~/adapters/track'
6+
import { transformAndCleanList } from '~/adapters/utils'
7+
import { useQueryContext } from '~/api/tan-query/utils'
8+
9+
import { QUERY_KEYS } from '../queryKeys'
10+
import { QueryOptions } from '../types'
11+
import { useCurrentUserId } from '../users/account/useCurrentUserId'
12+
import { primeTrackData } from '../utils/primeTrackData'
13+
14+
type WinnersType = 'tracks' | 'underground'
15+
16+
export type UseTrendingWinnersArgs = {
17+
week?: string | null
18+
type: WinnersType
19+
}
20+
21+
export const getTrendingWinnersQueryKey = ({
22+
week,
23+
type
24+
}: UseTrendingWinnersArgs) => [
25+
QUERY_KEYS.trendingWinners,
26+
week ?? 'latest',
27+
type
28+
]
29+
30+
/**
31+
* Maps default API Track (from sdk.tracks) to full.TrackFull-like structure
32+
* for userTrackMetadataFromSDK. The default Track has user but not userId.
33+
* TODO: Remove when full is gone.
34+
*/
35+
const toTrackFullLike = (t: Track): full.TrackFull | null => {
36+
// @ts-expect-error - TODO: Remove when full is gone.
37+
const user = t.user as full.UserFull | undefined
38+
if (!user?.id) return null
39+
const trackWithUserId = {
40+
...t,
41+
userId: user.id,
42+
coverArtCids:
43+
(t as { coverArtCids?: unknown }).coverArtCids ?? t.coverArtSizes,
44+
trackSegments: Array.isArray(t.trackSegments) ? t.trackSegments : []
45+
}
46+
return trackWithUserId as unknown as full.TrackFull
47+
}
48+
49+
export const useTrendingWinners = (
50+
{ week, type }: UseTrendingWinnersArgs,
51+
options?: QueryOptions
52+
) => {
53+
const { audiusSdk } = useQueryContext()
54+
const { data: currentUserId } = useCurrentUserId()
55+
const queryClient = useQueryClient()
56+
57+
return useQuery({
58+
queryKey: getTrendingWinnersQueryKey({ week, type }),
59+
queryFn: async () => {
60+
const sdk = await audiusSdk()
61+
62+
const params = {
63+
week: week ? new Date(week + 'T12:00:00Z') : undefined,
64+
userId: OptionalId.parse(currentUserId)
65+
}
66+
67+
const response =
68+
type === 'underground'
69+
? await sdk.tracks.getTrendingUndergroundWinners(params)
70+
: await sdk.tracks.getTrendingWinners(params)
71+
72+
const sdkResponse = response?.data ?? []
73+
74+
const trackFullLike = sdkResponse
75+
.map(toTrackFullLike)
76+
.filter((t): t is full.TrackFull => t !== null)
77+
78+
const tracks = transformAndCleanList(
79+
trackFullLike,
80+
userTrackMetadataFromSDK
81+
)
82+
83+
primeTrackData({ tracks, queryClient })
84+
85+
return tracks
86+
},
87+
...options,
88+
enabled: options?.enabled !== false
89+
})
90+
}

packages/common/src/api/tan-query/queryKeys.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export const QUERY_KEYS = {
8080
profileTracks: 'profileTracks',
8181
trendingIds: 'trendingIds',
8282
trendingUnderground: 'trendingUnderground',
83+
trendingWinners: 'trendingWinners',
8384
trackPageLineup: 'trackPageLineup',
8485
connectedWallets: 'connectedWallets',
8586
audioBalance: 'audioBalance',

packages/common/src/store/pages/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ export { trendingUndergroundPageLineupActions } from './trending-underground/lin
44

55
export { default as trendingUndergroundPageReducer } from './trending-underground/slice'
66

7+
export * as trendingWinnersPageLineupSelectors from './trending-winners/lineup/selectors'
8+
export { trendingWinnersPageLineupActions } from './trending-winners/lineup/actions'
9+
export { default as trendingWinnersPageReducer } from './trending-winners/slice'
10+
711
export * as trendingPageLineupReducer from './trending/lineup/reducer'
812
export * as trendingPageLineupSelectors from './trending/lineup/selectors'
913
export * as trendingPageLineupActions from './trending/lineup/actions'
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { LineupActions } from '../../../lineup/actions'
2+
3+
export const PREFIX = 'TRENDING_WINNERS'
4+
5+
class TrendingWinnersPageLineupActions extends LineupActions {
6+
constructor() {
7+
super(PREFIX)
8+
}
9+
}
10+
11+
export const trendingWinnersPageLineupActions =
12+
new TrendingWinnersPageLineupActions()
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { RESET_SUCCEEDED, stripPrefix } from '~/store/lineup/actions'
2+
import { initialLineupState } from '~/store/lineup/reducer'
3+
4+
import { LineupState, Track } from '../../../../models'
5+
6+
import { PREFIX } from './actions'
7+
8+
export const initialState: LineupState<Track> = {
9+
...initialLineupState,
10+
prefix: PREFIX
11+
}
12+
13+
type ResetSucceeded = typeof RESET_SUCCEEDED
14+
15+
type ResetSucceededAction = {
16+
type: ResetSucceeded
17+
}
18+
19+
const actionsMap = {
20+
[RESET_SUCCEEDED](
21+
_state: typeof initialState,
22+
_action: ResetSucceededAction
23+
) {
24+
const newState = initialState
25+
return newState
26+
}
27+
}
28+
29+
const trendingWinnersReducer = (
30+
state = initialState,
31+
action: ResetSucceededAction
32+
) => {
33+
const baseActionType = stripPrefix(PREFIX, action.type) as ResetSucceeded
34+
const matchingReduceFunction = actionsMap[baseActionType]
35+
if (!matchingReduceFunction) return state
36+
return matchingReduceFunction(state, action)
37+
}
38+
39+
export default trendingWinnersReducer
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { CommonState } from '~/store/commonStore'
2+
3+
export const getLineup = (state: CommonState) =>
4+
state.pages.trendingWinners.trending
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { combineReducers, createSlice } from '@reduxjs/toolkit'
2+
3+
import { asLineup } from '~/store/lineup/reducer'
4+
5+
import { PREFIX } from './lineup/actions'
6+
import trendingReducer, {
7+
initialState as initialLineupState
8+
} from './lineup/reducer'
9+
10+
const initialState = { trending: initialLineupState }
11+
12+
const slice = createSlice({
13+
name: 'application/pages/trendingWinners',
14+
initialState,
15+
reducers: {}
16+
})
17+
18+
const trendingWinnersLineupReducer = asLineup(PREFIX, trendingReducer)
19+
20+
export default combineReducers({
21+
page: slice.reducer,
22+
trending: trendingWinnersLineupReducer
23+
})

packages/common/src/store/queue/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export enum QueueSource {
2525
CHAT_PLAYLIST_TRACKS = 'CHAT_PLAYLIST_TRACKS',
2626
EXPLORE_PREMIUM_TRACKS = 'EXPLORE_PREMIUM_TRACKS',
2727
PICK_WINNERS_TRACKS = 'PICK_WINNERS_TRACKS',
28+
DISCOVER_TRENDING_WINNERS = 'DISCOVER_TRENDING_WINNERS',
2829
EXPLORE = 'EXPLORE'
2930
}
3031

packages/common/src/store/reducers.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import { TrackPageState } from './pages/track/types'
4444
import trending from './pages/trending/reducer'
4545
import { TrendingPageState } from './pages/trending/types'
4646
import trendingUnderground from './pages/trending-underground/slice'
47+
import trendingWinners from './pages/trending-winners/slice'
4748
import { PlaybackPositionState } from './playback-position'
4849
import playbackPosition from './playback-position/slice'
4950
import player, { PlayerState } from './player/slice'
@@ -185,6 +186,7 @@ export const reducers = (storage: Storage, history?: History) => ({
185186
track,
186187
trending: trending(history),
187188
trendingUnderground,
189+
trendingWinners,
188190
settings,
189191
remixes,
190192
exclusiveTracks,
@@ -286,6 +288,7 @@ export type CommonState = {
286288
settings: SettingsPageState
287289
trending: TrendingPageState
288290
trendingUnderground: ReturnType<typeof trendingUnderground>
291+
trendingWinners: ReturnType<typeof trendingWinners>
289292
remixes: ReturnType<typeof remixes>
290293
exclusiveTracks: ReturnType<typeof exclusiveTracks>
291294
premiumTracks: ReturnType<typeof premiumTracks>

0 commit comments

Comments
 (0)