diff --git a/packages/common/src/api/account.ts b/packages/common/src/api/account.ts deleted file mode 100644 index c55722e5842..00000000000 --- a/packages/common/src/api/account.ts +++ /dev/null @@ -1,281 +0,0 @@ -import { Id } from '@audius/sdk' -import dayjs from 'dayjs' -import { useSelector } from 'react-redux' - -import { managedUserListFromSDK, userManagerListFromSDK } from '~/adapters/user' -import { createApi } from '~/audius-query' -import { AccountUserMetadata, ID, User, UserMetadata } from '~/models' -import { getWalletAddresses } from '~/store/account/selectors' - -import { SelectableQueryOptions } from './tan-query/types' -import { useWalletAccount } from './tan-query/users/account/useWalletUser' - -type ResetPasswordArgs = { - email: string - password: string -} - -type RequestAddManagerPayload = { - userId: number - managerUser: UserMetadata | User -} - -type RemoveManagerPayload = { - userId: number - managerUserId: number -} - -type ApproveManagedAccountPayload = { - userId: number - grantorUser: UserMetadata | User -} - -const accountApi = createApi({ - reducerPath: 'accountApi', - endpoints: { - resetPassword: { - async fetch(args: ResetPasswordArgs, context) { - const { email, password } = args - const { authService } = context - await authService.resetPassword({ - username: email, - password - }) - return { status: 'ok' } - }, - options: { - type: 'mutation' - } - }, - getManagedAccounts: { - async fetch({ userId }: { userId: ID }, { audiusSdk }) { - const sdk = await audiusSdk() - const managedUsers = await sdk.full.users.getManagedUsers({ - id: Id.parse(userId) - }) - - const { data = [] } = managedUsers - return managedUserListFromSDK(data) - }, - options: { - schemaKey: 'managedUsers' - } - }, - getManagers: { - async fetch({ userId }: { userId: ID }, { audiusSdk }) { - const sdk = await audiusSdk() - const managedUsers = await sdk.full.users.getManagers({ - id: Id.parse(userId) - }) - - const { data: rawData = [] } = managedUsers - const data = rawData.filter((g) => g.grant.isApproved !== false) - return userManagerListFromSDK(data) - }, - options: { - schemaKey: 'userManagers' - } - }, - requestAddManager: { - async fetch(payload: RequestAddManagerPayload, { audiusSdk }) { - const { managerUser, userId } = payload - const managerUserId = managerUser.user_id - const encodedUserId = Id.parse(userId) as string - const encodedManagerUserId = Id.parse(managerUserId) - const sdk = await audiusSdk() - - await sdk.grants.addManager({ - userId: encodedUserId, - managerUserId: encodedManagerUserId - }) - - return payload - }, - options: { - type: 'mutation', - schemaKey: 'userManagers' - }, - async onQuerySuccess( - _res, - payload: RequestAddManagerPayload, - { dispatch } - ) { - const { userId, managerUser } = payload - dispatch( - accountApi.util.updateQueryData( - 'getManagers', - { userId }, - (state) => { - const currentTime = dayjs().format('YYYY-MM-DD HH:mm:ss') - // TODO(C-4330) - The state type is incorrect - fix. - // @ts-expect-error - state.userManagers.push({ - grant: { - created_at: currentTime, - grantee_address: managerUser.erc_wallet ?? managerUser.wallet, - is_approved: null, - is_revoked: false, - updated_at: currentTime, - user_id: userId - }, - manager: managerUser - }) - } - ) - ) - } - }, - removeManager: { - async fetch(payload: RemoveManagerPayload, { audiusSdk }) { - const { managerUserId, userId } = payload - const encodedUserId = Id.parse(userId) as string - const encodedManagerUserId = Id.parse(managerUserId) - const sdk = await audiusSdk() - - await sdk.grants.removeManager({ - userId: encodedUserId, - managerUserId: encodedManagerUserId - }) - - return payload - }, - options: { - type: 'mutation', - schemaKey: 'userManagers' - }, - async onQueryStarted(payload: RemoveManagerPayload, { dispatch }) { - const { managerUserId, userId } = payload - dispatch( - accountApi.util.updateQueryData( - 'getManagedAccounts', - { userId: managerUserId }, - (state) => { - // TODO(C-4330) - The state type is incorrect - fix. - // @ts-expect-error - const foundIndex = state.managedUsers?.findIndex( - (m: { user: number }) => m.user === userId - ) - if (foundIndex != null && foundIndex > -1) { - // @ts-expect-error (C-4330) - state.managedUsers.splice(foundIndex, 1) - } - } - ) - ) - dispatch( - accountApi.util.updateQueryData( - 'getManagers', - { userId }, - (state) => { - // TODO(C-4330) - The state type is incorrect - fix. - // @ts-expect-error - const foundIndex = state.userManagers?.findIndex( - (m: { manager: number }) => { - return m.manager === managerUserId - } - ) - if (foundIndex != null && foundIndex > -1) { - // @ts-expect-error (C-4330) - state.userManagers.splice(foundIndex, 1) - } - } - ) - ) - } - }, - approveManagedAccount: { - async fetch(payload: ApproveManagedAccountPayload, { audiusSdk }) { - const { grantorUser, userId } = payload - const grantorUserId = grantorUser.user_id - const encodedUserId = Id.parse(userId) as string - const encodedGrantorUserId = Id.parse(grantorUserId) - const sdk = await audiusSdk() - - await sdk.grants.approveGrant({ - userId: encodedUserId, - grantorUserId: encodedGrantorUserId - }) - - return payload - }, - options: { - type: 'mutation' - }, - async onQueryStarted( - payload: ApproveManagedAccountPayload, - { dispatch } - ) { - const { userId, grantorUser } = payload - dispatch( - accountApi.util.updateQueryData( - 'getManagedAccounts', - { userId }, - (state) => { - // TODO(C-4330) - The state type is incorrect - fix. - // @ts-expect-error - const foundIndex = state.managedUsers.findIndex( - (m: { user: number }) => m.user === grantorUser.user_id - ) - // @ts-expect-error - state.managedUsers[foundIndex].grant.is_approved = true - } - ) - ) - } - } - } -}) - -export const useGetCurrentUser = ( - options?: SelectableQueryOptions< - AccountUserMetadata | null | undefined, - TResult - > -) => { - const { currentUser } = useSelector(getWalletAddresses) - - return useWalletAccount(currentUser, { - select: (data: AccountUserMetadata | null | undefined): TResult => - data?.user as TResult, - ...options - }) -} - -export const useGetCurrentWeb3User = ( - options?: SelectableQueryOptions< - AccountUserMetadata | null | undefined, - TResult - > -) => { - const { web3User } = useSelector(getWalletAddresses) - - return useWalletAccount(web3User, { - select: (data: AccountUserMetadata | null | undefined): TResult => - data?.user as TResult, - ...options - }) -} - -export const useGetCurrentUserId = ( - options?: SelectableQueryOptions< - AccountUserMetadata | null | undefined, - TResult - > -) => { - return useGetCurrentUser({ - select: (accountData: AccountUserMetadata | null | undefined): TResult => - accountData?.user.user_id as TResult, - ...options - }) -} - -export const { - useResetPassword, - useGetManagedAccounts, - useGetManagers, - useRequestAddManager, - useApproveManagedAccount, - useRemoveManager -} = accountApi.hooks - -export const accountApiReducer = accountApi.reducer diff --git a/packages/common/src/api/index.ts b/packages/common/src/api/index.ts index 650bd3a2c32..96393626413 100644 --- a/packages/common/src/api/index.ts +++ b/packages/common/src/api/index.ts @@ -1,6 +1,5 @@ // Audius query // TODO: migrate all of these to tan-query -export * from './account' export * from './authorizedApps' export * from './developerApps' export * from './library' @@ -119,6 +118,12 @@ export * from './tan-query/users/useUserCollectibles' export * from './tan-query/users/useUserPlaylists' export * from './tan-query/users/useUsers' export * from './tan-query/users/useUserTracksByHandle' +export * from './tan-query/users/account/useResetPassword' +export * from './tan-query/users/account/useManagedAccounts' +export * from './tan-query/users/account/useManagers' +export * from './tan-query/users/account/useRequestAddManager' +export * from './tan-query/users/account/useApproveManagedAccount' +export * from './tan-query/users/account/useRemoveManager' // Wallet logic export * from './tan-query/wallets/useAudioBalance' diff --git a/packages/common/src/api/reducer.ts b/packages/common/src/api/reducer.ts index 2c21bb1ddee..eb77b0212d1 100644 --- a/packages/common/src/api/reducer.ts +++ b/packages/common/src/api/reducer.ts @@ -1,6 +1,5 @@ import { combineReducers } from 'redux' -import { accountApiReducer } from './account' import { authorizedAppsApiReducer } from './authorizedApps' import { developerAppsApiReducer } from './developerApps' import { favoritesApiReducer } from './favorites' @@ -12,7 +11,6 @@ import { trendingApiReducer } from './trending' import { userApiReducer } from './user' export default combineReducers({ - accountApi: accountApiReducer, authorizedAppsApi: authorizedAppsApiReducer, developerAppsApi: developerAppsApiReducer, favoritesApi: favoritesApiReducer, diff --git a/packages/common/src/api/tan-query/queryKeys.ts b/packages/common/src/api/tan-query/queryKeys.ts index 7e0d04a392d..1a0fbdd5bda 100644 --- a/packages/common/src/api/tan-query/queryKeys.ts +++ b/packages/common/src/api/tan-query/queryKeys.ts @@ -90,5 +90,7 @@ export const QUERY_KEYS = { walletOwner: 'walletOwner', tokenPrice: 'tokenPrice', usdcBalance: 'usdcBalance', - fileSizes: 'fileSizes' + fileSizes: 'fileSizes', + managedAccounts: 'managedAccounts', + userManagers: 'userManagers' } as const diff --git a/packages/common/src/api/tan-query/users/account/useApproveManagedAccount.ts b/packages/common/src/api/tan-query/users/account/useApproveManagedAccount.ts new file mode 100644 index 00000000000..ff73dc91ee8 --- /dev/null +++ b/packages/common/src/api/tan-query/users/account/useApproveManagedAccount.ts @@ -0,0 +1,36 @@ +import { Id } from '@audius/sdk' +import { useMutation, useQueryClient } from '@tanstack/react-query' + +import { useAudiusQueryContext } from '~/audius-query' +import { User, UserMetadata } from '~/models' + +import { getManagedAccountsQueryKey } from './useManagedAccounts' + +type ApproveManagedAccountPayload = { + userId: number + grantorUser: UserMetadata | User +} + +export const useApproveManagedAccount = () => { + const { audiusSdk } = useAudiusQueryContext() + const queryClient = useQueryClient() + return useMutation({ + mutationFn: async (payload: ApproveManagedAccountPayload) => { + const { grantorUser, userId } = payload + const grantorUserId = grantorUser.user_id + const encodedUserId = Id.parse(userId) + const encodedGrantorUserId = Id.parse(grantorUserId) + const sdk = await audiusSdk() + await sdk.grants.approveGrant({ + userId: encodedUserId, + grantorUserId: encodedGrantorUserId + }) + return payload + }, + onSuccess: (data) => { + queryClient.invalidateQueries({ + queryKey: getManagedAccountsQueryKey(data.userId) + }) + } + }) +} diff --git a/packages/common/src/api/tan-query/users/account/useCurrentAccount.ts b/packages/common/src/api/tan-query/users/account/useCurrentAccount.ts index a30262b41f5..9be172c1680 100644 --- a/packages/common/src/api/tan-query/users/account/useCurrentAccount.ts +++ b/packages/common/src/api/tan-query/users/account/useCurrentAccount.ts @@ -78,6 +78,7 @@ export const useCurrentAccount = < }, staleTime: options?.staleTime ?? Infinity, gcTime: Infinity, - enabled: options?.enabled !== false && !!currentUserWallet + enabled: options?.enabled !== false && !!currentUserWallet, + ...options }) } diff --git a/packages/common/src/api/tan-query/users/account/useManagedAccounts.ts b/packages/common/src/api/tan-query/users/account/useManagedAccounts.ts new file mode 100644 index 00000000000..d2a7f29caec --- /dev/null +++ b/packages/common/src/api/tan-query/users/account/useManagedAccounts.ts @@ -0,0 +1,36 @@ +import { Id } from '@audius/sdk' +import { useQuery } from '@tanstack/react-query' + +import { managedUserListFromSDK } from '~/adapters/user' +import { useAudiusQueryContext } from '~/audius-query' +import { ID } from '~/models/Identifiers' +import { ManagedUserMetadata } from '~/models/User' + +import { QUERY_KEYS } from '../../queryKeys' +import { QueryKey, SelectableQueryOptions } from '../../types' +import { isValidId } from '../../utils/isValidId' + +export const getManagedAccountsQueryKey = (userId: ID | null | undefined) => + [QUERY_KEYS.managedAccounts, userId] as unknown as QueryKey< + ManagedUserMetadata[] + > + +export const useManagedAccounts = ( + userId?: ID | null, + options?: SelectableQueryOptions +) => { + const { audiusSdk } = useAudiusQueryContext() + return useQuery({ + queryKey: getManagedAccountsQueryKey(userId), + queryFn: async () => { + const sdk = await audiusSdk() + const managedUsers = await sdk.full.users.getManagedUsers({ + id: Id.parse(userId) + }) + const { data = [] } = managedUsers + return managedUserListFromSDK(data) as TResult + }, + enabled: isValidId(userId), + ...options + }) +} diff --git a/packages/common/src/api/tan-query/users/account/useManagers.ts b/packages/common/src/api/tan-query/users/account/useManagers.ts new file mode 100644 index 00000000000..3950ef8cbab --- /dev/null +++ b/packages/common/src/api/tan-query/users/account/useManagers.ts @@ -0,0 +1,38 @@ +import { Id } from '@audius/sdk' +import { useQuery } from '@tanstack/react-query' + +import { userManagerListFromSDK } from '~/adapters/user' +import { useAudiusQueryContext } from '~/audius-query' +import { ID } from '~/models/Identifiers' +import { UserManagerMetadata } from '~/models/User' + +import { QUERY_KEYS } from '../../queryKeys' +import { QueryKey, SelectableQueryOptions } from '../../types' +import { isValidId } from '../../utils/isValidId' + +export const getManagersQueryKey = (userId: ID | null | undefined) => + [QUERY_KEYS.userManagers, userId] as unknown as QueryKey< + UserManagerMetadata[] + > + +export const useManagers = ( + userId?: ID | null, + options?: SelectableQueryOptions +) => { + const { audiusSdk } = useAudiusQueryContext() + return useQuery({ + queryKey: getManagersQueryKey(userId), + queryFn: async () => { + const sdk = await audiusSdk() + const managers = await sdk.full.users.getManagers({ + id: Id.parse(userId) + }) + const { data: rawData = [] } = managers + // Only include approved or pending (not explicitly false) + const data = rawData.filter((g: any) => g.grant.isApproved !== false) + return userManagerListFromSDK(data) as TResult + }, + enabled: isValidId(userId), + ...options + }) +} diff --git a/packages/common/src/api/tan-query/users/account/useRemoveManager.ts b/packages/common/src/api/tan-query/users/account/useRemoveManager.ts new file mode 100644 index 00000000000..ef2ef41b1c5 --- /dev/null +++ b/packages/common/src/api/tan-query/users/account/useRemoveManager.ts @@ -0,0 +1,38 @@ +import { Id } from '@audius/sdk' +import { useMutation, useQueryClient } from '@tanstack/react-query' + +import { useAudiusQueryContext } from '~/audius-query' + +import { getManagedAccountsQueryKey } from './useManagedAccounts' +import { getManagersQueryKey } from './useManagers' + +type RemoveManagerPayload = { + userId: number + managerUserId: number +} + +export const useRemoveManager = () => { + const { audiusSdk } = useAudiusQueryContext() + const queryClient = useQueryClient() + return useMutation({ + mutationFn: async (payload: RemoveManagerPayload) => { + const { managerUserId, userId } = payload + const encodedUserId = Id.parse(userId) + const encodedManagerUserId = Id.parse(managerUserId) + const sdk = await audiusSdk() + await sdk.grants.removeManager({ + userId: encodedUserId, + managerUserId: encodedManagerUserId + }) + return payload + }, + onSuccess: (data) => { + queryClient.invalidateQueries({ + queryKey: getManagedAccountsQueryKey(data.managerUserId) + }) + queryClient.invalidateQueries({ + queryKey: getManagersQueryKey(data.userId) + }) + } + }) +} diff --git a/packages/common/src/api/tan-query/users/account/useRequestAddManager.ts b/packages/common/src/api/tan-query/users/account/useRequestAddManager.ts new file mode 100644 index 00000000000..80417b34e60 --- /dev/null +++ b/packages/common/src/api/tan-query/users/account/useRequestAddManager.ts @@ -0,0 +1,36 @@ +import { Id } from '@audius/sdk' +import { useMutation, useQueryClient } from '@tanstack/react-query' + +import { useAudiusQueryContext } from '~/audius-query' +import { User, UserMetadata } from '~/models' + +import { getManagersQueryKey } from './useManagers' + +type RequestAddManagerPayload = { + userId: number + managerUser: UserMetadata | User +} + +export const useRequestAddManager = () => { + const { audiusSdk } = useAudiusQueryContext() + const queryClient = useQueryClient() + return useMutation({ + mutationFn: async (payload: RequestAddManagerPayload) => { + const { managerUser, userId } = payload + const managerUserId = managerUser.user_id + const encodedUserId = Id.parse(userId) + const encodedManagerUserId = Id.parse(managerUserId) + const sdk = await audiusSdk() + await sdk.grants.addManager({ + userId: encodedUserId, + managerUserId: encodedManagerUserId + }) + return payload + }, + onSuccess: (data) => { + queryClient.invalidateQueries({ + queryKey: getManagersQueryKey(data.userId) + }) + } + }) +} diff --git a/packages/common/src/api/tan-query/users/account/useResetPassword.ts b/packages/common/src/api/tan-query/users/account/useResetPassword.ts new file mode 100644 index 00000000000..0c0c53d0b3f --- /dev/null +++ b/packages/common/src/api/tan-query/users/account/useResetPassword.ts @@ -0,0 +1,21 @@ +import { useMutation } from '@tanstack/react-query' + +import { useAudiusQueryContext } from '~/audius-query' + +type ResetPasswordArgs = { + email: string + password: string +} + +export const useResetPassword = () => { + const { authService } = useAudiusQueryContext() + return useMutation({ + mutationFn: async ({ email, password }: ResetPasswordArgs) => { + await authService.resetPassword({ + username: email, + password + }) + return { status: 'ok' } + } + }) +} diff --git a/packages/common/src/api/tan-query/users/account/useWalletUser.ts b/packages/common/src/api/tan-query/users/account/useWalletUser.ts index d2bb67e19e8..dd04673afa6 100644 --- a/packages/common/src/api/tan-query/users/account/useWalletUser.ts +++ b/packages/common/src/api/tan-query/users/account/useWalletUser.ts @@ -1,9 +1,11 @@ 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 } from '~/models' +import { AccountUserMetadata, UserMetadata } from '~/models' +import { getWalletAddresses } from '~/store/account/selectors' import { QUERY_KEYS } from '../../queryKeys' import { QueryKey, SelectableQueryOptions } from '../../types' @@ -56,3 +58,51 @@ export const useWalletAccount = < enabled: options?.enabled !== false && !!wallet }) } + +// Some helper selectors - these pull the current wallet addresses out of redux for you +// NOTE: currentUser means the user that we are currently auth-ed into - i.e. the managed user +export const useGetCurrentUser = ( + options?: SelectableQueryOptions< + AccountUserMetadata | null | undefined, + TResult + > +) => { + const { currentUser } = useSelector(getWalletAddresses) + + return useWalletAccount(currentUser, { + select: (data: AccountUserMetadata | null | undefined): TResult => + data?.user as TResult, + ...options + }) +} + +// Some helper selectors - these pull the current wallet addresses out of redux for you +// NOTE: web3User means the user that signed in originally (i.e. could be a manager) +export const useGetCurrentWeb3User = ( + options?: SelectableQueryOptions< + AccountUserMetadata | null | undefined, + TResult + > +) => { + const { web3User } = useSelector(getWalletAddresses) + + return useWalletAccount(web3User, { + select: (data: AccountUserMetadata | null | undefined): TResult => + data?.user as TResult, + ...options + }) +} + +// NOTE: currentUser means the user that we are currently auth-ed into - i.e. the managed user +export const useGetCurrentUserId = ( + options?: SelectableQueryOptions< + AccountUserMetadata | null | undefined, + TResult + > +) => { + return useGetCurrentUser({ + select: (accountData: AccountUserMetadata | null | undefined): TResult => + accountData?.user.user_id as TResult, + ...options + }) +} diff --git a/packages/common/src/api/tan-query/utils/isValidId.ts b/packages/common/src/api/tan-query/utils/isValidId.ts new file mode 100644 index 00000000000..df054e93154 --- /dev/null +++ b/packages/common/src/api/tan-query/utils/isValidId.ts @@ -0,0 +1,3 @@ +export const isValidId = (id: number | null | undefined | string) => { + return !!id && typeof id === 'number' && id > 0 +} diff --git a/packages/common/src/hooks/useAccountSwitcher.ts b/packages/common/src/hooks/useAccountSwitcher.ts index 66ad4a81fb4..78393f61385 100644 --- a/packages/common/src/hooks/useAccountSwitcher.ts +++ b/packages/common/src/hooks/useAccountSwitcher.ts @@ -1,6 +1,6 @@ import { useCallback } from 'react' -import { useGetCurrentUserId, useGetCurrentWeb3User } from '~/api/account' +import { useGetCurrentUserId, useGetCurrentWeb3User } from '~/api' import { useAppContext } from '~/context' import { Name } from '~/models/Analytics' import { UserMetadata } from '~/models/User' diff --git a/packages/common/src/store/pages/chat/selectors.ts b/packages/common/src/store/pages/chat/selectors.ts index 913a0503c32..b928b0471e0 100644 --- a/packages/common/src/store/pages/chat/selectors.ts +++ b/packages/common/src/store/pages/chat/selectors.ts @@ -258,7 +258,7 @@ export const getCanCreateChat = createSelector( (state: CommonState, { userId }: { userId: Maybe }) => { if (!userId) return null const usersMap = getUsers(state, { ids: [userId] }) - return usersMap[userId].metadata + return usersMap[userId]?.metadata } ], ( diff --git a/packages/mobile/src/screens/reset-password-screen/ResetPasswordScreen.tsx b/packages/mobile/src/screens/reset-password-screen/ResetPasswordScreen.tsx index aea91920a3f..ecf99905405 100644 --- a/packages/mobile/src/screens/reset-password-screen/ResetPasswordScreen.tsx +++ b/packages/mobile/src/screens/reset-password-screen/ResetPasswordScreen.tsx @@ -1,7 +1,6 @@ -import { useCallback, useEffect, useState } from 'react' +import { useCallback, useEffect } from 'react' import { useResetPassword } from '@audius/common/api' -import { Status } from '@audius/common/models' import { accountSelectors } from '@audius/common/store' import AsyncStorage from '@react-native-async-storage/async-storage' import type { NavigationProp, RouteProp } from '@react-navigation/native' @@ -49,16 +48,18 @@ const ResetPasswordScreen = () => { const { login, email } = params const navigation = useNavigation>() const isSignedIn = useSelector(getHasAccount) - const [resetStatus, setResetStatus] = useState(Status.IDLE) const { toast } = useToast() useAsync(async () => { await AsyncStorage.setItem(ENTROPY_KEY, atob(login)) }, []) - const [resetPassword, result] = useResetPassword() - - const { status } = result + const { + mutate: resetPassword, + isPending, + isSuccess, + isError + } = useResetPassword() const handleCancel = useCallback(() => { if (isSignedIn) { @@ -71,18 +72,23 @@ const ResetPasswordScreen = () => { const handleSubmit = useCallback( (password: string) => { resetPassword({ email, password }) - setResetStatus(Status.LOADING) }, [resetPassword, email] ) + // Restart on success useEffect(() => { - if (status === Status.SUCCESS) { + if (isSuccess) { RNRestart.Restart() - } else if (status === Status.ERROR) { + } + }, [isSuccess]) + + // Error handling + useEffect(() => { + if (isError) { toast({ content: messages.error }) } - }, [status, toast]) + }, [isError, toast]) return ( { diff --git a/packages/web/src/components/nav/desktop/AccountSwitcher/AccountSwitcher.tsx b/packages/web/src/components/nav/desktop/AccountSwitcher/AccountSwitcher.tsx index e1d814c3da4..289709ee651 100644 --- a/packages/web/src/components/nav/desktop/AccountSwitcher/AccountSwitcher.tsx +++ b/packages/web/src/components/nav/desktop/AccountSwitcher/AccountSwitcher.tsx @@ -3,10 +3,10 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useGetCurrentUserId, useGetCurrentWeb3User, - useGetManagedAccounts + useManagedAccounts } from '@audius/common/api' import { useAccountSwitcher } from '@audius/common/hooks' -import { Status, UserMetadata } from '@audius/common/models' +import { UserMetadata } from '@audius/common/models' import { accountSelectors } from '@audius/common/store' import { Box, IconButton, IconCaretDown, Popup } from '@audius/harmony' import { useSelector } from 'react-redux' @@ -36,8 +36,8 @@ export const AccountSwitcher = () => { const web3UserId = currentWeb3User?.user_id ?? null - const { data: managedAccounts = [], status: accountsStatus } = - useGetManagedAccounts({ userId: web3UserId! }, { disabled: !web3UserId }) + const { data: managedAccounts = [], isSuccess: isManagedAccountsSuccess } = + useManagedAccounts(web3UserId) const parentElementRef = useRef(null) const onClickExpander = useCallback( @@ -55,7 +55,7 @@ export const AccountSwitcher = () => { if ( !currentUserId || !currentWeb3User || - accountsStatus !== Status.SUCCESS || + !isManagedAccountsSuccess || checkedAccess ) { return @@ -70,10 +70,10 @@ export const AccountSwitcher = () => { } }, [ accounts, - accountsStatus, checkedAccess, currentUserId, currentWeb3User, + isManagedAccountsSuccess, switchToWeb3User ]) diff --git a/packages/web/src/hooks/useManagedAccountNotAllowedRedirect.ts b/packages/web/src/hooks/useManagedAccountNotAllowedRedirect.ts index f3be50839a5..433f0878d7f 100644 --- a/packages/web/src/hooks/useManagedAccountNotAllowedRedirect.ts +++ b/packages/web/src/hooks/useManagedAccountNotAllowedRedirect.ts @@ -1,8 +1,7 @@ import { useContext, useEffect } from 'react' -import { useGetManagedAccounts } from '@audius/common/api' +import { useManagedAccounts } from '@audius/common/api' import { useIsManagedAccount } from '@audius/common/hooks' -import { Status } from '@audius/common/models' import { accountSelectors } from '@audius/common/store' import { route } from '@audius/common/utils' import { useSelector } from 'react-redux' @@ -76,17 +75,10 @@ export const useIsUnauthorizedForHandleRedirect = ( const navigate = useNavigateToPage() const { toast } = useContext(ToastContext) - const { data: managedAccounts = [], status: accountsStatus } = - useGetManagedAccounts( - { userId: accountUserId! }, - { disabled: !accountUserId } - ) + const { data: managedAccounts = [], isPending: isManagedAccountsPending } = + useManagedAccounts(accountUserId) - const isLoading = - !accountHandle || - !accountUserId || - accountsStatus === Status.LOADING || - accountsStatus === Status.IDLE + const isLoading = !accountHandle || !accountUserId || isManagedAccountsPending const isOwner = accountHandle?.toLowerCase() === handle.toLowerCase() const isManaged = !!accountHandle && diff --git a/packages/web/src/pages/oauth-login-page/OAuthLoginPage.tsx b/packages/web/src/pages/oauth-login-page/OAuthLoginPage.tsx index d163043c794..96a30593643 100644 --- a/packages/web/src/pages/oauth-login-page/OAuthLoginPage.tsx +++ b/packages/web/src/pages/oauth-login-page/OAuthLoginPage.tsx @@ -4,7 +4,7 @@ import { accountFromSDK } from '@audius/common/adapters' import { useGetCurrentUserId, useGetCurrentWeb3User, - useGetManagedAccounts + useManagedAccounts } from '@audius/common/api' import { useAccountSwitcher } from '@audius/common/hooks' import { Name, ErrorLevel, UserMetadata } from '@audius/common/models' @@ -274,10 +274,7 @@ export const OAuthLoginPage = () => { const web3UserId = currentWeb3User?.user_id ?? null - const { data: managedAccounts = [] } = useGetManagedAccounts( - { userId: web3UserId! }, - { disabled: !web3UserId } - ) + const { data: managedAccounts = [] } = useManagedAccounts(web3UserId) const accounts = useMemo(() => { return managedAccounts.filter(({ grant }) => grant.is_approved) diff --git a/packages/web/src/pages/settings-page/components/desktop/ManagerMode/AccountListItem/ManagedUserListItem.tsx b/packages/web/src/pages/settings-page/components/desktop/ManagerMode/AccountListItem/ManagedUserListItem.tsx index 0c89bca4ca3..546b5e7f5bc 100644 --- a/packages/web/src/pages/settings-page/components/desktop/ManagerMode/AccountListItem/ManagedUserListItem.tsx +++ b/packages/web/src/pages/settings-page/components/desktop/ManagerMode/AccountListItem/ManagedUserListItem.tsx @@ -3,7 +3,7 @@ import { useCallback, useContext, useEffect, useMemo } from 'react' import { useApproveManagedAccount, useRemoveManager } from '@audius/common/api' import { useAppContext } from '@audius/common/context' import { useAccountSwitcher, useIsManagedAccount } from '@audius/common/hooks' -import { ManagedUserMetadata, Name, Status } from '@audius/common/models' +import { ManagedUserMetadata, Name } from '@audius/common/models' import { accountSelectors, chatSelectors } from '@audius/common/store' import { route } from '@audius/common/utils' import { @@ -81,13 +81,19 @@ export const ManagedUserListItem = ({ }) }, [currentUserId, user.user_id, onRemoveManager]) - const [approveManagedAccount, { status: approveStatus }] = - useApproveManagedAccount() - const [rejectManagedAccount, { status: rejectStatus }] = useRemoveManager() + const { + mutate: approveManagedAccount, + isPending: approveIsPending, + isSuccess: approveIsSuccess, + isError: approveIsError + } = useApproveManagedAccount() + const { + mutate: rejectManagedAccount, + isPending: rejectIsPending, + isError: rejectIsError + } = useRemoveManager() const isPending = - grant?.is_approved == null || - approveStatus === Status.LOADING || - rejectStatus === Status.LOADING + grant?.is_approved == null || approveIsPending || rejectIsPending const { toast } = useContext(ToastContext) const { analytics: { track, make } @@ -124,18 +130,18 @@ export const ManagedUserListItem = ({ }, [rejectManagedAccount, currentUserId, user.user_id, toast, make, track]) useEffect(() => { - if (approveStatus === Status.SUCCESS) { + if (approveIsSuccess) { toast(messages.invitationAccepted) - } else if (approveStatus === Status.ERROR) { + } else if (approveIsError) { toast(sharedMessages.somethingWentWrong) } - }, [toast, approveStatus]) + }, [toast, approveIsSuccess, approveIsError]) useEffect(() => { - if (rejectStatus === Status.ERROR) { + if (rejectIsError) { toast(sharedMessages.somethingWentWrong) } - }, [toast, rejectStatus]) + }, [toast, rejectIsError]) const popupMenuItems = useMemo(() => { const items = [] @@ -227,8 +233,8 @@ export const ManagedUserListItem = ({ - {isLoading ? ( + {isFetching ? ( ({ @@ -80,21 +79,21 @@ export const AccountsManagingYouHomePage = ( /> ) : null} - {managersStatus === Status.SUCCESS && - (!managers || managers.length === 0) ? ( + {isSuccess && !isFetching && (!managers || managers.length === 0) ? ( {messages.noManagers} ) : null} - {managers?.map((data) => { - return ( - - ) - })} + {!isFetching && + managers?.map((data) => { + return ( + + ) + })} ) diff --git a/packages/web/src/pages/settings-page/components/desktop/ManagerMode/AccountsYouManageHomePage.tsx b/packages/web/src/pages/settings-page/components/desktop/ManagerMode/AccountsYouManageHomePage.tsx index e0ee6ce307d..d803d68eb6e 100644 --- a/packages/web/src/pages/settings-page/components/desktop/ManagerMode/AccountsYouManageHomePage.tsx +++ b/packages/web/src/pages/settings-page/components/desktop/ManagerMode/AccountsYouManageHomePage.tsx @@ -1,7 +1,6 @@ import { useCallback } from 'react' -import { useGetManagedAccounts } from '@audius/common/api' -import { Status } from '@audius/common/models' +import { useManagedAccounts } from '@audius/common/api' import { accountSelectors } from '@audius/common/store' import { Box, Divider, Flex, Text } from '@audius/harmony' @@ -25,15 +24,14 @@ export const AccountsYouManageHomePage = ({ setPageState }: AccountsYouManagePageProps) => { const userId = useSelector(getUserId) - const { data: managedAccounts, status } = useGetManagedAccounts( - { userId: userId! }, - // Always update managed accounts list when mounting this page - { disabled: userId == null, force: true } - ) - // Don't flash loading spinner if we are refreshing the cache - const isLoading = - status !== Status.SUCCESS && - (!managedAccounts || managedAccounts.length === 0) + const { + data: managedAccounts, + isFetching, + isSuccess + } = useManagedAccounts(userId, { + // Always refetch the data + staleTime: 0 + }) usePendingInviteValidator({ managedAccounts, @@ -55,7 +53,7 @@ export const AccountsYouManageHomePage = ({ {messages.takeControl}{' '} - {isLoading ? ( + {isFetching ? ( ({ @@ -65,7 +63,8 @@ export const AccountsYouManageHomePage = ({ /> ) : null} - {status === Status.SUCCESS && + {isSuccess && + !isFetching && (!managedAccounts || managedAccounts.length === 0) ? ( <> @@ -74,15 +73,16 @@ export const AccountsYouManageHomePage = ({ ) : null} - {managedAccounts?.map((data) => { - return ( - - ) - })} + {!isFetching && + managedAccounts?.map((data) => { + return ( + + ) + })} ) } diff --git a/packages/web/src/pages/settings-page/components/desktop/ManagerMode/ConfirmAccountManagerPage.tsx b/packages/web/src/pages/settings-page/components/desktop/ManagerMode/ConfirmAccountManagerPage.tsx index 073ee1e58f8..942c156ba51 100644 --- a/packages/web/src/pages/settings-page/components/desktop/ManagerMode/ConfirmAccountManagerPage.tsx +++ b/packages/web/src/pages/settings-page/components/desktop/ManagerMode/ConfirmAccountManagerPage.tsx @@ -1,7 +1,6 @@ import { useCallback, useEffect, useState } from 'react' import { useRequestAddManager } from '@audius/common/api' -import { Status } from '@audius/common/models' import { accountSelectors } from '@audius/common/store' import { Box, @@ -40,25 +39,28 @@ export const ConfirmAccountManagerPage = ( const userId = useSelector(getUserId) const [submitting, setIsSubmitting] = useState(false) const [error, setError] = useState(null) - const [requestAddManager, result] = useRequestAddManager() - const { status } = result + const { + mutate: requestAddManager, + isSuccess, + isError + } = useRequestAddManager() const manager = params?.user useEffect(() => { - if (status === Status.SUCCESS) { + if (isSuccess) { setPageState({ page: AccountsManagingYouPages.HOME, transitionDirection: 'forward' }) } - }, [setPageState, status]) + }, [isSuccess, setPageState]) useEffect(() => { - if (status === Status.ERROR) { + if (isError) { setError(messages.errorGeneral) setIsSubmitting(false) } - }, [status]) + }, [isError]) const handleConfirm = useCallback(() => { setIsSubmitting(true) diff --git a/packages/web/src/pages/settings-page/components/desktop/ManagerMode/FindAccountManagerPage.tsx b/packages/web/src/pages/settings-page/components/desktop/ManagerMode/FindAccountManagerPage.tsx index 1e63dc12744..22182a5b9e3 100644 --- a/packages/web/src/pages/settings-page/components/desktop/ManagerMode/FindAccountManagerPage.tsx +++ b/packages/web/src/pages/settings-page/components/desktop/ManagerMode/FindAccountManagerPage.tsx @@ -1,6 +1,6 @@ import { useCallback, useMemo, useState } from 'react' -import { useGetManagers } from '@audius/common/api' +import { useManagers } from '@audius/common/api' import { User } from '@audius/common/models' import { accountSelectors } from '@audius/common/store' import { Box, Flex, IconShieldUser, Text } from '@audius/harmony' @@ -25,10 +25,7 @@ export const FindAccountManagerPage = (props: FindAccountManagerPageProps) => { const { setPageState, params } = props const [query, setQuery] = useState(params?.query ?? '') const userId = useSelector(getUserId) - const { data: managers } = useGetManagers( - { userId: userId! }, - { disabled: userId == null } - ) + const { data: managers } = useManagers(userId) const excludedUserIds = useMemo(() => { const res: number[] = managers?.map((m) => m.manager.user_id) ?? [] if (userId) { diff --git a/packages/web/src/pages/settings-page/components/desktop/ManagerMode/RemoveManagerConfirmationContent.tsx b/packages/web/src/pages/settings-page/components/desktop/ManagerMode/RemoveManagerConfirmationContent.tsx index 9d84c826342..7e6c7fdbf2e 100644 --- a/packages/web/src/pages/settings-page/components/desktop/ManagerMode/RemoveManagerConfirmationContent.tsx +++ b/packages/web/src/pages/settings-page/components/desktop/ManagerMode/RemoveManagerConfirmationContent.tsx @@ -3,7 +3,7 @@ import { useCallback, useContext, useEffect } from 'react' import { useGetCurrentWeb3User, useRemoveManager } from '@audius/common/api' import { useAppContext } from '@audius/common/context' import { useAccountSwitcher } from '@audius/common/hooks' -import { Name, Status } from '@audius/common/models' +import { Name } from '@audius/common/models' import { accountSelectors } from '@audius/common/store' import { Button, Flex, Text } from '@audius/harmony' import { useSelector } from 'react-redux' @@ -33,7 +33,12 @@ export const RemoveManagerConfirmationContent = ({ onSuccess, onCancel }: RemoveManagerConfirmationContentProps) => { - const [removeManager, result] = useRemoveManager() + const { + mutate: removeManager, + isPending, + isSuccess, + isError + } = useRemoveManager() const { data: currentWeb3User } = useGetCurrentWeb3User({}) const currentUserId = useSelector(accountSelectors.getUserId) const managerIsCurrentWeb3User = currentWeb3User?.user_id === managerUserId @@ -42,7 +47,6 @@ export const RemoveManagerConfirmationContent = ({ const { analytics: { track, make } } = useAppContext() - const { status } = result const handleDelete = useCallback(() => { if (!userId || !managerUserId) return @@ -56,28 +60,30 @@ export const RemoveManagerConfirmationContent = ({ }, [userId, managerUserId, removeManager, make, track]) useEffect(() => { - if (status === Status.SUCCESS) { + if (isSuccess) { onSuccess() // If we are currently switched into this user and removing ourselves // as manager, switch back to primary account if (currentUserId === userId && managerIsCurrentWeb3User) switchToWeb3User() - } else if (status === Status.ERROR) { - toast(sharedMessages.somethingWentWrong) } }, [ - status, - managerIsCurrentWeb3User, currentUserId, - userId, - toast, + managerIsCurrentWeb3User, switchToWeb3User, + userId, + isSuccess, onSuccess ]) - if (!managerUserId) return null + // error handling + useEffect(() => { + if (isError) { + toast(sharedMessages.somethingWentWrong) + } + }, [isError, toast]) - const isSubmitting = status !== Status.IDLE && status !== Status.ERROR + if (!managerUserId) return null return ( @@ -88,7 +94,7 @@ export const RemoveManagerConfirmationContent = ({ - {status !== Status.ERROR ? null : ( + {isError ? ( {messages.errorGeneral} - )} + ) : null} ) }