From 4f11cff9c283593f24f0268884a0fa7131ac1b3d Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Wed, 4 Feb 2026 17:34:26 -0800 Subject: [PATCH 1/4] Fix send flow connect --- .../src/api/tan-query/wallets/useSendCoins.ts | 2 +- .../send-tokens-modal/SendTokensInput.tsx | 14 ++-- .../send-tokens-modal/SendTokensModal.tsx | 67 ++++++++++++++++++- .../src/components/stat-banner/StatBanner.tsx | 35 ++++------ 4 files changed, 83 insertions(+), 35 deletions(-) diff --git a/packages/common/src/api/tan-query/wallets/useSendCoins.ts b/packages/common/src/api/tan-query/wallets/useSendCoins.ts index 8a380a0f5fe..0ac63b8b5f0 100644 --- a/packages/common/src/api/tan-query/wallets/useSendCoins.ts +++ b/packages/common/src/api/tan-query/wallets/useSendCoins.ts @@ -41,7 +41,7 @@ export const useSendCoins = ({ mint }: { mint: string }) => { const { data: coinBalance } = useCoinBalance({ mint, - includeExternalWallets: false, + includeExternalWallets: true, includeStaked: false }) diff --git a/packages/web/src/components/send-tokens-modal/SendTokensInput.tsx b/packages/web/src/components/send-tokens-modal/SendTokensInput.tsx index 8ab7a95e083..a961a48edf2 100644 --- a/packages/web/src/components/send-tokens-modal/SendTokensInput.tsx +++ b/packages/web/src/components/send-tokens-modal/SendTokensInput.tsx @@ -65,9 +65,7 @@ const messages = { amountRequired: 'Amount is required', amountTooLow: 'Amount must be at least $0.50', walletAddress: 'Wallet Address', - userRequired: 'Please select a user', - userNoWallet: - 'This user does not have a wallet address set up. Please send to a different user or use a wallet address instead.' + userRequired: 'Please select a user' } const { TERMS_OF_SERVICE } = route @@ -78,7 +76,6 @@ type ValidationError = | 'AMOUNT_REQUIRED' | 'AMOUNT_TOO_LOW' | 'USER_REQUIRED' - | 'USER_NO_WALLET' const SendTokensInput = ({ mint: initialMint, @@ -235,10 +232,11 @@ const SendTokensInput = ({ if (!selectedUser) { setAddressError('USER_REQUIRED') isValid = false - } else if (!selectedUser.spl_wallet) { - setAddressError('USER_NO_WALLET') - isValid = false } + // Note: We don't validate wallet address here because: + // 1. The backend relay will create the user-bank account if needed + // 2. The backend will return an appropriate error if the user truly doesn't have an ETH address + // 3. This allows sending to users even if their wallet info isn't fully populated in the frontend } else { // Validate wallet address if (!destinationAddress) { @@ -284,8 +282,6 @@ const SendTokensInput = ({ return messages.amountTooLow case 'USER_REQUIRED': return messages.userRequired - case 'USER_NO_WALLET': - return messages.userNoWallet default: return '' } diff --git a/packages/web/src/components/send-tokens-modal/SendTokensModal.tsx b/packages/web/src/components/send-tokens-modal/SendTokensModal.tsx index c8ba3f10231..1050ea0270c 100644 --- a/packages/web/src/components/send-tokens-modal/SendTokensModal.tsx +++ b/packages/web/src/components/send-tokens-modal/SendTokensModal.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react' +import { useState, useEffect, useRef } from 'react' import { useSendCoins } from '@audius/common/api' import { walletMessages } from '@audius/common/messages' @@ -35,6 +35,59 @@ type SendTokensState = { const SendTokensModal = () => { const { isOpen, onClose: closeModal, data } = useSendTokensModal() const { mint, user: prePopulatedUser } = data ?? {} + const isAppKitModalOpenRef = useRef(false) + + // Monitor for AppKit modal opening/closing + useEffect(() => { + if (!isOpen) return + + const checkIfAppKitOpen = () => { + const backdrop = document.querySelector('[data-reown-backdrop]') + if (!backdrop) return false + const style = window.getComputedStyle(backdrop) + return ( + style.display !== 'none' && + style.visibility !== 'hidden' && + parseFloat(style.opacity) > 0 + ) + } + + // Use MutationObserver to detect when AppKit modal appears/disappears + const observer = new MutationObserver(() => { + isAppKitModalOpenRef.current = checkIfAppKitOpen() + }) + + observer.observe(document.body, { + childList: true, + subtree: true, + attributes: true, + attributeFilter: ['style', 'class'] + }) + + // Check initial state + isAppKitModalOpenRef.current = checkIfAppKitOpen() + + // Listen for clicks on connect links to prevent immediate close + const handleClick = (e: MouseEvent) => { + const target = e.target as HTMLElement + const connectLink = target.closest('a[href="#"]') + if (connectLink && connectLink.textContent?.includes('Connect')) { + // Prevent closing for 1 second to allow AppKit modal to open + isAppKitModalOpenRef.current = true + setTimeout(() => { + isAppKitModalOpenRef.current = checkIfAppKitOpen() + }, 1000) + } + } + + document.addEventListener('click', handleClick, true) + + return () => { + observer.disconnect() + document.removeEventListener('click', handleClick, true) + isAppKitModalOpenRef.current = false + } + }, [isOpen]) const [state, setState] = useState({ step: 'input', @@ -95,7 +148,9 @@ const SendTokensModal = () => { recipientWallet: state.destinationAddress as SolanaWalletAddress, amount: state.amount, // When sending to a user, pass their Ethereum address to derive user-bank ATA - recipientEthAddress: state.selectedUser?.erc_wallet + // Use erc_wallet first, fallback to wallet field + recipientEthAddress: + state.selectedUser?.erc_wallet ?? state.selectedUser?.wallet }) setState((prev) => ({ @@ -149,6 +204,10 @@ const SendTokensModal = () => { } const handleClose = () => { + // Don't close if AppKit modal is open (wallet connection in progress) + if (isAppKitModalOpenRef.current) { + return + } closeModal() const { user: prePopulatedUser } = data ?? {} setState({ @@ -172,7 +231,9 @@ const SendTokensModal = () => { onClose={handleClose} title={state.step === 'confirm' ? 'Confirm Details' : walletMessages.send} size='m' - dismissOnClickOutside={state.step === 'input'} + dismissOnClickOutside={ + state.step === 'input' && !isAppKitModalOpenRef.current + } showDismissButton={state.step === 'input'} > {state.step === 'input' ? ( diff --git a/packages/web/src/components/stat-banner/StatBanner.tsx b/packages/web/src/components/stat-banner/StatBanner.tsx index 563c6290ce0..a48a68010ab 100644 --- a/packages/web/src/components/stat-banner/StatBanner.tsx +++ b/packages/web/src/components/stat-banner/StatBanner.tsx @@ -15,8 +15,6 @@ import { IconMoneySend, PopupMenu, Button, - IconButton, - Tooltip, FollowButton, Flex, Skeleton, @@ -267,26 +265,19 @@ export const StatBanner = (props: StatsBannerProps) => { <> {mode === 'visitor' ? ( - - - { - openSendTokensModal({ - mint: env.WAUDIO_MINT_ADDRESS, - isOpen: true, - user: profileUser ?? undefined - }) - }} - /> - - +