Skip to content
Merged
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
8 changes: 7 additions & 1 deletion packages/common/src/store/reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,9 @@ import followersUserListReducer from './user-list/followers/reducers'
import followingUserListReducer from './user-list/following/reducers'
import mutualsUserListReducer from './user-list/mutuals/reducers'
import notificationsUserListReducer from './user-list/notifications/reducers'
import purchasersUserListReducer from './user-list/purchasers/reducers'
import relatedArtistsListReducer from './user-list/related-artists/reducers'
import remixersUserListReducer from './user-list/remixers/reducers'
import repostsUserListReducer from './user-list/reposts/reducers'
import supportingUserListReducer from './user-list/supporting/reducers'
import topSupportersUserListReducer from './user-list/top-supporters/reducers'
Expand Down Expand Up @@ -210,7 +212,9 @@ export const reducers = (storage: Storage, history?: History) => ({
supporting: supportingUserListReducer,
mutuals: mutualsUserListReducer,
notifications: notificationsUserListReducer,
relatedArtists: relatedArtistsListReducer
relatedArtists: relatedArtistsListReducer,
purchasers: purchasersUserListReducer,
remixers: remixersUserListReducer
}),
theme,
vipDiscordModal: vipDiscordModalReducer,
Expand Down Expand Up @@ -338,6 +342,8 @@ export type CommonState = {
topSupporters: ReturnType<typeof topSupportersUserListReducer>
supporting: ReturnType<typeof supportingUserListReducer>
relatedArtists: ReturnType<typeof relatedArtistsListReducer>
purchasers: ReturnType<typeof purchasersUserListReducer>
remixers: ReturnType<typeof remixersUserListReducer>
}
theme: ThemeState
vipDiscordModal: VipDiscordModalState
Expand Down
8 changes: 8 additions & 0 deletions packages/common/src/store/user-list/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,11 @@ export * from './favorites/types'
export * as relatedArtistsUserListActions from './related-artists/actions'
export * as relatedArtistsUserListSelectors from './related-artists/selectors'
export * from './related-artists/types'

export * as purchasersUserListActions from './purchasers/actions'
export * as purchasersUserListSelectors from './purchasers/selectors'
export * from './purchasers/types'

export * as remixersUserListActions from './remixers/actions'
export * as remixersUserListSelectors from './remixers/selectors'
export * from './remixers/types'
31 changes: 31 additions & 0 deletions packages/common/src/store/user-list/purchasers/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { createCustomAction } from 'typesafe-actions'

import type { PurchaseableContentType } from '~/store/purchase-content'

import { ID } from '../../../models'

export const SET_PURCHASERS = 'PURCHASERS_USER_PAGE/SET_PURCHASERS'
export const GET_PURCHASERS_ERROR = 'PURCHASERS_USER_PAGE/GET_PURCHASERS_ERROR'

export const setPurchasers = createCustomAction(
SET_PURCHASERS,
(id: ID, contentType?: PurchaseableContentType, contentId?: ID) => ({
id,
contentType,
contentId
})
)
export const getPurchasersError = createCustomAction(
GET_PURCHASERS_ERROR,
(
id: ID,
error: string,
contentType?: PurchaseableContentType,
contentId?: ID
) => ({
id,
contentType,
contentId,
error
})
)
39 changes: 39 additions & 0 deletions packages/common/src/store/user-list/purchasers/reducers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { combineReducers } from 'redux'
import { createReducer, ActionType } from 'typesafe-actions'

import { UserListReducerFactory } from '../reducer'

import * as actions from './actions'
import { PurchasersOwnState, PURCHASERS_USER_LIST_TAG } from './types'

type PurchasersActions = ActionType<typeof actions>

const userListReducer = UserListReducerFactory.createReducer({
tag: PURCHASERS_USER_LIST_TAG,
pageSize: 15
})

const initialState = {
id: null,
contentType: undefined,
contentId: undefined
}

const purchasersPageReducer = createReducer<
PurchasersOwnState,
PurchasersActions
>(initialState, {
[actions.SET_PURCHASERS](state, action) {
return {
...state,
id: action.id,
contentType: action.contentType,
contentId: action.contentId
}
}
})

export default combineReducers({
purchasersPage: purchasersPageReducer,
userList: userListReducer
})
18 changes: 18 additions & 0 deletions packages/common/src/store/user-list/purchasers/selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { CommonState } from '~/store/commonStore'
import { PurchaseableContentType } from '~/store/purchase-content'

import { ID } from '../../../models/Identifiers'
import { UserListStoreState } from '../types'

export const getId = (state: CommonState): ID | null =>
state.ui.userList.purchasers.purchasersPage.id
export const getUserList = (state: CommonState): UserListStoreState =>
state.ui.userList.purchasers.userList
export const getUserIds = (state: CommonState): ID[] =>
state.ui.userList.purchasers.userList.userIds
export const getContentId = (state: CommonState): ID | undefined =>
state.ui.userList.purchasers.purchasersPage.contentId
export const getContentType = (
state: CommonState
): PurchaseableContentType | undefined =>
state.ui.userList.purchasers.purchasersPage.contentType
17 changes: 17 additions & 0 deletions packages/common/src/store/user-list/purchasers/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { PurchaseableContentType } from '~/store/purchase-content'
import { UserListStoreState } from '~/store/user-list/types'

import { ID } from '../../../models'

export type PurchasersOwnState = {
id: ID | null
contentType?: PurchaseableContentType
contentId?: ID
}

export type PurchasersPageState = {
purchasersPage: PurchasersOwnState
userList: UserListStoreState
}

export const PURCHASERS_USER_LIST_TAG = 'PURCHASERS'
22 changes: 22 additions & 0 deletions packages/common/src/store/user-list/remixers/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { createCustomAction } from 'typesafe-actions'

import { ID } from '../../../models'

export const SET_REMIXERS = 'REMIXERS_USER_PAGE/SET_REMIXERS'
export const GET_REMIXERS_ERROR = 'REMIXERS_USER_PAGE/GET_REMIXERS_ERROR'

export const setRemixers = createCustomAction(
SET_REMIXERS,
(id: ID, trackId?: ID) => ({
id,
trackId
})
)
export const getRemixersError = createCustomAction(
GET_REMIXERS_ERROR,
(id: ID, error: string, trackId?: ID) => ({
id,
trackId,
error
})
)
37 changes: 37 additions & 0 deletions packages/common/src/store/user-list/remixers/reducers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { combineReducers } from 'redux'
import { createReducer, ActionType } from 'typesafe-actions'

import { UserListReducerFactory } from '../reducer'

import * as actions from './actions'
import { RemixersOwnState, REMIXERS_USER_LIST_TAG } from './types'

type RemixersActions = ActionType<typeof actions>

const userListReducer = UserListReducerFactory.createReducer({
tag: REMIXERS_USER_LIST_TAG,
pageSize: 15
})

const initialState = {
id: null,
trackId: undefined
}

const remixersPageReducer = createReducer<RemixersOwnState, RemixersActions>(
initialState,
{
[actions.SET_REMIXERS](state, action) {
return {
...state,
id: action.id,
trackId: action.trackId
}
}
}
)

export default combineReducers({
remixersPage: remixersPageReducer,
userList: userListReducer
})
13 changes: 13 additions & 0 deletions packages/common/src/store/user-list/remixers/selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { CommonState } from '~/store/commonStore'

import { ID } from '../../../models/Identifiers'
import { UserListStoreState } from '../types'

export const getId = (state: CommonState): ID | null =>
state.ui.userList.remixers.remixersPage.id
export const getUserList = (state: CommonState): UserListStoreState =>
state.ui.userList.remixers.userList
export const getUserIds = (state: CommonState): ID[] =>
state.ui.userList.remixers.userList.userIds
export const getTrackId = (state: CommonState): ID | undefined =>
state.ui.userList.remixers.remixersPage.trackId
15 changes: 15 additions & 0 deletions packages/common/src/store/user-list/remixers/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { UserListStoreState } from '~/store/user-list/types'

import { ID } from '../../../models'

export type RemixersOwnState = {
id: ID | null
trackId?: ID
}

export type RemixersPageState = {
remixersPage: RemixersOwnState
userList: UserListStoreState
}

export const REMIXERS_USER_LIST_TAG = 'REMIXERS'
4 changes: 4 additions & 0 deletions packages/mobile/src/store/sagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ import followersPageSagas from 'common/store/user-list/followers/sagas'
import followingPageSagas from 'common/store/user-list/following/sagas'
import mutualsPageSagas from 'common/store/user-list/mutuals/sagas'
import notificationUsersPageSagas from 'common/store/user-list/notifications/sagas'
import purchasersPageSagas from 'common/store/user-list/purchasers/sagas'
import relatedArtistsPageSagas from 'common/store/user-list/related-artists/sagas'
import remixersPageSagas from 'common/store/user-list/remixers/sagas'
import repostPageSagas from 'common/store/user-list/reposts/sagas'
import supportingPageSagas from 'common/store/user-list/supporting/sagas'
import topSupportersPageSagas from 'common/store/user-list/top-supporters/sagas'
Expand Down Expand Up @@ -170,6 +172,8 @@ export default function* rootSaga() {
...settingsSagas(),
...aiSagas(),
...premiumTracksSagas(),
...purchasersPageSagas(),
...remixersPageSagas(),

// Cast
...castSagas(),
Expand Down
25 changes: 25 additions & 0 deletions packages/web/src/common/store/user-list/purchasers/errorSagas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { purchasersUserListActions } from '@audius/common/store'
import { put, takeEvery } from 'redux-saga/effects'

import * as errorActions from 'store/errors/actions'
const { GET_PURCHASERS_ERROR, getPurchasersError } = purchasersUserListActions

type HandlePurchasersError = ReturnType<typeof getPurchasersError>

export function* handlePurchasersError(action: HandlePurchasersError) {
yield put(
errorActions.handleError({
message: action.type,
shouldRedirect: true,
shouldReport: true,
additionalInfo: {
errorMessage: action.error,
id: action.id
}
})
)
}

export function* watchPurchasersError() {
yield takeEvery([GET_PURCHASERS_ERROR], handlePurchasersError)
}
94 changes: 94 additions & 0 deletions packages/web/src/common/store/user-list/purchasers/sagas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import {
userMetadataFromSDK,
transformAndCleanList
} from '@audius/common/adapters'
import { Id, ID, OptionalId } from '@audius/common/models'
import {
accountSelectors,
UserListSagaFactory,
purchasersUserListActions,
purchasersUserListSelectors,
PURCHASERS_USER_LIST_TAG,
getSDK,
PurchaseableContentType
} from '@audius/common/store'
import { call, put, select } from 'typed-redux-saga'

import { watchPurchasersError } from './errorSagas'

export const MAX_PURCHASERS = 50

const { getPurchasersError } = purchasersUserListActions
const { getId, getUserList, getContentId, getContentType } =
purchasersUserListSelectors

type FetchPurchasersArgs = {
id: ID
contentId?: ID
contentType?: PurchaseableContentType
currentPage: number
pageSize: number
}

function* fetchPurchasers({
id,
contentId,
contentType,
currentPage,
pageSize
}: FetchPurchasersArgs) {
const offset = currentPage * pageSize
const sdk = yield* getSDK()
const currentUserId = yield* select(accountSelectors.getUserId)

const { data } = yield* call([sdk.full.users, sdk.full.users.getPurchasers], {
id: Id.parse(id),
limit: MAX_PURCHASERS,
offset,
userId: OptionalId.parse(currentUserId),
contentType,
contentId: contentId?.toString()
})

const users = transformAndCleanList(data, userMetadataFromSDK)

const userIds = users.map((user) => user.user_id)
return {
userIds,
hasMore: false
}
}

function* errorDispatcher(error: Error) {
const id = yield* select(getId)
if (id) {
yield* put(getPurchasersError(id, error.message))
}
}

function* getPurchasers(currentPage: number, pageSize: number) {
const id = yield* select(getId)
if (!id) return { userIds: [], hasMore: false }

const contentType = yield* select(getContentType)
const contentId = yield* select(getContentId)

return yield* call(fetchPurchasers, {
id,
contentId,
contentType,
currentPage,
pageSize
})
}

const userListSagas = UserListSagaFactory.createSagas({
tag: PURCHASERS_USER_LIST_TAG,
fetchUsers: getPurchasers,
stateSelector: getUserList,
errorDispatcher
})

export default function sagas() {
return [userListSagas, watchPurchasersError]
}
Loading