Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

.manageAccountContainer {
margin-top: var(--unit-5);
margin-bottom: var(--unit-4);
}

.manageBtns {
Expand Down
97 changes: 63 additions & 34 deletions protocol-dashboard/src/hooks/useConnectAudiusProfile.ts
Original file line number Diff line number Diff line change
@@ -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)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we maybe have a reject case here so the hook doesn't stall forever in the case where userHandle is null?

}

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.")
}
}

Expand All @@ -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 => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sneaky

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just double checking we can get back to a repeatable state if something fails/want to try again, if those are actual requirements

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes we can!

resolveUserHandle = resolve
})
window.addEventListener('message', receiveUserId, false)
const sdk = await audiusSdk()
sdk.oauth.login({
scope: 'write_once',
Expand All @@ -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(() => {
Expand Down
22 changes: 19 additions & 3 deletions protocol-dashboard/src/hooks/useDashboardWalletUsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<DashboardWalletUser[]> => {
Expand All @@ -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()
)
})
}
2 changes: 1 addition & 1 deletion protocol-dashboard/src/services/Audius/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const initAudiusSdk = ({
} = bootstrapConfig
const dnSelector = new DiscoveryNodeSelector({
bootstrapServices: discoveryNodes
// initialSelectedNode: 'https://discoveryprovider.staging.audius.co'
// initialSelectedNode: 'https://discoveryprovider4.staging.audius.co'
})
instance = sdk({
appName: 'Audius Protocol Dashboard',
Expand Down
5 changes: 2 additions & 3 deletions protocol-dashboard/src/services/Audius/setup.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -112,8 +111,8 @@ export async function setup(this: AudiusClient): Promise<void> {
})
// 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()
}
Expand Down
2 changes: 2 additions & 0 deletions protocol-dashboard/src/store/account/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 --------------------------------

Expand Down
7 changes: 6 additions & 1 deletion protocol-dashboard/src/store/account/slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type State = {
transactions?: Array<DelayedPendingTransaction>
}
error?: string
isAudiusProfileRefetchDisabled?: boolean
}

export const initialState: State = {
Expand Down Expand Up @@ -67,6 +68,9 @@ const slice = createSlice({
) => {
state.pendingTransactions.status = Status.Success
state.pendingTransactions.transactions = action.payload
},
disableAudiusProfileRefetch: state => {
state.isAudiusProfileRefetchDisabled = true
}
}
})
Expand All @@ -77,7 +81,8 @@ export const {
setPendingTransactionsLoading,
setPendingTransactions,
setPendingClaimLoading,
setPendingClaim
setPendingClaim,
disableAudiusProfileRefetch
} = slice.actions

export default slice.reducer
2 changes: 1 addition & 1 deletion protocol-dashboard/src/store/cache/user/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<AppState, Audius, AnyAction> = useDispatch()
Expand Down
3 changes: 0 additions & 3 deletions protocol-dashboard/src/utils/switchNetwork.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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') {
Expand Down