diff --git a/packages/common/src/api/index.ts b/packages/common/src/api/index.ts index bff8dcec58c..9b01ab416a7 100644 --- a/packages/common/src/api/index.ts +++ b/packages/common/src/api/index.ts @@ -88,7 +88,7 @@ export * from './tan-query/tracks/useRemixedTracks' // Users export * from './tan-query/users/account/useCurrentUserId' -export * from './tan-query/users/account/useCurrentUser' +export * from './tan-query/users/account/useWalletUser' export * from './tan-query/users/account/useAddToPlaylistFolder' export * from './tan-query/users/account/useCurrentAccount' export * from './tan-query/users/account/usePlaylistLibrary' diff --git a/packages/common/src/api/tan-query/queryKeys.ts b/packages/common/src/api/tan-query/queryKeys.ts index 4409333524c..7e0d04a392d 100644 --- a/packages/common/src/api/tan-query/queryKeys.ts +++ b/packages/common/src/api/tan-query/queryKeys.ts @@ -1,6 +1,8 @@ export const QUERY_KEYS = { aiTracks: 'aiTracks', accountUser: 'accountUser', + walletUser: 'walletUser', + walletAccount: 'walletAccount', trackCommentList: 'trackCommentList', userCommentList: 'userCommentList', comment: 'comment', diff --git a/packages/common/src/api/tan-query/tracks/useDeleteTrack.ts b/packages/common/src/api/tan-query/tracks/useDeleteTrack.ts index c222c986059..22b6fd306d4 100644 --- a/packages/common/src/api/tan-query/tracks/useDeleteTrack.ts +++ b/packages/common/src/api/tan-query/tracks/useDeleteTrack.ts @@ -1,6 +1,6 @@ import { Id } from '@audius/sdk' import { useMutation, useQueryClient } from '@tanstack/react-query' -import { useDispatch, useSelector } from 'react-redux' +import { useDispatch } from 'react-redux' import { useAudiusQueryContext } from '~/audius-query' import { useAppContext } from '~/context/appContext' @@ -9,10 +9,8 @@ import { Feature } from '~/models/ErrorReporting' import { ID } from '~/models/Identifiers' import { Track } from '~/models/Track' import { UserMetadata } from '~/models/User' -import { getWalletAddresses } from '~/store/account/selectors' import { deleteTrackRequested } from '~/store/cache/tracks/actions' -import { getCurrentUserQueryKey } from '../users/account/useCurrentUser' import { useCurrentUserId } from '../users/account/useCurrentUserId' import { useUser } from '../users/useUser' import { primeTrackData } from '../utils/primeTrackData' @@ -35,7 +33,6 @@ export const useDeleteTrack = () => { const queryClient = useQueryClient() const dispatch = useDispatch() const { data: currentUserId } = useCurrentUserId() - const { currentUser: currentUserWallet } = useSelector(getWalletAddresses) const { data: currentUser } = useUser(currentUserId) const { analytics: { track: trackEvent } @@ -78,11 +75,6 @@ export const useDeleteTrack = () => { dispatch, forceReplace: true }) - - queryClient.setQueryData( - getCurrentUserQueryKey(currentUserWallet), - updatedCurrentUser - ) } // Optimistic update in cache diff --git a/packages/common/src/api/tan-query/users/account/useCurrentUser.ts b/packages/common/src/api/tan-query/users/account/useCurrentUser.ts deleted file mode 100644 index bc57d3ca64c..00000000000 --- a/packages/common/src/api/tan-query/users/account/useCurrentUser.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { useQuery } from '@tanstack/react-query' -import { useSelector } from 'react-redux' - -import { accountFromSDK } from '~/adapters/user' -import { useAudiusQueryContext } from '~/audius-query' -import { UserMetadata } from '~/models/User' -import { getWalletAddresses } from '~/store/account/selectors' - -import { QUERY_KEYS } from '../../queryKeys' -import { QueryKey, SelectableQueryOptions } from '../../types' - -export const getCurrentUserQueryKey = ( - currentUser: string | null | undefined -) => - [ - QUERY_KEYS.accountUser, - currentUser - ] as unknown as QueryKey - -/** - * Hook to get the currently logged in user's data - */ -export const useCurrentUser = ( - options?: SelectableQueryOptions -) => { - const { audiusSdk } = useAudiusQueryContext() - const { currentUser } = useSelector(getWalletAddresses) - - return useQuery({ - queryKey: getCurrentUserQueryKey(currentUser), - queryFn: async () => { - const sdk = await audiusSdk() - const { data } = await sdk.full.users.getUserAccount({ - wallet: currentUser! - }) - - if (!data) { - console.warn('Missing user from account response') - return null - } - - const account = accountFromSDK(data) - return account?.user - }, - ...options, - enabled: options?.enabled !== false && !!currentUser - }) -} diff --git a/packages/common/src/api/tan-query/users/account/useWalletUser.ts b/packages/common/src/api/tan-query/users/account/useWalletUser.ts new file mode 100644 index 00000000000..e3e6aff889b --- /dev/null +++ b/packages/common/src/api/tan-query/users/account/useWalletUser.ts @@ -0,0 +1,59 @@ +import { AudiusSdk } from '@audius/sdk' +import { useQuery } from '@tanstack/react-query' +import { useSelector } from 'react-redux' + +import { accountFromSDK } from '~/adapters/user' +import { useAudiusQueryContext } from '~/audius-query' +import { AccountUserMetadata, ID } from '~/models' +import { getWalletAddresses } from '~/store/account/selectors' + +import { QUERY_KEYS } from '../../queryKeys' +import { QueryKey, SelectableQueryOptions } from '../../types' + +export const getWalletAccountQueryKey = (wallet: string | null | undefined) => + [ + QUERY_KEYS.walletAccount, + wallet + ] as unknown as QueryKey + +export const getWalletUserQueryKey = (wallet: string | null | undefined) => + [QUERY_KEYS.walletUser, wallet] as unknown as QueryKey + +// This queryFn is separate in order to be used in sagas +export const getWalletAccountQueryFn = async ( + wallet: string, + sdk: AudiusSdk +) => { + const { data } = await sdk.full.users.getUserAccount({ + wallet + }) + + if (!data) { + console.warn('Missing user from account response') + return null + } + + const account = accountFromSDK(data) + return account +} + +/** + * Hook to get the currently logged in user's data + */ +export const useWalletUser = ( + options?: SelectableQueryOptions +) => { + const { audiusSdk } = useAudiusQueryContext() + const { currentUser: currentUserWallet } = useSelector(getWalletAddresses) + + return useQuery({ + queryKey: getWalletUserQueryKey(currentUserWallet), + queryFn: async () => { + const sdk = await audiusSdk() + return (await getWalletAccountQueryFn(currentUserWallet!, sdk))?.user + ?.user_id + }, + ...options, + enabled: options?.enabled !== false && !!currentUserWallet + }) +} diff --git a/packages/common/src/api/tan-query/users/useUpdateUser.ts b/packages/common/src/api/tan-query/users/useUpdateUser.ts index fe9950b899d..b79b27e8af5 100644 --- a/packages/common/src/api/tan-query/users/useUpdateUser.ts +++ b/packages/common/src/api/tan-query/users/useUpdateUser.ts @@ -7,7 +7,7 @@ import { ID } from '~/models/Identifiers' import { PlaylistLibrary } from '~/models/PlaylistLibrary' import { UserMetadata } from '~/models/User' -import { getCurrentUserQueryKey } from './account/useCurrentUser' +import { getCurrentAccountQueryKey } from './account/useCurrentAccount' import { getUserQueryKey } from './useUser' type MutationContext = { @@ -53,7 +53,7 @@ export const useUpdateUser = () => { // Snapshot the previous account user if it matches const previousAccountUser = queryClient .getQueriesData({ - queryKey: getCurrentUserQueryKey(userId.toString()) + queryKey: getCurrentAccountQueryKey(userId) }) .find(([_, data]) => data?.user_id === userId)?.[1] @@ -65,7 +65,7 @@ export const useUpdateUser = () => { // Optimistically update accountUser queries if they match the user queryClient.setQueriesData( - { queryKey: getCurrentUserQueryKey(userId.toString()) }, + { queryKey: getCurrentAccountQueryKey(userId) }, (oldData: any) => { if (!oldData?.user_id || oldData.user_id !== userId) return oldData return { ...oldData, ...metadata } @@ -84,7 +84,7 @@ export const useUpdateUser = () => { // Roll back accountUser queries if we have the previous state if (context?.previousAccountUser) { queryClient.setQueriesData( - { queryKey: getCurrentUserQueryKey(userId.toString()) }, + { queryKey: getCurrentAccountQueryKey(userId) }, (oldData: any) => { if (!oldData?.user_id || oldData.user_id !== userId) return oldData return context.previousAccountUser diff --git a/packages/common/src/api/user.ts b/packages/common/src/api/user.ts index 1491b283210..5a59a8b290f 100644 --- a/packages/common/src/api/user.ts +++ b/packages/common/src/api/user.ts @@ -1,6 +1,6 @@ import { full, Id, OptionalId } from '@audius/sdk' -import { accountFromSDK, userMetadataListFromSDK } from '~/adapters/user' +import { userMetadataListFromSDK } from '~/adapters/user' import { createApi } from '~/audius-query' import { ID, Kind, StringUSDC } from '~/models' import { @@ -8,7 +8,6 @@ import { USDCTransactionMethod, USDCTransactionType } from '~/models/USDCTransactions' -import { isResponseError } from '~/utils' import { Nullable } from '~/utils/typeUtils' type GetUSDCTransactionListArgs = { @@ -43,30 +42,7 @@ const parseTransaction = ({ const userApi = createApi({ reducerPath: 'userApi', endpoints: { - // TODO: Remove this once saga calls are removed - getUserAccount: { - fetch: async ({ wallet }: { wallet: string }, { audiusSdk }) => { - try { - const sdk = await audiusSdk() - const { data } = await sdk.full.users.getUserAccount({ wallet }) - if (!data) { - console.warn('Missing user from account response') - return null - } - - return accountFromSDK(data) - } catch (e) { - // Account doesn't exist, don't bubble up an error, just return null - if (isResponseError(e) && [401, 404].includes(e.response.status)) { - return null - } - throw e - } - }, - options: { - schemaKey: 'accountUser' - } - }, + // TODO: Remove these once fetch export calls are removed getUserByHandle: { fetch: async ( { @@ -135,6 +111,5 @@ const userApi = createApi({ export const userApiReducer = userApi.reducer export const userApiFetch = userApi.fetch -export const userApiFetchSaga = userApi.fetchSaga export const userApiActions = userApi.actions export const userApiUtils = userApi.util diff --git a/packages/common/src/store/account/sagas.ts b/packages/common/src/store/account/sagas.ts index 0be9e393322..d61c82c72fd 100644 --- a/packages/common/src/store/account/sagas.ts +++ b/packages/common/src/store/account/sagas.ts @@ -9,7 +9,7 @@ import { takeLatest } from 'typed-redux-saga' -import { userApiFetchSaga } from '~/api/user' +import { getWalletAccountQueryFn, getWalletAccountQueryKey } from '~/api' import { AccountUserMetadata, ErrorLevel, Kind, UserMetadata } from '~/models' import { getContext } from '~/store/effects' import { chatActions } from '~/store/pages/chat' @@ -103,15 +103,18 @@ function* initializeMetricsForUser({ const solanaWalletService = yield* getContext('solanaWalletService') const analytics = yield* getContext('analytics') const sdk = yield* getSDK() + const queryClient = yield* getContext('queryClient') if (accountUser && accountUser.handle) { const [web3WalletAddress] = yield* call([ sdk.services.audiusWalletClient, sdk.services.audiusWalletClient.getAddresses ]) - const { user: web3User } = yield* call(userApiFetchSaga.getUserAccount, { - wallet: web3WalletAddress - }) + const accountData = (yield* call([queryClient, queryClient.fetchQuery], { + queryKey: getWalletAccountQueryKey(web3WalletAddress), + queryFn: async () => getWalletAccountQueryFn(web3WalletAddress, sdk) + })) as AccountUserMetadata | undefined + const { user: web3User } = accountData ?? {} let solanaWallet let managerUserId @@ -164,13 +167,15 @@ export function* fetchAccountAsync({ const localStorage = yield* getContext('localStorage') const reportToSentry = yield* getContext('reportToSentry') const sdk = yield* getSDK() + const queryClient = yield* getContext('queryClient') // Don't revert successful local account fetch if (shouldMarkAccountAsLoading) { yield* put(fetchAccountRequested()) } - let wallet, web3WalletAddress + let wallet: string | undefined + let web3WalletAddress: string | undefined try { const connectedWallets = yield* call([ sdk.services.audiusWalletClient, @@ -200,13 +205,10 @@ export function* fetchAccountAsync({ return } - const accountData: AccountUserMetadata | undefined = yield* call( - userApiFetchSaga.getUserAccount, - { - wallet - }, - true // force refresh to get updated user w handle - ) + const accountData = (yield* call([queryClient, queryClient.fetchQuery], { + queryKey: getWalletAccountQueryKey(wallet), + queryFn: async () => getWalletAccountQueryFn(wallet!, sdk) + })) as AccountUserMetadata | undefined if (!accountData) { yield* put(resetAccount()) diff --git a/packages/web/src/common/store/pages/signon/sagas.ts b/packages/web/src/common/store/pages/signon/sagas.ts index 768357bfbcc..868ea18bca9 100644 --- a/packages/web/src/common/store/pages/signon/sagas.ts +++ b/packages/web/src/common/store/pages/signon/sagas.ts @@ -1,4 +1,7 @@ -import { userApiFetchSaga } from '@audius/common/api' +import { + getWalletAccountQueryFn, + getWalletAccountQueryKey +} from '@audius/common/api' import { GUEST_EMAIL } from '@audius/common/hooks' import { Name, @@ -538,6 +541,7 @@ function* createGuestAccount( function* signUp() { const reportToSentry = yield* getContext('reportToSentry') const localStorage = yield* getContext('localStorage') + const queryClient = yield* getContext('queryClient') try { const signOn = yield* select(getSignOn) @@ -587,12 +591,14 @@ function* signUp() { sdk.services.audiusWalletClient, sdk.services.audiusWalletClient.getAddresses ]) - const account: AccountUserMetadata | null = yield* call( - userApiFetchSaga.getUserAccount, + const account = (yield* call( + [queryClient, queryClient.fetchQuery], { - wallet + queryKey: getWalletAccountQueryKey(wallet), + queryFn: async () => getWalletAccountQueryFn(wallet!, sdk) } - ) + )) as AccountUserMetadata | undefined + // TODO: Do I need to prime the accountUser slice here? if (!account) { throw new Error('Account user ID does not exist') } @@ -858,6 +864,7 @@ function* signIn(action: ReturnType) { const authService = yield* getContext('authService') const isNativeMobile = yield* getContext('isNativeMobile') const isElectron = yield* getContext('isElectron') + const queryClient = yield* getContext('queryClient') const clientOrigin = isNativeMobile ? 'mobile' : isElectron @@ -896,12 +903,11 @@ function* signIn(action: ReturnType) { return } - const account: AccountUserMetadata | null = yield* call( - userApiFetchSaga.getUserAccount, - { - wallet: signInResponse.walletAddress - } - ) + const account = (yield* call([queryClient, queryClient.fetchQuery], { + queryKey: getWalletAccountQueryKey(signInResponse.walletAddress), + queryFn: async () => + getWalletAccountQueryFn(signInResponse.walletAddress, sdk) + })) as AccountUserMetadata | undefined // Login succeeded but we found no account for the user (incomplete signup) if (!account) { diff --git a/packages/web/src/hooks/useConnectAndAssociateWallets.ts b/packages/web/src/hooks/useConnectAndAssociateWallets.ts index 18e47e3aaea..7a5710783a7 100644 --- a/packages/web/src/hooks/useConnectAndAssociateWallets.ts +++ b/packages/web/src/hooks/useConnectAndAssociateWallets.ts @@ -3,9 +3,9 @@ import { useRef, useCallback, useEffect } from 'react' import { type ConnectedWallet, useConnectedWallets, - useAddConnectedWallet + useAddConnectedWallet, + useCurrentAccount } from '@audius/common/api' -import { useCurrentUser } from '@audius/common/api' import { useAppContext } from '@audius/common/context' import { Name, Chain } from '@audius/common/models' import { useTheme } from '@emotion/react' @@ -89,7 +89,8 @@ export const useConnectAndAssociateWallets = ( const theme = useTheme() const { open } = useAppKit() const { signMessageAgnostic } = useSignMessageAgnostic() - const currentUser = useCurrentUser() + const { data: currentAccount } = useCurrentAccount() + const currentUser = currentAccount!.user const { data: connectedWallets } = useConnectedWallets() const { switchAccountAsync } = useSwitchAccount() const { disconnect } = useDisconnect() @@ -141,7 +142,7 @@ export const useConnectAndAssociateWallets = ( const chainId = await connector!.getChainId() const connectedAccountIsUserWallet = accounts && - accounts[0]?.toLowerCase() === currentUser.data?.wallet?.toLowerCase() + accounts[0]?.toLowerCase() === currentUser?.wallet?.toLowerCase() if (!connectedAccountIsUserWallet || chainId !== audiusChain.id) { console.debug( '[associate-wallet]', @@ -153,7 +154,7 @@ export const useConnectAndAssociateWallets = ( await switchAccountAsync({ connector }) } } - }, [currentUser.data?.wallet, switchAccountAsync]) + }, [currentUser.wallet, switchAccountAsync]) /** * Associates any Reown connected wallets to the user's account. @@ -164,7 +165,7 @@ export const useConnectAndAssociateWallets = ( isAssociatingRef.current = true track(make({ eventName: Name.CONNECT_WALLET_NEW_WALLET_START })) const activeAccount = appkitModal.getAccount() - const originalAddress = currentUser?.data?.wallet + const originalAddress = currentUser?.wallet // Map the wallets to add our chain format const wallets = @@ -218,7 +219,7 @@ export const useConnectAndAssociateWallets = ( }) ) const signature = await signMessageAgnostic( - `AudiusUserID:${currentUser.data?.user_id}`, + `AudiusUserID:${currentUser.user_id}`, address, namespace ) @@ -259,8 +260,8 @@ export const useConnectAndAssociateWallets = ( }, [ addConnectedWalletAsync, connectedWallets, - currentUser.data?.user_id, - currentUser.data?.wallet, + currentUser.user_id, + currentUser.wallet, make, onError, onSuccess,