-
Notifications
You must be signed in to change notification settings - Fork 134
[PE-7266] Add coin exclusive tracks to coin page #13332
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
2c50821
[PE-7266] Add tracks to coin page
dylanjeffers 3ebb5cb
Finalize
dylanjeffers be1f3a7
Fixes
dylanjeffers 84ade88
Last change
dylanjeffers 043e8ae
drop nft
dylanjeffers d556ff7
Merge branch 'main' into pe-7266-tracks-on-coin-page
dylanjeffers e758e84
A few sdk changes
dylanjeffers e82d737
Fixes
dylanjeffers 9eb5c02
drop chart library
dylanjeffers File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
149 changes: 149 additions & 0 deletions
149
packages/common/src/api/tan-query/lineups/useExclusiveTracks.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| import { EntityType, OptionalId } from '@audius/sdk' | ||
| import { | ||
| InfiniteData, | ||
| useInfiniteQuery, | ||
| useQuery, | ||
| useQueryClient | ||
| } from '@tanstack/react-query' | ||
| import { useDispatch } from 'react-redux' | ||
|
|
||
| import { transformAndCleanList, userTrackMetadataFromSDK } from '~/adapters' | ||
| import { useQueryContext } from '~/api/tan-query/utils' | ||
| import { PlaybackSource } from '~/models/Analytics' | ||
| import { ID } from '~/models/Identifiers' | ||
| import { | ||
| exclusiveTracksPageSelectors, | ||
| exclusiveTracksPageLineupActions | ||
| } from '~/store/pages' | ||
|
|
||
| import { QUERY_KEYS } from '../queryKeys' | ||
| import { QueryKey, LineupData, QueryOptions } from '../types' | ||
| import { useCurrentUserId } from '../users/account/useCurrentUserId' | ||
| import { primeTrackData } from '../utils/primeTrackData' | ||
|
|
||
| import { useLineupQuery } from './useLineupQuery' | ||
|
|
||
| const DEFAULT_PAGE_SIZE = 10 | ||
|
|
||
| type GateCondition = 'ungated' | 'usdc_purchase' | 'follow' | 'tip' | 'token' | ||
|
|
||
| type UseExclusiveTracksArgs = { | ||
| userId: ID | null | undefined | ||
| gateConditions?: GateCondition[] | ||
| pageSize?: number | ||
| initialPageSize?: number | ||
| } | ||
|
|
||
| export const getExclusiveTracksQueryKey = ({ | ||
| userId, | ||
| gateConditions = ['token'], | ||
| pageSize | ||
| }: UseExclusiveTracksArgs) => | ||
| [ | ||
| QUERY_KEYS.exclusiveTracks, | ||
| userId, | ||
| { gateConditions, pageSize } | ||
| ] as unknown as QueryKey<InfiniteData<LineupData[]>> | ||
|
|
||
| export const useExclusiveTracks = ( | ||
| { | ||
| userId, | ||
| gateConditions = ['token'], | ||
| pageSize = DEFAULT_PAGE_SIZE, | ||
| initialPageSize | ||
| }: UseExclusiveTracksArgs, | ||
| options?: QueryOptions | ||
| ) => { | ||
| const { audiusSdk } = useQueryContext() | ||
| const { data: currentUserId } = useCurrentUserId() | ||
| const queryClient = useQueryClient() | ||
| const dispatch = useDispatch() | ||
|
|
||
| const queryData = useInfiniteQuery({ | ||
| queryKey: getExclusiveTracksQueryKey({ | ||
| userId, | ||
| gateConditions, | ||
| pageSize | ||
| }), | ||
| initialPageParam: 0, | ||
| getNextPageParam: (lastPage: LineupData[], allPages) => { | ||
| if (lastPage.length < pageSize) return undefined | ||
| return allPages.length * pageSize | ||
| }, | ||
| queryFn: async ({ pageParam }) => { | ||
| const sdk = await audiusSdk() | ||
| const { data: tracks = [] } = await sdk.full.users.getTracksByUser({ | ||
| id: OptionalId.parse(userId)!, | ||
| userId: OptionalId.parse(currentUserId), | ||
| gateCondition: gateConditions as any, | ||
| limit: pageSize, | ||
| offset: pageParam | ||
| }) | ||
|
|
||
| const processedTracks = transformAndCleanList( | ||
| tracks, | ||
| userTrackMetadataFromSDK | ||
| ) | ||
| primeTrackData({ tracks: processedTracks, queryClient }) | ||
|
|
||
| // Update lineup when new data arrives | ||
| dispatch( | ||
| exclusiveTracksPageLineupActions.fetchLineupMetadatas( | ||
| pageParam, | ||
| pageSize, | ||
| false, | ||
| { items: processedTracks } | ||
| ) | ||
| ) | ||
|
|
||
| return processedTracks.map((t) => ({ | ||
| id: t.track_id, | ||
| type: EntityType.TRACK | ||
| })) | ||
| }, | ||
| select: (data) => data?.pages.flat(), | ||
| ...options, | ||
| enabled: options?.enabled !== false && !!userId | ||
| }) | ||
|
|
||
| return useLineupQuery({ | ||
| lineupData: queryData.data ?? [], | ||
| queryData, | ||
| queryKey: getExclusiveTracksQueryKey({ | ||
| userId, | ||
| gateConditions, | ||
| pageSize | ||
| }), | ||
| lineupActions: exclusiveTracksPageLineupActions, | ||
| lineupSelector: exclusiveTracksPageSelectors.getLineup, | ||
| playbackSource: PlaybackSource.EXCLUSIVE_TRACKS_PAGE, | ||
| pageSize, | ||
| initialPageSize | ||
| }) | ||
| } | ||
|
|
||
| // Hook to get the count of exclusive tracks | ||
| export const useExclusiveTracksCount = (args: { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this right? seems like we'd need a separate endpoint? |
||
| userId: ID | null | undefined | ||
| gateConditions?: GateCondition[] | ||
| enabled?: boolean | ||
| }) => { | ||
| const { userId, gateConditions = ['token'], enabled = true } = args | ||
| const { audiusSdk } = useQueryContext() | ||
| const { data: currentUserId } = useCurrentUserId() | ||
|
|
||
| return useQuery({ | ||
| queryKey: [QUERY_KEYS.exclusiveTracksCount, userId, { gateConditions }], | ||
| queryFn: async () => { | ||
| const sdk = await audiusSdk() | ||
| const { data: count } = await sdk.full.users.getTracksCountByUser({ | ||
| id: OptionalId.parse(userId)!, | ||
| userId: OptionalId.parse(currentUserId), | ||
| gateCondition: gateConditions as any | ||
| }) | ||
|
|
||
| return count ?? 0 | ||
| }, | ||
| enabled: enabled && !!userId | ||
| }) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
69 changes: 69 additions & 0 deletions
69
packages/common/src/api/tan-query/wallets/useUserBalanceHistory.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| import { useQuery } from '@tanstack/react-query' | ||
|
|
||
| import { QueryOptions } from '../types' | ||
| import { useCurrentUserId } from '../users/account/useCurrentUserId' | ||
|
|
||
| export type BalanceHistoryDataPoint = { | ||
| timestamp: number | ||
| balanceUsd: number | ||
| } | ||
|
|
||
| type UseUserBalanceHistoryParams = { | ||
| userId?: number | ||
| enabled?: boolean | ||
| } | ||
|
|
||
| /** | ||
| * Mock hook for getting user balance history (coins + USDC + AUDIO in USD). | ||
| * Returns hourly data points for the past week. | ||
| * TODO: Replace with real API call when backend is ready | ||
| */ | ||
| export const useUserBalanceHistory = ( | ||
| params: UseUserBalanceHistoryParams = {}, | ||
| options?: QueryOptions | ||
| ) => { | ||
| const { data: currentUserId } = useCurrentUserId() | ||
| const userId = params.userId ?? currentUserId | ||
|
|
||
| return useQuery({ | ||
| queryKey: ['userBalanceHistory', userId], | ||
| queryFn: async (): Promise<BalanceHistoryDataPoint[]> => { | ||
| // Mock data: Generate hourly data points for the past week (7 days * 24 hours = 168 data points) | ||
| const now = Date.now() | ||
| const oneHour = 60 * 60 * 1000 | ||
| const oneWeek = 7 * 24 * oneHour | ||
| const startTime = now - oneWeek | ||
|
|
||
| const dataPoints: BalanceHistoryDataPoint[] = [] | ||
|
|
||
| // Generate realistic-looking balance data with some variance | ||
| const baseBalance = 5000 // Base balance in USD | ||
| let previousBalance = baseBalance | ||
|
|
||
| for (let i = 0; i <= 168; i++) { | ||
| const timestamp = startTime + i * oneHour | ||
|
|
||
| // Add some realistic variance (±2% per hour with trend) | ||
| const randomVariance = (Math.random() - 0.48) * 0.02 | ||
| const trendVariance = Math.sin(i / 20) * 0.01 // Slight upward/downward trend | ||
| const totalVariance = 1 + randomVariance + trendVariance | ||
|
|
||
| const newBalance = previousBalance * totalVariance | ||
| previousBalance = newBalance | ||
|
|
||
| dataPoints.push({ | ||
| timestamp, | ||
| balanceUsd: Math.round(newBalance * 100) / 100 // Round to 2 decimals | ||
| }) | ||
| } | ||
|
|
||
| // Simulate network delay | ||
| await new Promise((resolve) => setTimeout(resolve, 300)) | ||
|
|
||
| return dataPoints | ||
| }, | ||
| enabled: !!userId && (params.enabled ?? true) && (options?.enabled ?? true), | ||
| staleTime: 60000, // 1 minute | ||
| ...options | ||
| }) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| export { default as exclusiveTracksPageReducer } from './slice' | ||
| export * as exclusiveTracksPageActions from './slice' | ||
| export * as exclusiveTracksPageLineupActions from './lineup/actions' | ||
| export * as exclusiveTracksPageSelectors from './selectors' |
11 changes: 11 additions & 0 deletions
11
packages/common/src/store/pages/exclusive-tracks/lineup/actions.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import { LineupActions } from '~/store/lineup/actions' | ||
|
|
||
| export const PREFIX = 'EXCLUSIVE_TRACKS' | ||
|
|
||
| class TracksActions extends LineupActions { | ||
| constructor() { | ||
| super(PREFIX) | ||
| } | ||
| } | ||
|
|
||
| export const tracksActions = new TracksActions() |
25 changes: 25 additions & 0 deletions
25
packages/common/src/store/pages/exclusive-tracks/lineup/reducer.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| import { RESET_SUCCEEDED, stripPrefix } from '~/store/lineup/actions' | ||
| import { initialLineupState } from '~/store/lineup/reducer' | ||
|
|
||
| import { PREFIX } from './actions' | ||
|
|
||
| export const initialState = { | ||
| ...initialLineupState, | ||
| prefix: PREFIX | ||
| } | ||
|
|
||
| const actionsMap: { [key in string]: any } = { | ||
| [RESET_SUCCEEDED](_state: typeof initialState) { | ||
| const newState = initialState | ||
| return newState | ||
| } | ||
| } | ||
|
|
||
| const tracks = (state = initialState, action: { type: string }) => { | ||
| const baseActionType = stripPrefix(PREFIX, action.type) | ||
| const matchingReduceFunction = actionsMap[baseActionType] | ||
| if (!matchingReduceFunction) return state | ||
| return matchingReduceFunction(state, action) | ||
| } | ||
|
|
||
| export default tracks |
7 changes: 7 additions & 0 deletions
7
packages/common/src/store/pages/exclusive-tracks/selectors.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| import { CommonState } from '~/store/commonStore' | ||
|
|
||
| export const getBaseState = (state: CommonState) => state.pages.exclusiveTracks | ||
|
|
||
| export const getLineup = (state: CommonState) => getBaseState(state).tracks | ||
|
|
||
| export const getUserId = (state: CommonState) => getBaseState(state).page.userId |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| import { createSlice, PayloadAction } from '@reduxjs/toolkit' | ||
| import { combineReducers } from 'redux' | ||
|
|
||
| import { asLineup } from '~/store/lineup/reducer' | ||
|
|
||
| import { ID } from '../../../models/Identifiers' | ||
|
|
||
| import { PREFIX as exclusiveTracksPrefix } from './lineup/actions' | ||
| import exclusiveTracksReducer, { | ||
| initialState as initialLineupState | ||
| } from './lineup/reducer' | ||
|
|
||
| type State = { | ||
| userId: ID | null | ||
| tracks: typeof initialLineupState | ||
| } | ||
|
|
||
| const initialState: State = { | ||
| userId: null, | ||
| tracks: initialLineupState | ||
| } | ||
|
|
||
| const slice = createSlice({ | ||
| name: 'application/pages/exclusiveTracks', | ||
| initialState, | ||
| reducers: { | ||
| reset: (state) => { | ||
| state.userId = null | ||
| }, | ||
| setUserId: (state, action: PayloadAction<{ userId: ID }>) => { | ||
| const { userId } = action.payload | ||
| state.userId = userId | ||
| } | ||
| } | ||
| }) | ||
|
|
||
| const exclusiveTracksLineupReducer = asLineup( | ||
| exclusiveTracksPrefix, | ||
| exclusiveTracksReducer | ||
| ) | ||
|
|
||
| export const { reset, setUserId } = slice.actions | ||
|
|
||
| export default combineReducers({ | ||
| page: slice.reducer, | ||
| tracks: exclusiveTracksLineupReducer | ||
| }) | ||
|
|
||
| export const actions = slice.actions |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just checking 10 was intentional?