|
| 1 | +import { |
| 2 | + DropdownMenuSub, |
| 3 | + DropdownMenuSubContent, |
| 4 | + DropdownMenuSubTrigger, |
| 5 | + DropdownMenuLabel, |
| 6 | + DropdownMenuSeparator, |
| 7 | + DropdownMenuRadioGroup, |
| 8 | + DropdownMenuRadioItem, |
| 9 | + DropdownMenuItem, |
| 10 | +} from "@/components/ui/dropdown-menu"; |
| 11 | + |
| 12 | +import type { UiWallet, UiWalletAccount } from "@wallet-standard/react"; |
| 13 | +import { uiWalletAccountsAreSame, useConnect, useDisconnect } from "@wallet-standard/react"; |
| 14 | +import { useCallback, useContext } from "react"; |
| 15 | + |
| 16 | +import { SelectedWalletAccountContext } from "@/context/SelectedWalletAccountContext"; |
| 17 | +import { WalletMenuItemContent } from "@/components/solana/WalletMenuItemContent"; |
| 18 | + |
| 19 | +type Props = Readonly<{ |
| 20 | + onAccountSelect(account: UiWalletAccount | undefined): void; |
| 21 | + onDisconnect(wallet: UiWallet): void; |
| 22 | + onError(error: unknown): void; |
| 23 | + wallet: UiWallet; |
| 24 | +}>; |
| 25 | + |
| 26 | +export function ConnectWalletMenuItem({ onAccountSelect, onDisconnect, onError, wallet }: Props) { |
| 27 | + const [isConnecting, connect] = useConnect(wallet); |
| 28 | + const [isDisconnecting, disconnect] = useDisconnect(wallet); |
| 29 | + const isPending = isConnecting || isDisconnecting; |
| 30 | + const isConnected = wallet.accounts.length > 0; |
| 31 | + const [selectedWalletAccount] = useContext(SelectedWalletAccountContext); |
| 32 | + const handleConnectClick = useCallback(async () => { |
| 33 | + try { |
| 34 | + const existingAccounts = [...wallet.accounts]; |
| 35 | + const nextAccounts = await connect(); |
| 36 | + // Try to choose the first never-before-seen account. |
| 37 | + for (const nextAccount of nextAccounts) { |
| 38 | + if (!existingAccounts.some((existingAccount) => uiWalletAccountsAreSame(nextAccount, existingAccount))) { |
| 39 | + onAccountSelect(nextAccount); |
| 40 | + return; |
| 41 | + } |
| 42 | + } |
| 43 | + // Failing that, choose the first account in the list. |
| 44 | + if (nextAccounts[0]) { |
| 45 | + onAccountSelect(nextAccounts[0]); |
| 46 | + } |
| 47 | + } catch (error) { |
| 48 | + onError(error); |
| 49 | + } |
| 50 | + }, [connect, onAccountSelect, onError, wallet.accounts]); |
| 51 | + return ( |
| 52 | + <DropdownMenuSub open={!isConnected ? false : undefined}> |
| 53 | + <DropdownMenuSubTrigger |
| 54 | + disabled={isPending} |
| 55 | + onClick={!isConnected ? handleConnectClick : undefined} |
| 56 | + > |
| 57 | + <WalletMenuItemContent wallet={wallet} /> |
| 58 | + </DropdownMenuSubTrigger> |
| 59 | + <DropdownMenuSubContent> |
| 60 | + <DropdownMenuLabel>Accounts</DropdownMenuLabel> |
| 61 | + <DropdownMenuRadioGroup value={selectedWalletAccount?.address}> |
| 62 | + {wallet.accounts.map((account) => ( |
| 63 | + <DropdownMenuRadioItem |
| 64 | + key={account.address} |
| 65 | + value={account.address} |
| 66 | + onSelect={() => { |
| 67 | + onAccountSelect(account); |
| 68 | + }} |
| 69 | + > |
| 70 | + {account.address.slice(0, 8)}… |
| 71 | + </DropdownMenuRadioItem> |
| 72 | + ))} |
| 73 | + </DropdownMenuRadioGroup> |
| 74 | + <DropdownMenuSeparator /> |
| 75 | + <DropdownMenuItem |
| 76 | + onSelect={async (event) => { |
| 77 | + event.preventDefault(); |
| 78 | + await handleConnectClick(); |
| 79 | + }} |
| 80 | + > |
| 81 | + Connect More |
| 82 | + </DropdownMenuItem> |
| 83 | + <DropdownMenuItem |
| 84 | + color="red" |
| 85 | + onSelect={async (event) => { |
| 86 | + event.preventDefault(); |
| 87 | + try { |
| 88 | + await disconnect(); |
| 89 | + onDisconnect(wallet); |
| 90 | + } catch (error) { |
| 91 | + onError(error); |
| 92 | + } |
| 93 | + }} |
| 94 | + > |
| 95 | + Disconnect |
| 96 | + </DropdownMenuItem> |
| 97 | + </DropdownMenuSubContent> |
| 98 | + </DropdownMenuSub> |
| 99 | + ); |
| 100 | +} |
0 commit comments