From 4a234499e16b685c210b46e01c5e8eca4a7e5f05 Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Wed, 7 May 2025 09:17:08 -0700 Subject: [PATCH 1/3] [PE-6064] Migrate authorized apps to tan-query --- packages/common/src/api/authorizedApps.ts | 75 ------------------- packages/common/src/api/index.ts | 5 +- packages/common/src/api/reducer.ts | 2 - .../authorized-apps/useRemoveAuthorizedApp.ts | 21 +++--- .../AuthorizedApps/AuthorizedAppListItem.tsx | 4 +- .../AuthorizedAppsSettingsModal.tsx | 6 +- .../desktop/AuthorizedApps/YourAppsPage.tsx | 21 ++---- .../desktop/AuthorizedApps/types.ts | 6 +- 8 files changed, 27 insertions(+), 113 deletions(-) delete mode 100644 packages/common/src/api/authorizedApps.ts diff --git a/packages/common/src/api/authorizedApps.ts b/packages/common/src/api/authorizedApps.ts deleted file mode 100644 index abc15bc3aa5..00000000000 --- a/packages/common/src/api/authorizedApps.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { Id } from '@audius/sdk' - -import { createApi } from '~/audius-query' -import { ID } from '~/models/Identifiers' - -import { DeveloperApp } from './developerApps' - -type RemoveAuthorizedAppArgs = { - apiKey: string - userId: number -} - -const authorizedAppsApi = createApi({ - reducerPath: 'authorizedAppsApi', - endpoints: { - getAuthorizedApps: { - async fetch({ id }: { id: ID }, { audiusSdk }) { - const encodedUserId = Id.parse(id) - const sdk = await audiusSdk() - const { data = [] } = await sdk.users.getAuthorizedApps({ - id: encodedUserId - }) - - return { - apps: data.map( - ({ address, name, description, imageUrl }): DeveloperApp => ({ - name, - description, - imageUrl, - apiKey: address.slice(2) - }) - ) - } - }, - - options: { idArgKey: 'id' } - }, - removeAuthorizedApp: { - async fetch(args: RemoveAuthorizedAppArgs, { audiusSdk }) { - const { userId, apiKey } = args - const encodedUserId = Id.parse(userId) - const sdk = await audiusSdk() - - await sdk.grants.revokeGrant({ - userId: encodedUserId, - appApiKey: apiKey - }) - return {} - }, - options: { - type: 'mutation' - }, - async onQuerySuccess(_response, args, { dispatch }) { - const { userId, apiKey } = args - dispatch( - authorizedAppsApi.util.updateQueryData( - 'getAuthorizedApps', - { id: userId }, - (state) => { - const appIndex = state.apps.findIndex( - (app) => app.apiKey === apiKey - ) - state.apps.splice(appIndex, 1) - } - ) - ) - } - } - } -}) - -export const { useGetAuthorizedApps, useRemoveAuthorizedApp } = - authorizedAppsApi.hooks - -export const authorizedAppsApiReducer = authorizedAppsApi.reducer diff --git a/packages/common/src/api/index.ts b/packages/common/src/api/index.ts index 701539e7ef3..929ccfc29e5 100644 --- a/packages/common/src/api/index.ts +++ b/packages/common/src/api/index.ts @@ -1,7 +1,6 @@ // Audius query // TODO: migrate all of these to tan-query export * from './account' -export * from './authorizedApps' export * from './developerApps' export * from './library' export * from './signUp' @@ -130,3 +129,7 @@ export * from './tan-query/wallets/useUSDCBalance' // Saga fetch utils, remove when migration is complete export * from './tan-query/saga-utils' export * from './tan-query/utils/defaultConfig' + +// New authorized-apps exports +export * from './tan-query/authorized-apps/useAuthorizedApps' +export * from './tan-query/authorized-apps/useRemoveAuthorizedApp' diff --git a/packages/common/src/api/reducer.ts b/packages/common/src/api/reducer.ts index 7071f34371c..984c55aa3f6 100644 --- a/packages/common/src/api/reducer.ts +++ b/packages/common/src/api/reducer.ts @@ -1,7 +1,6 @@ import { combineReducers } from 'redux' import { accountApiReducer } from './account' -import { authorizedAppsApiReducer } from './authorizedApps' import { developerAppsApiReducer } from './developerApps' import { favoritesApiReducer } from './favorites' import { libraryApiReducer } from './library' @@ -12,7 +11,6 @@ import { userApiReducer } from './user' export default combineReducers({ accountApi: accountApiReducer, - authorizedAppsApi: authorizedAppsApiReducer, developerAppsApi: developerAppsApiReducer, favoritesApi: favoritesApiReducer, libraryApi: libraryApiReducer, diff --git a/packages/common/src/api/tan-query/authorized-apps/useRemoveAuthorizedApp.ts b/packages/common/src/api/tan-query/authorized-apps/useRemoveAuthorizedApp.ts index c008c46e3c0..2ce5a88e374 100644 --- a/packages/common/src/api/tan-query/authorized-apps/useRemoveAuthorizedApp.ts +++ b/packages/common/src/api/tan-query/authorized-apps/useRemoveAuthorizedApp.ts @@ -1,4 +1,4 @@ -import { Id } from '@audius/sdk' +import { AuthorizedApp, Id } from '@audius/sdk' import { useMutation, useQueryClient } from '@tanstack/react-query' import { cloneDeep } from 'lodash' @@ -8,16 +8,14 @@ import { ID } from '~/models' import { QUERY_KEYS } from '../queryKeys' import { QueryKey } from '../types' -import { DeveloperApp } from './developerApps' - export type UseRemoveAuthorizedAppArgs = { - apiKey: string + address: string userId: ID } export const getRemoveAuthorizedAppQueryKey = (userId: ID) => { return [QUERY_KEYS.authorizedApps, userId] as unknown as QueryKey< - DeveloperApp[] + AuthorizedApp[] > } @@ -27,24 +25,23 @@ export const useRemoveAuthorizedApp = () => { return useMutation({ mutationFn: async (args: UseRemoveAuthorizedAppArgs) => { - const { apiKey, userId } = args + const { address, userId } = args const sdk = await audiusSdk() await sdk.grants.revokeGrant({ userId: Id.parse(userId), - appApiKey: apiKey + appApiKey: address.slice(2) }) }, onMutate: (args) => { - const { apiKey, userId } = args + const { address: apiKey, userId } = args queryClient.invalidateQueries({ queryKey: getRemoveAuthorizedAppQueryKey(userId) }) - const previousApps: DeveloperApp[] | undefined = queryClient.getQueryData( - getRemoveAuthorizedAppQueryKey(userId) - ) + const previousApps: AuthorizedApp[] | undefined = + queryClient.getQueryData(getRemoveAuthorizedAppQueryKey(userId)) if (previousApps === undefined) { return { @@ -53,7 +50,7 @@ export const useRemoveAuthorizedApp = () => { } // Splice out the removed app - const appIndex = previousApps?.findIndex((app) => app.apiKey === apiKey) + const appIndex = previousApps?.findIndex((app) => app.address === apiKey) const newApps = cloneDeep(previousApps).splice(appIndex, 1) queryClient.setQueryData(getRemoveAuthorizedAppQueryKey(userId), newApps) diff --git a/packages/web/src/pages/settings-page/components/desktop/AuthorizedApps/AuthorizedAppListItem.tsx b/packages/web/src/pages/settings-page/components/desktop/AuthorizedApps/AuthorizedAppListItem.tsx index 3f356afc082..a22b056c016 100644 --- a/packages/web/src/pages/settings-page/components/desktop/AuthorizedApps/AuthorizedAppListItem.tsx +++ b/packages/web/src/pages/settings-page/components/desktop/AuthorizedApps/AuthorizedAppListItem.tsx @@ -1,6 +1,5 @@ import { useCallback, useMemo } from 'react' -import { DeveloperApp } from '@audius/common/api' import { IconButton, IconEmbed, @@ -10,6 +9,7 @@ import { PopupMenu, PopupMenuItem } from '@audius/harmony' +import { AuthorizedApp } from '@audius/sdk' import PreloadImage from 'components/preload-image/PreloadImage' @@ -24,7 +24,7 @@ const messages = { type AuthorizedAppListItemProps = Pick & { index: number - app: DeveloperApp + app: AuthorizedApp } export const AuthorizedAppListItem = (props: AuthorizedAppListItemProps) => { diff --git a/packages/web/src/pages/settings-page/components/desktop/AuthorizedApps/AuthorizedAppsSettingsModal.tsx b/packages/web/src/pages/settings-page/components/desktop/AuthorizedApps/AuthorizedAppsSettingsModal.tsx index aae374d478d..2fa7cd65ffe 100644 --- a/packages/web/src/pages/settings-page/components/desktop/AuthorizedApps/AuthorizedAppsSettingsModal.tsx +++ b/packages/web/src/pages/settings-page/components/desktop/AuthorizedApps/AuthorizedAppsSettingsModal.tsx @@ -1,6 +1,5 @@ import { useCallback, useEffect, useState } from 'react' -import { DeveloperApp } from '@audius/common/api' import { Modal, ModalContentPages, @@ -10,6 +9,7 @@ import { IconTrash, IconShieldCheck } from '@audius/harmony' +import { AuthorizedApp } from '@audius/sdk' import { AppDetailsPage } from './AppDetailsPage' import styles from './AuthorizedAppsSettingsModal.module.css' @@ -61,10 +61,10 @@ export const AuthorizedAppsSettingsModal = ( AuthorizedAppsPages.APP_DETAILS ) - const [currentPageParams, setCurrentPageParams] = useState() + const [currentPageParams, setCurrentPageParams] = useState() const handleSetPage = useCallback( - (page: AuthorizedAppsPages, params?: DeveloperApp) => { + (page: AuthorizedAppsPages, params?: AuthorizedApp) => { setCurrentPage(page) if (params) { setCurrentPageParams(params) diff --git a/packages/web/src/pages/settings-page/components/desktop/AuthorizedApps/YourAppsPage.tsx b/packages/web/src/pages/settings-page/components/desktop/AuthorizedApps/YourAppsPage.tsx index 33adaf2eee6..ec42e7f1e80 100644 --- a/packages/web/src/pages/settings-page/components/desktop/AuthorizedApps/YourAppsPage.tsx +++ b/packages/web/src/pages/settings-page/components/desktop/AuthorizedApps/YourAppsPage.tsx @@ -1,18 +1,13 @@ -import { useGetAuthorizedApps } from '@audius/common/api' -import { Status } from '@audius/common/models' -import { accountSelectors } from '@audius/common/store' +import { useAuthorizedApps } from '@audius/common/api' import { ModalContentText } from '@audius/harmony' import { Divider } from 'components/divider' import LoadingSpinner from 'components/loading-spinner/LoadingSpinner' -import { useSelector } from 'utils/reducer' import { AuthorizedAppListItem } from './AuthorizedAppListItem' import styles from './YourAppsPage.module.css' import { AuthorizedAppPageProps } from './types' -const { getUserId } = accountSelectors - const messages = { description: 'Manage the 3rd party apps that are allowed to modify your account.', @@ -24,11 +19,7 @@ type YourAppsPageProps = AuthorizedAppPageProps export const YourAppsPage = (props: YourAppsPageProps) => { const { setPage } = props - const userId = useSelector(getUserId) - const { data, status } = useGetAuthorizedApps( - { id: userId as number }, - { disabled: !userId } - ) + const { data: apps, isPending } = useAuthorizedApps() return (
@@ -38,15 +29,15 @@ export const YourAppsPage = (props: YourAppsPageProps) => {

{messages.yourAppsTitle}

- {status !== Status.SUCCESS ? ( + {isPending ? ( - ) : data?.apps.length === 0 ? ( + ) : apps?.length === 0 ? (

{messages.noApps}

) : (
    - {data?.apps.map((app, index) => ( + {apps?.map((app, index) => ( void - params?: DeveloperApp + setPage: (page: AuthorizedAppsPages, params?: AuthorizedApp) => void + params?: AuthorizedApp } From 465d3c018b061acbd7a37ca31b1ce5488a7052dc Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Wed, 7 May 2025 15:03:01 -0700 Subject: [PATCH 2/3] Finalize --- packages/common/src/api/authorizedApps.ts | 75 ------------------- packages/common/src/api/index.ts | 2 +- .../authorized-apps/useRemoveAuthorizedApp.ts | 1 - .../developer-apps/useAddDeveloperApp.ts | 2 +- .../developer-apps/useDeleteDeveloperApp.ts | 2 +- .../developer-apps/useDeveloperApps.ts | 3 +- .../developer-apps/useEditDeveloperApp.ts | 2 +- .../developerApps.ts | 16 ++-- 8 files changed, 13 insertions(+), 90 deletions(-) delete mode 100644 packages/common/src/api/authorizedApps.ts rename packages/common/src/{api/tan-query/developer-apps => schemas}/developerApps.ts (100%) diff --git a/packages/common/src/api/authorizedApps.ts b/packages/common/src/api/authorizedApps.ts deleted file mode 100644 index ca897ace7cb..00000000000 --- a/packages/common/src/api/authorizedApps.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { Id } from '@audius/sdk' - -import { createApi } from '~/audius-query' -import { ID } from '~/models/Identifiers' - -import { DeveloperApp } from './tan-query/developer-apps/developerApps' - -type RemoveAuthorizedAppArgs = { - apiKey: string - userId: number -} - -const authorizedAppsApi = createApi({ - reducerPath: 'authorizedAppsApi', - endpoints: { - getAuthorizedApps: { - async fetch({ id }: { id: ID }, { audiusSdk }) { - const encodedUserId = Id.parse(id) - const sdk = await audiusSdk() - const { data = [] } = await sdk.users.getAuthorizedApps({ - id: encodedUserId - }) - - return { - apps: data.map( - ({ address, name, description, imageUrl }): DeveloperApp => ({ - name, - description, - imageUrl, - apiKey: address.slice(2) - }) - ) - } - }, - - options: { idArgKey: 'id' } - }, - removeAuthorizedApp: { - async fetch(args: RemoveAuthorizedAppArgs, { audiusSdk }) { - const { userId, apiKey } = args - const encodedUserId = Id.parse(userId) - const sdk = await audiusSdk() - - await sdk.grants.revokeGrant({ - userId: encodedUserId, - appApiKey: apiKey - }) - return {} - }, - options: { - type: 'mutation' - }, - async onQuerySuccess(_response, args, { dispatch }) { - const { userId, apiKey } = args - dispatch( - authorizedAppsApi.util.updateQueryData( - 'getAuthorizedApps', - { id: userId }, - (state) => { - const appIndex = state.apps.findIndex( - (app) => app.apiKey === apiKey - ) - state.apps.splice(appIndex, 1) - } - ) - ) - } - } - } -}) - -export const { useGetAuthorizedApps, useRemoveAuthorizedApp } = - authorizedAppsApi.hooks - -export const authorizedAppsApiReducer = authorizedAppsApi.reducer diff --git a/packages/common/src/api/index.ts b/packages/common/src/api/index.ts index 69787169996..fda40adff98 100644 --- a/packages/common/src/api/index.ts +++ b/packages/common/src/api/index.ts @@ -24,7 +24,7 @@ export * from './tan-query/collection/useCollectionTracksWithUid' export * from './tan-query/collection/useFeaturedPlaylists' // Developer Apps -export * from './tan-query/developer-apps/developerApps' +export * from '../schemas/developerApps' export * from './tan-query/developer-apps/useDeveloperApps' export * from './tan-query/developer-apps/useAddDeveloperApp' export * from './tan-query/developer-apps/useEditDeveloperApp' diff --git a/packages/common/src/api/tan-query/authorized-apps/useRemoveAuthorizedApp.ts b/packages/common/src/api/tan-query/authorized-apps/useRemoveAuthorizedApp.ts index 7b17aac3956..2ce5a88e374 100644 --- a/packages/common/src/api/tan-query/authorized-apps/useRemoveAuthorizedApp.ts +++ b/packages/common/src/api/tan-query/authorized-apps/useRemoveAuthorizedApp.ts @@ -5,7 +5,6 @@ import { cloneDeep } from 'lodash' import { useAudiusQueryContext } from '~/audius-query/AudiusQueryContext' import { ID } from '~/models' -import { DeveloperApp } from '../developer-apps/developerApps' import { QUERY_KEYS } from '../queryKeys' import { QueryKey } from '../types' diff --git a/packages/common/src/api/tan-query/developer-apps/useAddDeveloperApp.ts b/packages/common/src/api/tan-query/developer-apps/useAddDeveloperApp.ts index c191be0bcda..8e2b2c25a02 100644 --- a/packages/common/src/api/tan-query/developer-apps/useAddDeveloperApp.ts +++ b/packages/common/src/api/tan-query/developer-apps/useAddDeveloperApp.ts @@ -2,8 +2,8 @@ import { Id } from '@audius/sdk' import { useMutation, useQueryClient } from '@tanstack/react-query' import { useAudiusQueryContext } from '~/audius-query' +import { DeveloperApp, NewAppPayload } from '~/schemas/developerApps' -import { DeveloperApp, NewAppPayload } from './developerApps' import { getDeveloperAppsQueryKey } from './useDeveloperApps' export const useAddDeveloperApp = () => { diff --git a/packages/common/src/api/tan-query/developer-apps/useDeleteDeveloperApp.ts b/packages/common/src/api/tan-query/developer-apps/useDeleteDeveloperApp.ts index 537b032adde..99cdbc07e77 100644 --- a/packages/common/src/api/tan-query/developer-apps/useDeleteDeveloperApp.ts +++ b/packages/common/src/api/tan-query/developer-apps/useDeleteDeveloperApp.ts @@ -2,8 +2,8 @@ import { Id } from '@audius/sdk' import { useMutation, useQueryClient } from '@tanstack/react-query' import { useAudiusQueryContext } from '~/audius-query' +import { DeleteDeveloperAppArgs, DeveloperApp } from '~/schemas/developerApps' -import { DeleteDeveloperAppArgs, DeveloperApp } from './developerApps' import { getDeveloperAppsQueryKey } from './useDeveloperApps' export const useDeleteDeveloperApp = () => { diff --git a/packages/common/src/api/tan-query/developer-apps/useDeveloperApps.ts b/packages/common/src/api/tan-query/developer-apps/useDeveloperApps.ts index c63f4a57441..8f1fee7c7e3 100644 --- a/packages/common/src/api/tan-query/developer-apps/useDeveloperApps.ts +++ b/packages/common/src/api/tan-query/developer-apps/useDeveloperApps.ts @@ -3,13 +3,12 @@ import { useQuery } from '@tanstack/react-query' import { useAudiusQueryContext } from '~/audius-query' import { ID } from '~/models' +import { DeveloperApp } from '~/schemas/developerApps' import { Nullable } from '~/utils/typeUtils' import { QUERY_KEYS } from '../queryKeys' import { QueryKey, SelectableQueryOptions } from '../types' -import { DeveloperApp } from './developerApps' - export const getDeveloperAppsQueryKey = (userId: Nullable) => { return [QUERY_KEYS.developerApps, userId] as unknown as QueryKey<{ apps: DeveloperApp[] diff --git a/packages/common/src/api/tan-query/developer-apps/useEditDeveloperApp.ts b/packages/common/src/api/tan-query/developer-apps/useEditDeveloperApp.ts index 89aa7fbd57d..3fd93ed998b 100644 --- a/packages/common/src/api/tan-query/developer-apps/useEditDeveloperApp.ts +++ b/packages/common/src/api/tan-query/developer-apps/useEditDeveloperApp.ts @@ -2,8 +2,8 @@ import { Id } from '@audius/sdk' import { useMutation, useQueryClient } from '@tanstack/react-query' import { useAudiusQueryContext } from '~/audius-query' +import { DeveloperApp, EditAppPayload } from '~/schemas/developerApps' -import { DeveloperApp, EditAppPayload } from './developerApps' import { getDeveloperAppsQueryKey } from './useDeveloperApps' export const useEditDeveloperApp = () => { diff --git a/packages/common/src/api/tan-query/developer-apps/developerApps.ts b/packages/common/src/schemas/developerApps.ts similarity index 100% rename from packages/common/src/api/tan-query/developer-apps/developerApps.ts rename to packages/common/src/schemas/developerApps.ts index 9da726f68b8..331d289f483 100644 --- a/packages/common/src/api/tan-query/developer-apps/developerApps.ts +++ b/packages/common/src/schemas/developerApps.ts @@ -9,6 +9,14 @@ const messages = { invalidUrl: 'Invalid URL' } +export type DeveloperApp = { + name: string + description?: string + imageUrl?: string + apiKey: string + apiSecret?: string +} + export const developerAppSchema = z.object({ userId: z.number(), name: z.string().max(DEVELOPER_APP_NAME_MAX_LENGTH), @@ -38,14 +46,6 @@ export const developerAppEditSchema = z.object({ description: z.string().max(DEVELOPER_APP_DESCRIPTION_MAX_LENGTH).optional() }) -export type DeveloperApp = { - name: string - description?: string - imageUrl?: string - apiKey: string - apiSecret?: string -} - export type NewAppPayload = Omit & { userId: number } From 8c15300a43d40f1b8518ca981f177ee606d9c429 Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Wed, 7 May 2025 15:48:25 -0700 Subject: [PATCH 3/3] Finalize --- .../authorized-apps/useRemoveAuthorizedApp.ts | 35 +++++++++----- .../developer-apps/useAddDeveloperApp.ts | 26 ++++++---- .../developer-apps/useDeleteDeveloperApp.ts | 31 ++++++------ .../developer-apps/useDeveloperApps.ts | 28 +++++------ .../developer-apps/useEditDeveloperApp.ts | 29 +++++------ packages/common/src/schemas/developerApps.ts | 15 +----- .../RemoveAppConfirmationPage.tsx | 48 ++++++++++--------- .../DeveloperApps/CreateNewAppPage.tsx | 5 -- .../DeleteAppConfirmationPage.tsx | 22 +++------ .../DeveloperAppsSettingsModal.module.css | 5 -- .../DeveloperAppsSettingsModal.tsx | 7 ++- .../desktop/DeveloperApps/YourAppsPage.tsx | 8 ++-- 12 files changed, 126 insertions(+), 133 deletions(-) delete mode 100644 packages/web/src/pages/settings-page/components/desktop/DeveloperApps/DeveloperAppsSettingsModal.module.css diff --git a/packages/common/src/api/tan-query/authorized-apps/useRemoveAuthorizedApp.ts b/packages/common/src/api/tan-query/authorized-apps/useRemoveAuthorizedApp.ts index 2ce5a88e374..7242a98fd47 100644 --- a/packages/common/src/api/tan-query/authorized-apps/useRemoveAuthorizedApp.ts +++ b/packages/common/src/api/tan-query/authorized-apps/useRemoveAuthorizedApp.ts @@ -7,6 +7,7 @@ import { ID } from '~/models' import { QUERY_KEYS } from '../queryKeys' import { QueryKey } from '../types' +import { useCurrentUserId } from '../users/account/useCurrentUserId' export type UseRemoveAuthorizedAppArgs = { address: string @@ -22,26 +23,30 @@ export const getRemoveAuthorizedAppQueryKey = (userId: ID) => { export const useRemoveAuthorizedApp = () => { const { audiusSdk } = useAudiusQueryContext() const queryClient = useQueryClient() + const { data: currentUserId } = useCurrentUserId() return useMutation({ - mutationFn: async (args: UseRemoveAuthorizedAppArgs) => { - const { address, userId } = args + mutationFn: async (address: string) => { + if (!currentUserId) { + throw new Error('No current user ID') + } const sdk = await audiusSdk() await sdk.grants.revokeGrant({ - userId: Id.parse(userId), + userId: Id.parse(currentUserId), appApiKey: address.slice(2) }) }, - onMutate: (args) => { - const { address: apiKey, userId } = args - + onMutate: (address) => { + if (!currentUserId) { + throw new Error('No current user ID') + } queryClient.invalidateQueries({ - queryKey: getRemoveAuthorizedAppQueryKey(userId) + queryKey: getRemoveAuthorizedAppQueryKey(currentUserId) }) const previousApps: AuthorizedApp[] | undefined = - queryClient.getQueryData(getRemoveAuthorizedAppQueryKey(userId)) + queryClient.getQueryData(getRemoveAuthorizedAppQueryKey(currentUserId)) if (previousApps === undefined) { return { @@ -50,17 +55,23 @@ export const useRemoveAuthorizedApp = () => { } // Splice out the removed app - const appIndex = previousApps?.findIndex((app) => app.address === apiKey) + const appIndex = previousApps?.findIndex((app) => app.address === address) const newApps = cloneDeep(previousApps).splice(appIndex, 1) - queryClient.setQueryData(getRemoveAuthorizedAppQueryKey(userId), newApps) + queryClient.setQueryData( + getRemoveAuthorizedAppQueryKey(currentUserId), + newApps + ) // Return context with the previous apps return { previousApps } }, - onError: (_error, args, context) => { + onError: (_error, _address, context) => { + if (!currentUserId) { + throw new Error('No current user ID') + } queryClient.setQueryData( - getRemoveAuthorizedAppQueryKey(args.userId), + getRemoveAuthorizedAppQueryKey(currentUserId), context?.previousApps ) } diff --git a/packages/common/src/api/tan-query/developer-apps/useAddDeveloperApp.ts b/packages/common/src/api/tan-query/developer-apps/useAddDeveloperApp.ts index 8e2b2c25a02..b9c1f6494bd 100644 --- a/packages/common/src/api/tan-query/developer-apps/useAddDeveloperApp.ts +++ b/packages/common/src/api/tan-query/developer-apps/useAddDeveloperApp.ts @@ -4,16 +4,22 @@ import { useMutation, useQueryClient } from '@tanstack/react-query' import { useAudiusQueryContext } from '~/audius-query' import { DeveloperApp, NewAppPayload } from '~/schemas/developerApps' +import { useCurrentUserId } from '../users/account/useCurrentUserId' + import { getDeveloperAppsQueryKey } from './useDeveloperApps' export const useAddDeveloperApp = () => { const { audiusSdk } = useAudiusQueryContext() const queryClient = useQueryClient() + const { data: currentUserId } = useCurrentUserId() return useMutation({ mutationFn: async (newApp: NewAppPayload) => { - const { name, description, imageUrl, userId } = newApp - const encodedUserId = Id.parse(userId) + if (!currentUserId) { + throw new Error('No current user ID') + } + const { name, description, imageUrl } = newApp + const encodedUserId = Id.parse(currentUserId) const sdk = await audiusSdk() const { apiKey, apiSecret } = await sdk.developerApps.createDeveloperApp({ @@ -30,17 +36,17 @@ export const useAddDeveloperApp = () => { return { name, description, imageUrl, apiKey, apiSecret } }, - onSuccess: (newApp: DeveloperApp, newAppArgs: NewAppPayload) => { - const { userId } = newAppArgs + onSuccess: (newApp: DeveloperApp) => { + if (!currentUserId) { + throw new Error('No current user ID') + } const { apiSecret: apiSecretIgnored, ...restNewApp } = newApp queryClient.setQueryData( - getDeveloperAppsQueryKey(userId), - (oldData: { apps: DeveloperApp[] } | undefined) => { - if (!oldData) return { apps: [restNewApp] } - return { - apps: [...oldData.apps, restNewApp] - } + getDeveloperAppsQueryKey(currentUserId), + (oldData: DeveloperApp[] | undefined) => { + if (!oldData) return [restNewApp] + return [...oldData, restNewApp] } ) } diff --git a/packages/common/src/api/tan-query/developer-apps/useDeleteDeveloperApp.ts b/packages/common/src/api/tan-query/developer-apps/useDeleteDeveloperApp.ts index 99cdbc07e77..dc1844cccc0 100644 --- a/packages/common/src/api/tan-query/developer-apps/useDeleteDeveloperApp.ts +++ b/packages/common/src/api/tan-query/developer-apps/useDeleteDeveloperApp.ts @@ -2,36 +2,39 @@ import { Id } from '@audius/sdk' import { useMutation, useQueryClient } from '@tanstack/react-query' import { useAudiusQueryContext } from '~/audius-query' -import { DeleteDeveloperAppArgs, DeveloperApp } from '~/schemas/developerApps' +import { DeveloperApp } from '~/schemas/developerApps' + +import { useCurrentUserId } from '../users/account/useCurrentUserId' import { getDeveloperAppsQueryKey } from './useDeveloperApps' export const useDeleteDeveloperApp = () => { const { audiusSdk } = useAudiusQueryContext() const queryClient = useQueryClient() + const { data: currentUserId } = useCurrentUserId() return useMutation({ - mutationFn: async (args: DeleteDeveloperAppArgs) => { - const { userId, apiKey } = args - const encodedUserId = Id.parse(userId) + mutationFn: async (apiKey: string) => { + if (!currentUserId) { + throw new Error('No current user ID') + } const sdk = await audiusSdk() await sdk.developerApps.deleteDeveloperApp({ - userId: encodedUserId, + userId: Id.parse(currentUserId), appApiKey: apiKey }) return {} }, - onSuccess: (_response, args) => { - const { userId, apiKey } = args - + onSuccess: (_response, apiKey) => { + if (!currentUserId) { + throw new Error('No current user ID') + } queryClient.setQueryData( - getDeveloperAppsQueryKey(userId), - (oldData: { apps: DeveloperApp[] } | undefined) => { - if (!oldData) return { apps: [] } - return { - apps: oldData.apps.filter((app) => app.apiKey !== apiKey) - } + getDeveloperAppsQueryKey(currentUserId), + (oldData: DeveloperApp[] | undefined) => { + if (!oldData) return [] + return oldData.filter((app) => app.apiKey !== apiKey) } ) } diff --git a/packages/common/src/api/tan-query/developer-apps/useDeveloperApps.ts b/packages/common/src/api/tan-query/developer-apps/useDeveloperApps.ts index 8f1fee7c7e3..78931094a63 100644 --- a/packages/common/src/api/tan-query/developer-apps/useDeveloperApps.ts +++ b/packages/common/src/api/tan-query/developer-apps/useDeveloperApps.ts @@ -10,14 +10,14 @@ import { QUERY_KEYS } from '../queryKeys' import { QueryKey, SelectableQueryOptions } from '../types' export const getDeveloperAppsQueryKey = (userId: Nullable) => { - return [QUERY_KEYS.developerApps, userId] as unknown as QueryKey<{ - apps: DeveloperApp[] - }> + return [QUERY_KEYS.developerApps, userId] as unknown as QueryKey< + DeveloperApp[] + > } -export const useDeveloperApps = ( +export const useDeveloperApps = ( userId: Nullable, - options?: SelectableQueryOptions<{ apps: DeveloperApp[] }, TResult> + options?: SelectableQueryOptions ) => { const { audiusSdk } = useAudiusQueryContext() @@ -29,16 +29,14 @@ export const useDeveloperApps = ( id: Id.parse(userId) }) - return { - apps: data.map( - ({ address, name, description, imageUrl }): DeveloperApp => ({ - name, - description, - imageUrl, - apiKey: address.slice(2) - }) - ) - } + return data.map( + ({ address, name, description, imageUrl }): DeveloperApp => ({ + name, + description, + imageUrl, + apiKey: address.slice(2) + }) + ) }, ...options, enabled: options?.enabled !== false && !!userId diff --git a/packages/common/src/api/tan-query/developer-apps/useEditDeveloperApp.ts b/packages/common/src/api/tan-query/developer-apps/useEditDeveloperApp.ts index 3fd93ed998b..e2c667278a4 100644 --- a/packages/common/src/api/tan-query/developer-apps/useEditDeveloperApp.ts +++ b/packages/common/src/api/tan-query/developer-apps/useEditDeveloperApp.ts @@ -4,16 +4,21 @@ import { useMutation, useQueryClient } from '@tanstack/react-query' import { useAudiusQueryContext } from '~/audius-query' import { DeveloperApp, EditAppPayload } from '~/schemas/developerApps' +import { useCurrentUserId } from '../users/account/useCurrentUserId' + import { getDeveloperAppsQueryKey } from './useDeveloperApps' export const useEditDeveloperApp = () => { const { audiusSdk } = useAudiusQueryContext() const queryClient = useQueryClient() + const { data: currentUserId } = useCurrentUserId() return useMutation({ mutationFn: async (editApp: EditAppPayload) => { - const { name, description, imageUrl, userId, apiKey } = editApp - const encodedUserId = Id.parse(userId) + if (!currentUserId) { + throw new Error('No current user ID') + } + const { name, description, imageUrl, apiKey } = editApp const sdk = await audiusSdk() await sdk.developerApps.updateDeveloperApp({ @@ -21,23 +26,19 @@ export const useEditDeveloperApp = () => { name, description, imageUrl, - userId: encodedUserId + userId: Id.parse(currentUserId) }) return { name, description, imageUrl, apiKey } }, - onSuccess: (editApp: DeveloperApp, editAppArgs: EditAppPayload) => { - const { userId } = editAppArgs - + onSuccess: (editApp: DeveloperApp) => { queryClient.setQueryData( - getDeveloperAppsQueryKey(userId), - (oldData: { apps: DeveloperApp[] } | undefined) => { - if (!oldData) return { apps: [editApp] } - return { - apps: oldData.apps.map((app) => - app.apiKey === editApp.apiKey ? editApp : app - ) - } + getDeveloperAppsQueryKey(currentUserId), + (oldData: DeveloperApp[] | undefined) => { + if (!oldData) return [editApp] + return oldData.map((app) => + app.apiKey === editApp.apiKey ? editApp : app + ) } ) } diff --git a/packages/common/src/schemas/developerApps.ts b/packages/common/src/schemas/developerApps.ts index 331d289f483..6976c8dda46 100644 --- a/packages/common/src/schemas/developerApps.ts +++ b/packages/common/src/schemas/developerApps.ts @@ -18,7 +18,6 @@ export type DeveloperApp = { } export const developerAppSchema = z.object({ - userId: z.number(), name: z.string().max(DEVELOPER_APP_NAME_MAX_LENGTH), imageUrl: z.optional( z @@ -32,7 +31,6 @@ export const developerAppSchema = z.object({ }) export const developerAppEditSchema = z.object({ - userId: z.number(), apiKey: z.string(), name: z.string().max(DEVELOPER_APP_NAME_MAX_LENGTH), imageUrl: z.optional( @@ -46,15 +44,6 @@ export const developerAppEditSchema = z.object({ description: z.string().max(DEVELOPER_APP_DESCRIPTION_MAX_LENGTH).optional() }) -export type NewAppPayload = Omit & { - userId: number -} - -export type EditAppPayload = Omit & { - userId: number -} +export type NewAppPayload = Omit -export type DeleteDeveloperAppArgs = { - apiKey: string - userId: number -} +export type EditAppPayload = Omit diff --git a/packages/web/src/pages/settings-page/components/desktop/AuthorizedApps/RemoveAppConfirmationPage.tsx b/packages/web/src/pages/settings-page/components/desktop/AuthorizedApps/RemoveAppConfirmationPage.tsx index 59410a907db..81a57a51940 100644 --- a/packages/web/src/pages/settings-page/components/desktop/AuthorizedApps/RemoveAppConfirmationPage.tsx +++ b/packages/web/src/pages/settings-page/components/desktop/AuthorizedApps/RemoveAppConfirmationPage.tsx @@ -1,7 +1,7 @@ import { useCallback, useEffect } from 'react' import { useRemoveAuthorizedApp } from '@audius/common/api' -import { Name, Status } from '@audius/common/models' +import { Name } from '@audius/common/models' import { accountSelectors } from '@audius/common/store' import { Button, ModalFooter } from '@audius/harmony' @@ -26,52 +26,56 @@ export const RemoveAppConfirmationPage = ( props: AuthorizedAppConfirmationPageProps ) => { const { params, setPage } = props - const [removeAuthorizedApp, result] = useRemoveAuthorizedApp() - const { status, errorMessage } = result + const { + mutate: removeAuthorizedApp, + error, + isPending, + isSuccess, + isError + } = useRemoveAuthorizedApp() + const errorMessage = error?.message const userId = useSelector(getUserId) const record = useRecord() - const apiKey = params?.apiKey + const address = params?.address + const apiKey = address?.slice(2) + const name = params?.name const handleCancel = useCallback(() => { setPage(AuthorizedAppsPages.YOUR_APPS) }, [setPage]) const handleRemove = useCallback(() => { - if (!userId || !apiKey) return - removeAuthorizedApp({ userId, apiKey }) - }, [userId, apiKey, removeAuthorizedApp]) + if (!userId || !address) return + removeAuthorizedApp(address) + }, [userId, address, removeAuthorizedApp]) useEffect(() => { - if (status === Status.SUCCESS) { + if (isSuccess) { setPage(AuthorizedAppsPages.YOUR_APPS) record( make(Name.AUTHORIZED_APP_REMOVE_SUCCESS, { - name: params?.name, - apiKey: params?.apiKey + name, + apiKey }) ) } - }, [status, setPage, record, params?.name, params?.apiKey]) + }, [isSuccess, setPage, record, name, address, apiKey]) useEffect(() => { - if (status === Status.ERROR) { + if (isError) { setPage(AuthorizedAppsPages.YOUR_APPS) record( make(Name.AUTHORIZED_APP_REMOVE_ERROR, { - name: params?.name, - apiKey: params?.apiKey, + name, + apiKey, error: errorMessage }) ) } - }, [status, setPage, record, params?.name, params?.apiKey, errorMessage]) + }, [isError, setPage, record, name, address, apiKey, errorMessage]) if (!params) return null - const { name } = params - - const isRemoving = status !== Status.IDLE - return (

    {name}

    @@ -80,7 +84,7 @@ export const RemoveAppConfirmationPage = (
    diff --git a/packages/web/src/pages/settings-page/components/desktop/DeveloperApps/CreateNewAppPage.tsx b/packages/web/src/pages/settings-page/components/desktop/DeveloperApps/CreateNewAppPage.tsx index cc19c107007..2e76f66d7d3 100644 --- a/packages/web/src/pages/settings-page/components/desktop/DeveloperApps/CreateNewAppPage.tsx +++ b/packages/web/src/pages/settings-page/components/desktop/DeveloperApps/CreateNewAppPage.tsx @@ -8,7 +8,6 @@ import { useAddDeveloperApp } from '@audius/common/api' import { Name } from '@audius/common/models' -import { accountSelectors } from '@audius/common/store' import { Button } from '@audius/harmony' import { Form, Formik } from 'formik' import { z } from 'zod' @@ -16,11 +15,9 @@ import { toFormikValidationSchema } from 'zod-formik-adapter' import { make, useRecord } from 'common/store/analytics/actions' import { TextAreaField, TextField } from 'components/form-fields' -import { useSelector } from 'utils/reducer' import styles from './CreateNewAppPage.module.css' import { CreateAppPageProps, CreateAppsPages } from './types' -const { getUserId } = accountSelectors type DeveloperAppValues = z.input @@ -38,7 +35,6 @@ type CreateNewAppPageProps = CreateAppPageProps export const CreateNewAppPage = (props: CreateNewAppPageProps) => { const { setPage } = props - const userId = useSelector(getUserId) as number const record = useRecord() const { data, isSuccess, isError, error, mutate, isPending } = @@ -83,7 +79,6 @@ export const CreateNewAppPage = (props: CreateNewAppPageProps) => { ) const initialValues: DeveloperAppValues = { - userId, name: '', description: '', // Undefined unless set to pass validation diff --git a/packages/web/src/pages/settings-page/components/desktop/DeveloperApps/DeleteAppConfirmationPage.tsx b/packages/web/src/pages/settings-page/components/desktop/DeveloperApps/DeleteAppConfirmationPage.tsx index 0d697d6273a..41745cecf40 100644 --- a/packages/web/src/pages/settings-page/components/desktop/DeveloperApps/DeleteAppConfirmationPage.tsx +++ b/packages/web/src/pages/settings-page/components/desktop/DeveloperApps/DeleteAppConfirmationPage.tsx @@ -2,17 +2,13 @@ import { useCallback, useEffect } from 'react' import { useDeleteDeveloperApp } from '@audius/common/api' import { Name } from '@audius/common/models' -import { accountSelectors } from '@audius/common/store' import { Button, ModalFooter } from '@audius/harmony' -import { useSelector } from 'common/hooks/useSelector' import { make, useRecord } from 'common/store/analytics/actions' import styles from './DeleteAppConfirmationPage.module.css' import { CreateAppPageProps, CreateAppsPages } from './types' -const { getUserId } = accountSelectors - const messages = { confirmation: 'Are you sure you want to delete this app? \n\n You will permanently lose access to any accounts that have authorized this app in the past.', @@ -29,30 +25,30 @@ export const DeleteAppConfirmationPage = ( const { params, setPage } = props const { isSuccess, isError, error, mutate, isPending } = useDeleteDeveloperApp() - const userId = useSelector(getUserId) const record = useRecord() const apiKey = params?.apiKey + const name = params?.name const handleCancel = useCallback(() => { setPage(CreateAppsPages.YOUR_APPS) }, [setPage]) const handleDelete = useCallback(() => { - if (!userId || !apiKey) return - mutate({ userId, apiKey }) - }, [userId, apiKey, mutate]) + if (!apiKey) return + mutate(apiKey) + }, [apiKey, mutate]) useEffect(() => { if (isSuccess) { setPage(CreateAppsPages.YOUR_APPS) record( make(Name.DEVELOPER_APP_DELETE_SUCCESS, { - name: params?.name, - apiKey: params?.apiKey + name, + apiKey }) ) } - }, [isSuccess, setPage, record, params?.name, params?.apiKey]) + }, [isSuccess, setPage, record, name, apiKey]) useEffect(() => { if (isError) { @@ -67,10 +63,6 @@ export const DeleteAppConfirmationPage = ( } }, [isError, setPage, record, params?.name, params?.apiKey, error?.message]) - if (!params) return null - - const { name } = params - return (

    {name}

    diff --git a/packages/web/src/pages/settings-page/components/desktop/DeveloperApps/DeveloperAppsSettingsModal.module.css b/packages/web/src/pages/settings-page/components/desktop/DeveloperApps/DeveloperAppsSettingsModal.module.css deleted file mode 100644 index 4c4bbc2b018..00000000000 --- a/packages/web/src/pages/settings-page/components/desktop/DeveloperApps/DeveloperAppsSettingsModal.module.css +++ /dev/null @@ -1,5 +0,0 @@ -.titleIcon { - height: var(--harmony-unit-6); - width: var(--harmony-unit-6); - margin-top: 2px; -} diff --git a/packages/web/src/pages/settings-page/components/desktop/DeveloperApps/DeveloperAppsSettingsModal.tsx b/packages/web/src/pages/settings-page/components/desktop/DeveloperApps/DeveloperAppsSettingsModal.tsx index 0c8451569c2..74aad9349c9 100644 --- a/packages/web/src/pages/settings-page/components/desktop/DeveloperApps/DeveloperAppsSettingsModal.tsx +++ b/packages/web/src/pages/settings-page/components/desktop/DeveloperApps/DeveloperAppsSettingsModal.tsx @@ -14,7 +14,6 @@ import { import { AppDetailsPage } from './AppDetailsPage' import { CreateNewAppPage } from './CreateNewAppPage' import { DeleteAppConfirmationPage } from './DeleteAppConfirmationPage' -import styles from './DeveloperAppsSettingsModal.module.css' import { EditAppPage } from './EditAppPage' import { YourAppsPage } from './YourAppsPage' import { CreateAppsPages } from './types' @@ -53,9 +52,9 @@ const getTitle = (currentPage: CreateAppsPages) => { const getTitleIcon = (currentPage: CreateAppsPages) => { switch (currentPage) { case CreateAppsPages.DELETE_APP: - return + return IconTrash default: - return + return IconEmbed } } @@ -89,7 +88,7 @@ export const DeveloperAppsSettingsModal = ( diff --git a/packages/web/src/pages/settings-page/components/desktop/DeveloperApps/YourAppsPage.tsx b/packages/web/src/pages/settings-page/components/desktop/DeveloperApps/YourAppsPage.tsx index 05899b00c50..8d0af4e584f 100644 --- a/packages/web/src/pages/settings-page/components/desktop/DeveloperApps/YourAppsPage.tsx +++ b/packages/web/src/pages/settings-page/components/desktop/DeveloperApps/YourAppsPage.tsx @@ -29,9 +29,9 @@ type YourAppsPageProps = CreateAppPageProps export const YourAppsPage = (props: YourAppsPageProps) => { const { setPage } = props const userId = useSelector(getUserId) - const { data, status } = useDeveloperApps(userId) + const { data: apps, status } = useDeveloperApps(userId) - const hasMaxAllowedApps = (data?.apps?.length ?? 0) >= maxAppsAllowed + const hasMaxAllowedApps = (apps?.length ?? 0) >= maxAppsAllowed let createAppButton = (