From e316bc8fae22a5e886c014830371c6121c23dd24 Mon Sep 17 00:00:00 2001 From: Nikki Kang Date: Fri, 12 Jan 2024 16:44:23 -0800 Subject: [PATCH 1/2] reset --- .../ManageService/ManageService.module.css | 1 + .../src/hooks/useConnectAudiusProfile.ts | 97 ++++++++++++------- .../src/hooks/useDashboardWalletUsers.ts | 22 ++++- protocol-dashboard/src/services/Audius/sdk.ts | 4 +- .../src/services/Audius/setup.ts | 5 +- protocol-dashboard/src/store/account/hooks.ts | 2 + protocol-dashboard/src/store/account/slice.ts | 7 +- .../src/store/cache/user/hooks.ts | 2 +- protocol-dashboard/src/utils/switchNetwork.ts | 3 - 9 files changed, 96 insertions(+), 47 deletions(-) diff --git a/protocol-dashboard/src/components/ManageService/ManageService.module.css b/protocol-dashboard/src/components/ManageService/ManageService.module.css index 4056e1a12bb..00b34e03fe8 100644 --- a/protocol-dashboard/src/components/ManageService/ManageService.module.css +++ b/protocol-dashboard/src/components/ManageService/ManageService.module.css @@ -27,6 +27,7 @@ .manageAccountContainer { margin-top: var(--unit-5); + margin-bottom: var(--unit-4); } .manageBtns { diff --git a/protocol-dashboard/src/hooks/useConnectAudiusProfile.ts b/protocol-dashboard/src/hooks/useConnectAudiusProfile.ts index f0f598e769d..f125774eaf7 100644 --- a/protocol-dashboard/src/hooks/useConnectAudiusProfile.ts +++ b/protocol-dashboard/src/hooks/useConnectAudiusProfile.ts @@ -1,49 +1,58 @@ +import { OAUTH_URL, DecodedUserToken } from '@audius/sdk' import { useQueryClient } from '@tanstack/react-query' import { getDashboardWalletUserQueryKey } from 'hooks/useDashboardWalletUsers' import { useEffect } from 'react' +import { useDispatch } from 'react-redux' import { audiusSdk } from 'services/Audius/sdk' -import { useUser } from 'store/cache/user/hooks' -import { - AUDIUS_NETWORK_ID, - ETH_NETWORK_ID, - disableRefreshAfterNetworkChange, - switchNetwork -} from 'utils/switchNetwork' +import { disableAudiusProfileRefetch } from 'store/account/slice' const env = import.meta.env.VITE_ENVIRONMENT +let resolveUserHandle = null +let receiveUserHandlePromise = null + +const receiveUserId = async (event: MessageEvent) => { + const sdk = await audiusSdk() + const oauthOrigin = new URL(OAUTH_URL[env]).origin + if ( + event.origin !== oauthOrigin || + event.source !== sdk.oauth.activePopupWindow || + !event.data.state + ) { + return + } + if (sdk.oauth.getCsrfToken() !== event.data.state) { + console.error('State mismatch.') + return + } + if (event.data.userHandle != null) { + resolveUserHandle(event.data.userHandle) + } +} + export const useConnectAudiusProfile = (wallet: string) => { - const { user } = useUser({ wallet }) const queryClient = useQueryClient() - - const handleSuccess = async profile => { + const dispatch = useDispatch() + const handleSuccess = async (profile: DecodedUserToken) => { + window.removeEventListener('message', receiveUserId) const sdk = await audiusSdk() - disableRefreshAfterNetworkChange.value = true - const switched = await switchNetwork(AUDIUS_NETWORK_ID) - if (switched) { - await sdk.dashboardWalletUsers.connectUserToDashboardWallet({ - wallet: user?.wallet ?? wallet, - userId: profile.userId, - userSignature: profile.txSignature - }) - // Optimistically set user - await queryClient.cancelQueries({ queryKey: ['todos'] }) - const audiusUser = await sdk.users.getUser({ id: profile.userId }) - queryClient.setQueryData(getDashboardWalletUserQueryKey(wallet), { - wallet, - user: audiusUser - }) - const switchedBack = await switchNetwork(ETH_NETWORK_ID) - if (switchedBack) { - window.ethereum.on('chainChanged', () => { - disableRefreshAfterNetworkChange.value = false + // Optimistically set user + await queryClient.cancelQueries({ + queryKey: getDashboardWalletUserQueryKey(wallet) + }) + dispatch(disableAudiusProfileRefetch()) + + try { + const audiusUser = await sdk.users.getUser({ id: profile.userId }) + if (audiusUser?.data) { + queryClient.setQueryData(getDashboardWalletUserQueryKey(wallet), { + wallet, + user: audiusUser.data }) - } else { - disableRefreshAfterNetworkChange.value = false } - } else { - disableRefreshAfterNetworkChange.value = false + } catch { + console.error("Couldn't fetch Audius profile data.") } } @@ -53,13 +62,18 @@ export const useConnectAudiusProfile = (wallet: string) => { env: env === 'production' ? 'production' : 'staging', successCallback: handleSuccess, errorCallback: (errorMessage: string) => { - // Error calllback + window.removeEventListener('message', receiveUserId) console.error(errorMessage) } }) } const loginWithAudius = async () => { + window.removeEventListener('message', receiveUserId) + receiveUserHandlePromise = new Promise(resolve => { + resolveUserHandle = resolve + }) + window.addEventListener('message', receiveUserId, false) const sdk = await audiusSdk() sdk.oauth.login({ scope: 'write_once', @@ -68,6 +82,21 @@ export const useConnectAudiusProfile = (wallet: string) => { wallet } }) + + // Leg 1: Receive Audius user id from OAuth popup + const userHandle = await receiveUserHandlePromise + // Sign wallet signature from EM transaction + const message = `Connecting Audius user @${userHandle} at ${Math.round( + new Date().getTime() / 1000 + )}` + const signature = await window.audiusLibs.web3Manager.sign(message) + + const walletSignature = { message, signature } + // Leg 2: Send wallet signature to OAuth popup + sdk.oauth.activePopupWindow.postMessage( + { state: sdk.oauth.getCsrfToken(), walletSignature }, + new URL(OAUTH_URL[env]).origin + ) } useEffect(() => { diff --git a/protocol-dashboard/src/hooks/useDashboardWalletUsers.ts b/protocol-dashboard/src/hooks/useDashboardWalletUsers.ts index 7087dad17c5..d9a1e94c093 100644 --- a/protocol-dashboard/src/hooks/useDashboardWalletUsers.ts +++ b/protocol-dashboard/src/hooks/useDashboardWalletUsers.ts @@ -2,6 +2,11 @@ import { useQuery } from '@tanstack/react-query' import { create, windowScheduler } from '@yornaath/batshit' import { audiusSdk } from 'services/Audius/sdk' import { DashboardWalletUser } from '@audius/sdk' +import { useSelector } from 'react-redux' +import { + getAccountWallet, + getIsAudiusProfileRefetchDisabled +} from 'store/account/hooks' const dashboardWalletUsersBatcher = create({ fetcher: async (wallets: string[]): Promise => { @@ -17,17 +22,28 @@ const dashboardWalletUsersBatcher = create({ scheduler: windowScheduler(10) }) -export const getDashboardWalletUserQueryKey = (wallet: string) => { - return ['dashboardWalletUsers', wallet] +export const getDashboardWalletUserQueryKey = ( + wallet: string | undefined | null +) => { + return ['dashboardWalletUsers', wallet?.toLowerCase()] } export const useDashboardWalletUser = (wallet: string) => { + const isAudiusProfileRefetchDisabled = useSelector( + getIsAudiusProfileRefetchDisabled + ) + const currentUserWallet = useSelector(getAccountWallet) return useQuery({ queryKey: getDashboardWalletUserQueryKey(wallet), queryFn: async () => { const res = await dashboardWalletUsersBatcher.fetch(wallet) return res ?? null }, - enabled: !!wallet + enabled: + !!wallet && + !( + isAudiusProfileRefetchDisabled && + wallet?.toLowerCase() === currentUserWallet?.toLowerCase() + ) }) } diff --git a/protocol-dashboard/src/services/Audius/sdk.ts b/protocol-dashboard/src/services/Audius/sdk.ts index 2297ae7506b..2a9d09f9ac4 100644 --- a/protocol-dashboard/src/services/Audius/sdk.ts +++ b/protocol-dashboard/src/services/Audius/sdk.ts @@ -35,8 +35,8 @@ const initAudiusSdk = ({ identityServiceUrl } = bootstrapConfig const dnSelector = new DiscoveryNodeSelector({ - bootstrapServices: discoveryNodes - // initialSelectedNode: 'https://discoveryprovider.staging.audius.co' + bootstrapServices: discoveryNodes, + initialSelectedNode: 'https://discoveryprovider4.staging.audius.co' }) instance = sdk({ appName: 'Audius Protocol Dashboard', diff --git a/protocol-dashboard/src/services/Audius/setup.ts b/protocol-dashboard/src/services/Audius/setup.ts index a1f4ea4b7ad..929f5c6ebaa 100644 --- a/protocol-dashboard/src/services/Audius/setup.ts +++ b/protocol-dashboard/src/services/Audius/setup.ts @@ -1,7 +1,6 @@ import { AudiusClient } from './AudiusClient' import { libs as AudiusLibs, Utils } from '@audius/sdk/dist/web-libs.js' import { initAudiusSdk } from './sdk' -import { disableRefreshAfterNetworkChange } from 'utils/switchNetwork' declare global { interface Window { @@ -112,8 +111,8 @@ export async function setup(this: AudiusClient): Promise { }) // Reload anytime the network changes window.ethereum.on('chainChanged', () => { - console.log('Chain change') - if (!willReload && !disableRefreshAfterNetworkChange.value) { + if (!willReload) { + console.log('Chain change') willReload = true window.location.reload() } diff --git a/protocol-dashboard/src/store/account/hooks.ts b/protocol-dashboard/src/store/account/hooks.ts index 9110d3b9e3b..4196270e2a7 100644 --- a/protocol-dashboard/src/store/account/hooks.ts +++ b/protocol-dashboard/src/store/account/hooks.ts @@ -28,6 +28,8 @@ export const getAccountStatus = (state: AppState) => state.account.status export const getPendingTransactions = (state: AppState) => state.account.pendingTransactions export const getPendingClaim = (state: AppState) => state.account.pendingClaim +export const getIsAudiusProfileRefetchDisabled = (state: AppState) => + state.account.isAudiusProfileRefetchDisabled // -------------------------------- Thunk Actions -------------------------------- diff --git a/protocol-dashboard/src/store/account/slice.ts b/protocol-dashboard/src/store/account/slice.ts index 694c0f3e077..bf303d1bdb9 100644 --- a/protocol-dashboard/src/store/account/slice.ts +++ b/protocol-dashboard/src/store/account/slice.ts @@ -15,6 +15,7 @@ export type State = { transactions?: Array } error?: string + isAudiusProfileRefetchDisabled?: boolean } export const initialState: State = { @@ -67,6 +68,9 @@ const slice = createSlice({ ) => { state.pendingTransactions.status = Status.Success state.pendingTransactions.transactions = action.payload + }, + disableAudiusProfileRefetch: state => { + state.isAudiusProfileRefetchDisabled = true } } }) @@ -77,7 +81,8 @@ export const { setPendingTransactionsLoading, setPendingTransactions, setPendingClaimLoading, - setPendingClaim + setPendingClaim, + disableAudiusProfileRefetch } = slice.actions export default slice.reducer diff --git a/protocol-dashboard/src/store/cache/user/hooks.ts b/protocol-dashboard/src/store/cache/user/hooks.ts index 0f79b873fcf..76d5a9e173c 100644 --- a/protocol-dashboard/src/store/cache/user/hooks.ts +++ b/protocol-dashboard/src/store/cache/user/hooks.ts @@ -466,7 +466,7 @@ export const useUserProfile = ({ wallet }: UseUserProfile) => { const image = status !== Status.Loading - ? audiusProfile?.profilePicture['_480x480'] ?? user.image + ? audiusProfile?.profilePicture?.['_480x480'] ?? user.image : undefined const dispatch: ThunkDispatch = useDispatch() diff --git a/protocol-dashboard/src/utils/switchNetwork.ts b/protocol-dashboard/src/utils/switchNetwork.ts index 3f91aa07ead..8e5f139f5ca 100644 --- a/protocol-dashboard/src/utils/switchNetwork.ts +++ b/protocol-dashboard/src/utils/switchNetwork.ts @@ -5,11 +5,8 @@ export const decimalNetworkIdToHexNetworkId = (id: string | number) => { } const MISSING_CHAIN_ERROR_CODE = 4902 -export const AUDIUS_NETWORK_ID = import.meta.env.VITE_AUDIUS_NETWORK_ID export const ETH_NETWORK_ID = import.meta.env.VITE_ETH_NETWORK_ID -export const disableRefreshAfterNetworkChange = { value: false } - export const switchNetwork = async (to: string) => { const chainId = decimalNetworkIdToHexNetworkId(to) if (typeof window.ethereum !== 'undefined') { From d046c110ab22427350f842587c825cee5529f9c2 Mon Sep 17 00:00:00 2001 From: Nikki Kang Date: Fri, 12 Jan 2024 16:46:45 -0800 Subject: [PATCH 2/2] reset --- protocol-dashboard/src/services/Audius/sdk.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protocol-dashboard/src/services/Audius/sdk.ts b/protocol-dashboard/src/services/Audius/sdk.ts index 2a9d09f9ac4..2c84bb0f0ea 100644 --- a/protocol-dashboard/src/services/Audius/sdk.ts +++ b/protocol-dashboard/src/services/Audius/sdk.ts @@ -35,8 +35,8 @@ const initAudiusSdk = ({ identityServiceUrl } = bootstrapConfig const dnSelector = new DiscoveryNodeSelector({ - bootstrapServices: discoveryNodes, - initialSelectedNode: 'https://discoveryprovider4.staging.audius.co' + bootstrapServices: discoveryNodes + // initialSelectedNode: 'https://discoveryprovider4.staging.audius.co' }) instance = sdk({ appName: 'Audius Protocol Dashboard',