UNPKG

@web3auth/modal

Version:

Multi chain wallet aggregator for web3Auth

283 lines (275 loc) 12.6 kB
import _objectSpread from '@babel/runtime/helpers/objectSpread2'; import { WALLET_CONNECTORS, ANALYTICS_EVENTS, log } from '@web3auth/no-modal'; import { useContext, useState, useMemo, useEffect } from 'react'; import { CONNECT_WALLET_PAGES } from '../../constants.js'; import { AnalyticsContext } from '../../context/AnalyticsContext.js'; import { RootContext } from '../../context/RootContext.js'; import ConnectWalletChainFilter from './ConnectWalletChainFilter/ConnectWalletChainFilter.js'; import ConnectWalletHeader from './ConnectWalletHeader/ConnectWalletHeader.js'; import ConnectWalletList from './ConnectWalletList/ConnectWalletList.js'; import ConnectWalletQrCode from './ConnectWalletQrCode/ConnectWalletQrCode.js'; import ConnectWalletSearch from './ConnectWalletSearch/ConnectWalletSearch.js'; import { jsxs, jsx } from 'react/jsx-runtime'; function ConnectWallet(props) { var _selectedButton$walle; const { isDark, config, walletConnectUri, metamaskConnectUri, walletRegistry, allRegistryButtons, customConnectorButtons, connectorVisibilityMap, deviceDetails, buttonRadius = "pill", chainNamespace, isExternalWalletModeOnly, onBackClick, handleExternalWalletClick, handleWalletDetailsHeight } = props; const { bodyState, setBodyState } = useContext(RootContext); const { analytics } = useContext(AnalyticsContext); const [currentPage, setCurrentPage] = useState(CONNECT_WALLET_PAGES.CONNECT_WALLET); const [selectedWallet, setSelectedWallet] = useState(false); const [isLoading] = useState(false); const [selectedButton, setSelectedButton] = useState(null); const [walletSearch, setWalletSearch] = useState(""); const [selectedChain, setSelectedChain] = useState("all"); const [isShowAllWallets, setIsShowAllWallets] = useState(false); const handleBack = () => { if (!selectedWallet && currentPage === CONNECT_WALLET_PAGES.CONNECT_WALLET && onBackClick) { onBackClick(false); return; } if (selectedWallet) { setCurrentPage(CONNECT_WALLET_PAGES.CONNECT_WALLET); setSelectedWallet(false); handleWalletDetailsHeight(); } }; const walletDiscoverySupported = useMemo(() => { const supported = walletRegistry && (Object.keys(walletRegistry.default || {}).length > 0 || Object.keys(walletRegistry.others || {}).length > 0); return supported; }, [walletRegistry]); const allUniqueButtons = useMemo(() => { const uniqueButtonSet = new Set(); return customConnectorButtons.concat(allRegistryButtons).filter(button => { if (uniqueButtonSet.has(button.name)) return false; uniqueButtonSet.add(button.name); return true; }); }, [allRegistryButtons, customConnectorButtons]); const defaultButtonKeys = useMemo(() => new Set(Object.keys(walletRegistry.default)), [walletRegistry]); const defaultButtons = useMemo(() => { // display order: default injected buttons > custom adapter buttons > default non-injected buttons const buttons = [...allRegistryButtons.filter(button => button.hasInjectedWallet && defaultButtonKeys.has(button.name)), ...customConnectorButtons, ...allRegistryButtons.filter(button => !button.hasInjectedWallet && defaultButtonKeys.has(button.name))].sort((a, b) => { // favor MetaMask over other wallets if (a.name === WALLET_CONNECTORS.METAMASK && b.name === WALLET_CONNECTORS.METAMASK) { // favor installed MetaMask over non-installed MetaMask if (a.isInstalled) return -1; if (b.isInstalled) return 1; // favor injected MetaMask over non-injected MetaMask if (a.hasInjectedWallet) return -1; if (b.hasInjectedWallet) return 1; return 0; } if (a.name === WALLET_CONNECTORS.METAMASK) return -1; if (b.name === WALLET_CONNECTORS.METAMASK) return 1; return 0; }); const buttonSet = new Set(); return buttons.filter(button => { if (buttonSet.has(button.name)) return false; buttonSet.add(button.name); return true; }).filter(button => { var _button$chainNamespac; return selectedChain === "all" || ((_button$chainNamespac = button.chainNamespaces) === null || _button$chainNamespac === void 0 ? void 0 : _button$chainNamespac.includes(selectedChain)); }); }, [allRegistryButtons, customConnectorButtons, defaultButtonKeys, selectedChain]); const installedWalletButtons = useMemo(() => { const visibilityMap = connectorVisibilityMap; return Object.keys(config).reduce((acc, localConnector) => { if (localConnector !== WALLET_CONNECTORS.WALLET_CONNECT_V2 && visibilityMap[localConnector]) { acc.push({ name: localConnector, displayName: config[localConnector].label || localConnector, hasInjectedWallet: config[localConnector].isInjected, hasWalletConnect: false, hasInstallLinks: false }); } return acc; }, []); }, [connectorVisibilityMap, config]); const handleWalletSearch = e => { const searchValue = e.target.value; setWalletSearch(searchValue); }; const handleChainFilterChange = chain => { setSelectedChain(chain); }; const filteredButtons = useMemo(() => { if (walletDiscoverySupported) { return [...allUniqueButtons.filter(button => button.hasInjectedWallet), ...allUniqueButtons.filter(button => !button.hasInjectedWallet)].sort((a, _) => a.name === WALLET_CONNECTORS.METAMASK ? -1 : 1).filter(button => selectedChain === "all" || button.chainNamespaces.includes(selectedChain)).filter(button => button.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]); const totalExternalWalletsCount = useMemo(() => filteredButtons.length, [filteredButtons]); const initialWalletCount = useMemo(() => { if (isShowAllWallets) return totalExternalWalletsCount; return walletDiscoverySupported ? defaultButtons.length : installedWalletButtons.length; }, [walletDiscoverySupported, defaultButtons, installedWalletButtons, isShowAllWallets, totalExternalWalletsCount]); // Automatically show all wallets if there are less than or equal to 15 wallets // also resets everytime we search causing no. of wallets to change or select different chain useEffect(() => { if (walletDiscoverySupported && totalExternalWalletsCount <= 15) { setIsShowAllWallets(true); } else { setIsShowAllWallets(false); } }, [walletDiscoverySupported, selectedChain, totalExternalWalletsCount]); /** * Wallet click logic * - For installed wallets * - For MetaMask non-injected on desktop, show QR code for connection * - Ask user to select a chain namespace if it has multiple namespaces * - Otherwise, use their connectors to connect * - For wallet-discovery wallets (not installed) * - On desktop, show QR code for connection if wallet connect v2 is supported, otherwise show install links * - On mobile, open deeplink with wallet connect uri (won't go into this function as it'll open the deeplink) */ const handleWalletClick = button => { analytics === null || analytics === void 0 || analytics.track(ANALYTICS_EVENTS.EXTERNAL_WALLET_SELECTED, { connector: button.isInstalled ? button.name : button.hasWalletConnect ? WALLET_CONNECTORS.WALLET_CONNECT_V2 : "", wallet_name: button.displayName, is_installed: button.isInstalled, is_injected: button.hasInjectedWallet, chain_namespaces: button.chainNamespaces, has_wallet_connect: button.hasWalletConnect, has_install_links: button.hasInstallLinks, has_wallet_registry_item: !!button.walletRegistryItem, total_external_wallets: allUniqueButtons.length }); log.info("handleWalletClick", button); // for installed wallets if (button.isInstalled) { var _button$chainNamespac2; // for MetaMask non-injected on desktop, show QR code for connection if (button.name === WALLET_CONNECTORS.METAMASK && !button.hasInjectedWallet && deviceDetails.platform === "desktop") { handleExternalWalletClick({ connector: button.name }); setSelectedButton(button); setSelectedWallet(true); setCurrentPage(CONNECT_WALLET_PAGES.SELECTED_WALLET); handleWalletDetailsHeight(); return; } // show chain namespace selector if the button has multiple chain namespaces if (((_button$chainNamespac2 = button.chainNamespaces) === null || _button$chainNamespac2 === void 0 ? void 0 : _button$chainNamespac2.length) > 1) { setBodyState(_objectSpread(_objectSpread({}, bodyState), {}, { multiChainSelector: { show: true, wallet: button } })); return; } // otherwise, use their connectors to connect handleExternalWalletClick({ connector: button.name }); return; } else { // show QR code if wallet connect v2 is supported if (button.hasWalletConnect) { setSelectedButton(button); setSelectedWallet(true); setCurrentPage(CONNECT_WALLET_PAGES.SELECTED_WALLET); handleWalletDetailsHeight(); } else { // otherwise, show install links setBodyState(_objectSpread(_objectSpread({}, bodyState), {}, { installLinks: { show: true, wallet: button } })); } } }; const handleMoreWallets = () => { setIsShowAllWallets(true); }; const qrCodeValue = useMemo(() => { if (!selectedWallet) return null; if (selectedButton.name === WALLET_CONNECTORS.METAMASK && !selectedButton.hasInjectedWallet) { return metamaskConnectUri; } return walletConnectUri; }, [metamaskConnectUri, selectedButton, selectedWallet, walletConnectUri]); const disableBackButton = useMemo(() => { // If wallet is selected, show the back button if (selectedWallet) return false; // Otherwise, if external wallet mode only, login screen is skipped so back button is not needed if (isExternalWalletModeOnly) return true; return false; }, [selectedWallet, isExternalWalletModeOnly]); return /*#__PURE__*/jsxs("div", { className: "w3a--relative w3a--flex w3a--flex-1 w3a--flex-col w3a--gap-y-4", children: [/*#__PURE__*/jsx(ConnectWalletHeader, { disableBackButton: disableBackButton, onBackClick: handleBack, currentPage: currentPage, selectedButton: selectedButton }), selectedWallet ? /*#__PURE__*/jsx(ConnectWalletQrCode, { qrCodeValue: qrCodeValue, isDark: isDark, selectedButton: selectedButton, primaryColor: (_selectedButton$walle = selectedButton.walletRegistryItem) === null || _selectedButton$walle === void 0 ? void 0 : _selectedButton$walle.primaryColor, logoImage: `https://images.web3auth.io/login-${selectedButton.name}.${selectedButton.imgExtension}` }) : /*#__PURE__*/jsxs("div", { className: "w3a--flex w3a--flex-col w3a--gap-y-2", children: [chainNamespace.length > 1 && /*#__PURE__*/jsx(ConnectWalletChainFilter, { isDark: isDark, isLoading: isLoading, selectedChain: selectedChain, setSelectedChain: handleChainFilterChange, chainNamespace: chainNamespace }), /*#__PURE__*/jsx(ConnectWalletSearch, { totalExternalWalletCount: totalExternalWalletsCount, isLoading: isLoading, walletSearch: walletSearch, handleWalletSearch: handleWalletSearch, buttonRadius: buttonRadius }), /*#__PURE__*/jsx(ConnectWalletList, { externalButtons: externalButtons, isLoading: isLoading, totalExternalWalletsCount: totalExternalWalletsCount, initialWalletCount: initialWalletCount, handleWalletClick: handleWalletClick, handleMoreWallets: handleMoreWallets, isDark: isDark, deviceDetails: deviceDetails, walletConnectUri: walletConnectUri, buttonRadius: buttonRadius, isShowAllWallets: isShowAllWallets })] })] }); } export { ConnectWallet as default };