-
Notifications
You must be signed in to change notification settings - Fork 259
feat: account linking #2423
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
feat: account linking #2423
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| import { type BaseConnectorConfig, type ChainNamespaceType, log, WALLET_CONNECTORS } from "@web3auth/no-modal"; | ||
| import { FormEvent, useCallback, useMemo, useState } from "react"; | ||
|
|
||
| import { useWidget } from "../../context/WidgetContext"; | ||
| import { ExternalButton } from "../../interfaces"; | ||
| import ConnectWalletChainFilter from "../ConnectWallet/ConnectWalletChainFilter"; | ||
| import ConnectWalletList from "../ConnectWallet/ConnectWalletList"; | ||
| import ConnectWalletSearch from "../ConnectWallet/ConnectWalletSearch"; | ||
|
|
||
| export interface LinkWalletProps { | ||
| allRegistryButtons: ExternalButton[]; | ||
| customConnectorButtons: ExternalButton[]; | ||
| connectorVisibilityMap: Record<string, boolean>; | ||
| externalWalletsConfig: Record<string, BaseConnectorConfig>; | ||
| } | ||
|
|
||
| function LinkWallet(props: LinkWalletProps) { | ||
| const { allRegistryButtons, customConnectorButtons, connectorVisibilityMap } = props; | ||
|
|
||
| const { isDark, uiConfig } = useWidget(); | ||
| const { walletRegistry } = uiConfig; | ||
|
|
||
| const [walletSearch, setWalletSearch] = useState(""); | ||
| const [selectedChain, setSelectedChain] = useState("all"); | ||
| const [isShowAllWallets, setIsShowAllWallets] = useState(false); | ||
|
|
||
| const config = useMemo(() => props.externalWalletsConfig ?? {}, [props.externalWalletsConfig]); | ||
|
|
||
| const walletDiscoverySupported = useMemo( | ||
| () => walletRegistry && (Object.keys(walletRegistry.default || {}).length > 0 || Object.keys(walletRegistry.others || {}).length > 0), | ||
| [walletRegistry] | ||
| ); | ||
|
|
||
| const allUniqueButtons = useMemo(() => { | ||
| const seen = new Set<string>(); | ||
| return customConnectorButtons.concat(allRegistryButtons).filter((b: ExternalButton) => { | ||
| if (seen.has(b.name)) return false; | ||
| seen.add(b.name); | ||
| return true; | ||
| }); | ||
| }, [allRegistryButtons, customConnectorButtons]); | ||
|
|
||
| const defaultButtonKeys = useMemo(() => new Set(Object.keys(walletRegistry.default)), [walletRegistry]); | ||
|
|
||
| const defaultButtons = useMemo(() => { | ||
| const buttons = [ | ||
| ...allRegistryButtons.filter((b: ExternalButton) => b.hasInjectedWallet && defaultButtonKeys.has(b.name)), | ||
| ...customConnectorButtons, | ||
| ...allRegistryButtons.filter((b: ExternalButton) => !b.hasInjectedWallet && defaultButtonKeys.has(b.name)), | ||
| ].sort((a: ExternalButton, b: ExternalButton) => { | ||
| if (a.name === WALLET_CONNECTORS.METAMASK && b.name !== WALLET_CONNECTORS.METAMASK) return -1; | ||
| if (b.name === WALLET_CONNECTORS.METAMASK && a.name !== WALLET_CONNECTORS.METAMASK) return 1; | ||
| return 0; | ||
| }); | ||
|
|
||
| const seen = new Set<string>(); | ||
| return buttons | ||
| .filter((b: ExternalButton) => { | ||
| if (seen.has(b.name)) return false; | ||
| seen.add(b.name); | ||
| return true; | ||
| }) | ||
| .filter((b: ExternalButton) => selectedChain === "all" || b.chainNamespaces?.includes(selectedChain as ChainNamespaceType)); | ||
| }, [allRegistryButtons, customConnectorButtons, defaultButtonKeys, selectedChain]); | ||
|
|
||
| const installedWalletButtons = useMemo(() => { | ||
| return Object.keys(config).reduce((acc: ExternalButton[], connector: string) => { | ||
| if (connector !== WALLET_CONNECTORS.WALLET_CONNECT_V2 && connectorVisibilityMap[connector]) { | ||
| acc.push({ | ||
| name: connector, | ||
| displayName: config[connector].label || connector, | ||
| hasInjectedWallet: config[connector].isInjected || false, | ||
| hasWalletConnect: false, | ||
| hasInstallLinks: false, | ||
| }); | ||
| } | ||
| return acc; | ||
| }, []); | ||
| }, [config, connectorVisibilityMap]); | ||
|
|
||
| const filteredButtons = useMemo(() => { | ||
| if (walletDiscoverySupported) { | ||
| return [ | ||
| ...allUniqueButtons.filter((b: ExternalButton) => b.hasInjectedWallet), | ||
| ...allUniqueButtons.filter((b: ExternalButton) => !b.hasInjectedWallet), | ||
| ] | ||
| .sort((a: ExternalButton) => (a.name === WALLET_CONNECTORS.METAMASK ? -1 : 1)) | ||
| .filter((b: ExternalButton) => selectedChain === "all" || b.chainNamespaces?.includes(selectedChain as ChainNamespaceType)) | ||
| .filter((b: ExternalButton) => b.name.toLowerCase().includes(walletSearch.toLowerCase())); | ||
| } | ||
| return installedWalletButtons; | ||
| }, [walletDiscoverySupported, installedWalletButtons, walletSearch, allUniqueButtons, selectedChain]); | ||
|
|
||
| const externalButtons = useMemo(() => { | ||
| if (walletDiscoverySupported && !walletSearch && !isShowAllWallets) return defaultButtons; | ||
| return filteredButtons; | ||
| }, [walletDiscoverySupported, walletSearch, filteredButtons, defaultButtons, isShowAllWallets]); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing auto-expand effect from duplicated wallet logicLow Severity
|
||
|
|
||
| const totalExternalWalletsCount = filteredButtons.length; | ||
|
|
||
| const initialWalletCount = useMemo(() => { | ||
| if (isShowAllWallets) return totalExternalWalletsCount; | ||
| return walletDiscoverySupported ? defaultButtons.length : installedWalletButtons.length; | ||
| }, [walletDiscoverySupported, defaultButtons, installedWalletButtons, isShowAllWallets, totalExternalWalletsCount]); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Extensive duplicated wallet filtering logic across componentsMedium Severity
|
||
|
|
||
| const handleWalletSearch = useCallback((e: FormEvent<HTMLInputElement>) => { | ||
| setWalletSearch((e.target as HTMLInputElement).value); | ||
| }, []); | ||
|
|
||
| const handleWalletClick = useCallback((button: ExternalButton) => { | ||
| log.info("linkWallet: wallet selected", { | ||
| name: button.name, | ||
| displayName: button.displayName, | ||
| isInstalled: button.isInstalled, | ||
| hasInjectedWallet: button.hasInjectedWallet, | ||
| chainNamespaces: button.chainNamespaces, | ||
| }); | ||
| }, []); | ||
cursor[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| const handleMoreWallets = useCallback(() => { | ||
| setIsShowAllWallets(true); | ||
| }, []); | ||
|
|
||
| return ( | ||
| <div className="w3a--relative w3a--flex w3a--flex-1 w3a--flex-col w3a--gap-y-4"> | ||
| <div className="w3a--flex w3a--items-center w3a--justify-center"> | ||
| <p className="w3a--text-base w3a--font-medium w3a--text-app-gray-900 dark:w3a--text-app-white">Link a wallet</p> | ||
| </div> | ||
| <div className="w3a--flex w3a--flex-col w3a--gap-y-2"> | ||
| <ConnectWalletChainFilter isDark={isDark} isLoading={false} selectedChain={selectedChain} setSelectedChain={setSelectedChain} /> | ||
| <ConnectWalletSearch | ||
| totalExternalWalletCount={totalExternalWalletsCount} | ||
| isLoading={false} | ||
| walletSearch={walletSearch} | ||
| handleWalletSearch={handleWalletSearch} | ||
| /> | ||
| <ConnectWalletList | ||
| externalButtons={externalButtons} | ||
| isLoading={false} | ||
| totalExternalWalletsCount={totalExternalWalletsCount} | ||
| initialWalletCount={initialWalletCount} | ||
| handleWalletClick={handleWalletClick} | ||
| handleMoreWallets={handleMoreWallets} | ||
| isDark={isDark} | ||
| walletConnectUri="" | ||
| isShowAllWallets={isShowAllWallets} | ||
| /> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| export default LinkWallet; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| export type { LinkWalletProps } from "./LinkWallet"; | ||
| export { default } from "./LinkWallet"; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| import { log, WalletInitializationError, Web3AuthError } from "@web3auth/no-modal"; | ||
| import { Ref, ref } from "vue"; | ||
|
|
||
| import { useWeb3AuthInner } from "./useWeb3AuthInner"; | ||
|
|
||
| export interface IUseLinkWallet { | ||
| loading: Ref<boolean>; | ||
| error: Ref<Web3AuthError | null>; | ||
| linkWallet(): Promise<void>; | ||
| } | ||
|
|
||
| export const useLinkWallet = (): IUseLinkWallet => { | ||
| const { web3Auth } = useWeb3AuthInner(); | ||
| const loading = ref(false); | ||
| const error = ref<Web3AuthError | null>(null); | ||
|
|
||
| const linkWallet = async () => { | ||
| try { | ||
| if (!web3Auth.value) throw WalletInitializationError.notReady(); | ||
| error.value = null; | ||
| loading.value = true; | ||
| await web3Auth.value.linkWallet(); | ||
| } catch (err) { | ||
| log.error("Error opening link wallet", err); | ||
| error.value = err as Web3AuthError; | ||
| } finally { | ||
| loading.value = false; | ||
| } | ||
| }; | ||
|
|
||
| return { | ||
| loading, | ||
| error, | ||
| linkWallet, | ||
| }; | ||
| }; |


Uh oh!
There was an error while loading. Please reload this page.