UNPKG

@0xsequence/connect

Version:
343 lines 21.3 kB
'use client'; import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { ArrowRightIcon, Divider, IconButton, Image, ModalPrimitive, Spinner, Text, TextInput } from '@0xsequence/design-system'; import { useGetWaasStatus } from '@0xsequence/hooks'; import { SequenceWaaS } from '@0xsequence/waas'; import { genUserId } from '@databeat/tracker'; import { clsx } from 'clsx'; import { useEffect, useState } from 'react'; import { appleAuthHelpers, useScript } from 'react-apple-signin-auth'; import { useConnect, useConnections, useSignMessage } from 'wagmi'; import { EVENT_SOURCE } from '../../constants/analytics.js'; import { LocalStorageKey } from '../../constants/localStorage.js'; import { CHAIN_ID_FOR_SIGNATURE } from '../../constants/walletLinking.js'; import { useAnalyticsContext } from '../../contexts/Analytics.js'; import { useStorage } from '../../hooks/useStorage.js'; import { useEmailAuth } from '../../hooks/useWaasEmailAuth.js'; import { useWaasLinkWallet } from '../../hooks/useWaasLinkWallet.js'; import { useWallets } from '../../hooks/useWallets.js'; import { useWalletSettings } from '../../hooks/useWalletSettings.js'; import { isEmailValid } from '../../utils/helpers.js'; import { GuestWaasConnectButton, XWaasConnectButton } from '../ConnectButton/ConnectButton.js'; import { AppleWaasConnectButton, ConnectButton, EpicWaasConnectButton, GoogleWaasConnectButton, ShowAllWalletsButton } from '../ConnectButton/index.js'; import { PoweredBySequence } from '../SequenceLogo/index.js'; import { Banner } from './Banner.js'; import { ConnectedWallets } from './ConnectedWallets.js'; import { EmailWaasVerify } from './EmailWaasVerify.js'; import { ExtendedWalletList } from './ExtendedWalletList.js'; const MAX_ITEM_PER_ROW = 4; export const SEQUENCE_UNIVERSAL_CONNECTOR_NAME = 'Sequence'; export const Connect = (props) => { useScript(appleAuthHelpers.APPLE_SCRIPT_SRC); const { analytics } = useAnalyticsContext(); const { hideExternalConnectOptions, hideConnectedWallets, hideSocialConnectOptions } = useWalletSettings(); const { onClose, emailConflictInfo, config = {}, isInline = false } = props; const { signIn = {} } = config; const storage = useStorage(); const descriptiveSocials = !!config?.signIn?.descriptiveSocials; const [isLoading, setIsLoading] = useState(false); const projectName = config?.signIn?.projectName; const showWalletAuthOptionsFirst = config?.signIn?.showWalletAuthOptionsFirst ?? false; const [email, setEmail] = useState(''); const [showEmailWaasPinInput, setShowEmailWaasPinInput] = useState(false); const [showExtendedList, setShowExtendedList] = useState(null); const { status, connectors, connect } = useConnect(); const connections = useConnections(); const { signMessageAsync } = useSignMessage(); const { wallets, linkedWallets, disconnectWallet, refetchLinkedWallets } = useWallets(); const { data: waasStatusData } = useGetWaasStatus(); const hasConnectedSequenceUniversal = connections.some(c => c.connector.name === SEQUENCE_UNIVERSAL_CONNECTOR_NAME); const hasConnectedSocialOrSequenceUniversal = connections.some(c => c.connector?._wallet?.type === 'social') || hasConnectedSequenceUniversal; const waasConnection = connections.find(c => c.connector?.type === 'sequence-waas'); // Setup wallet linking const { linkWallet, removeLinkedWallet } = useWaasLinkWallet(waasConnection?.connector); const [lastConnectedWallet, setLastConnectedWallet] = useState(undefined); const [isSigningLinkMessage, setIsSigningLinkMessage] = useState(false); const handleUnlinkWallet = async (address) => { try { await removeLinkedWallet(address); const parentWallet = wallets.find(w => w.isEmbedded)?.address; try { analytics?.track({ event: 'UNLINK_WALLET', props: { parentWalletAddress: parentWallet ? getUserIdForEvent(parentWallet) : '', linkedWalletAddress: getUserIdForEvent(address), linkedWalletType: linkedWallets?.find(lw => lw.linkedWalletAddress === address)?.walletType || '', source: EVENT_SOURCE } }); } catch (e) { console.warn('unlink analytics error:', e); } refetchLinkedWallets(); } catch (e) { console.warn('unlink error:', e); } }; useEffect(() => { if (!lastConnectedWallet) { return; } const tryLinkWallet = async () => { const nonWaasWallets = connections.filter(c => c.connector?.type !== 'sequence-waas'); const nonLinkedWallets = nonWaasWallets.filter(c => !linkedWallets?.find(lw => lw.linkedWalletAddress === c.accounts[0].toLowerCase())); if (nonLinkedWallets.map(w => w.accounts[0]).includes(lastConnectedWallet)) { const waasWalletAddress = waasConnection?.accounts[0]; if (!waasWalletAddress) { return; } const childWalletAddress = lastConnectedWallet; const childMessage = `Link to parent wallet with address ${waasWalletAddress}`; setIsSigningLinkMessage(true); let childSignature; try { childSignature = await signMessageAsync({ account: lastConnectedWallet, message: childMessage }); if (!childSignature) { return; } await linkWallet({ signatureChainId: CHAIN_ID_FOR_SIGNATURE, connectorName: connections.find(c => c.accounts[0] === lastConnectedWallet)?.connector?.name || '', childWalletAddress, childMessage, childSignature }); try { analytics?.track({ event: 'LINK_WALLET', props: { parentWalletAddress: getUserIdForEvent(waasWalletAddress), linkedWalletAddress: getUserIdForEvent(childWalletAddress), linkedWalletType: connections.find(c => c.accounts[0] === lastConnectedWallet)?.connector?.name || '', source: EVENT_SOURCE } }); } catch (e) { console.warn('link analytics error:', e); } refetchLinkedWallets(); } catch (e) { console.log(e); } } setIsSigningLinkMessage(false); setLastConnectedWallet(undefined); onClose(); }; if (connections && connections.length > 1 && waasConnection !== undefined) { tryLinkWallet(); } else { setLastConnectedWallet(undefined); onClose(); } }, [connections, lastConnectedWallet]); const baseWalletConnectors = connectors .filter(c => { return c._wallet && (c._wallet.type === 'wallet' || c._wallet.type === undefined); }) // Remove metamask if metamask is detected .filter(c => { const isMetamaskInjected = window.ethereum?.isMetaMask; if (c._wallet?.id === 'metamask-wallet' && isMetamaskInjected) { return false; } return true; }) // Remove coinbase if coinbase is detected .filter(c => { const isCoinbaseInjected = window.ethereum?.isCoinbaseWallet; if (c._wallet?.id === 'coinbase-wallet' && isCoinbaseInjected) { return false; } return true; }); const mockConnector = baseWalletConnectors.find(connector => { return connector._wallet.id === 'mock'; }); // EIP-6963 connectors will not have the _wallet property const injectedConnectors = connectors .filter(connector => { // Keep the connector when it is an EIP-6963 connector if (connector.type === 'injected') { return true; } // We check if SDK-generated connectors is actually an injected connector const isMetamaskInjected = window.ethereum?.isMetaMask; if (connector._wallet?.id === 'metamask-wallet' && isMetamaskInjected) { return true; } const isCoinbaseInjected = window.ethereum?.isCoinbaseWallet; if (connector._wallet?.id === 'coinbase-wallet' && isCoinbaseInjected) { return true; } return false; }) .map(connector => { if (connector?._wallet) { return connector; } const Logo = (props) => { return _jsx(Image, { src: connector.icon, alt: connector.name, disableAnimation: true, ...props }); }; return { ...connector, _wallet: { id: connector.id, name: connector.name, logoLight: Logo, logoDark: Logo, type: 'wallet' } }; }); const socialAuthConnectors = connectors .filter(c => c._wallet?.type === 'social') .filter(c => !c._wallet.id.includes('email')); const walletConnectors = [...injectedConnectors, ...baseWalletConnectors].filter(c => hasConnectedSequenceUniversal ? c.name !== SEQUENCE_UNIVERSAL_CONNECTOR_NAME : true); const emailConnector = connectors.find(c => c._wallet?.id.includes('email')); const onChangeEmail = ev => { setEmail(ev.target.value); }; useEffect(() => { setIsLoading(status === 'pending' || status === 'success'); }, [status]); const handleConnect = async (connector) => { if (connector._wallet.id === 'guest-waas') { const sequenceWaaS = new SequenceWaaS({ projectAccessKey: config.projectAccessKey, waasConfigKey: config.waasConfigKey ?? '' }); await sequenceWaaS.signIn({ guest: true }, 'Guest'); } connect({ connector }, { onSuccess: result => { if (result?.accounts[0]) { config.onConnectSuccess?.(result.accounts[0]); } }, onSettled: result => { setLastConnectedWallet(result?.accounts[0]); } }); }; const onConnect = (connector) => { if (signIn.useMock && mockConnector) { handleConnect(mockConnector); return; } if (connector._wallet.id === 'email') { const email = prompt('Auto-email login, please specify the email address:'); if ('setEmail' in connector) { ; connector.setEmail(email); } } handleConnect(connector); }; const onConnectInlineEmail = async (e) => { e.preventDefault(); if (!isEmailValid(email)) { return; } if (signIn.useMock && mockConnector) { handleConnect(mockConnector); return; } if (emailConnector) { if ('setEmail' in emailConnector) { ; emailConnector.setEmail(email); } if (emailConnector._wallet.id === 'email-waas') { try { await sendEmailCode(); setShowEmailWaasPinInput(true); } catch (e) { console.log(e); } } else { handleConnect(emailConnector); } } }; const { inProgress: emailAuthInProgress, loading: emailAuthLoading, error: emailAuthError, initiateAuth: initiateEmailAuth, sendChallengeAnswer, resetError } = useEmailAuth({ connector: emailConnector, onSuccess: async (result) => { if ('signInResponse' in result && result.signInResponse?.email) { storage?.setItem(LocalStorageKey.WaasSignInEmail, result.signInResponse.email); } if (emailConnector) { if (result.version === 1) { // Store the version 1 idToken so that it can be used to authenticate during a refresh storage?.setItem(LocalStorageKey.WaasEmailIdToken, result.idToken); } handleConnect(emailConnector); } } }); const sendEmailCode = async () => { await initiateEmailAuth(email); }; // Hide the email input if there is an email conflict useEffect(() => { if (emailConflictInfo) { setShowEmailWaasPinInput(false); } }, [emailConflictInfo]); const showSocialConnectorSection = socialAuthConnectors.length > 0; const showEmailInputSection = !!emailConnector; const showMoreSocialOptions = socialAuthConnectors.length > MAX_ITEM_PER_ROW; const showMoreWalletOptions = walletConnectors.length > MAX_ITEM_PER_ROW; const socialConnectorsPerRow = showMoreSocialOptions && !descriptiveSocials ? MAX_ITEM_PER_ROW - 1 : socialAuthConnectors.length; const walletConnectorsPerRow = showMoreWalletOptions ? MAX_ITEM_PER_ROW - 1 : walletConnectors.length; const WalletConnectorsSection = () => { return (_jsxs("div", { className: clsx('flex gap-2 flex-row justify-center items-center', hasConnectedSequenceUniversal ? 'mt-4' : 'mt-6'), children: [walletConnectors.slice(0, walletConnectorsPerRow).map(connector => { return _jsx(ConnectButton, { connector: connector, onConnect: onConnect }, connector.uid); }), showMoreWalletOptions && _jsx(ShowAllWalletsButton, { onClick: () => setShowExtendedList('wallet') })] })); }; if (showExtendedList) { const SEARCHABLE_TRESHOLD = 8; const connectors = showExtendedList === 'social' ? socialAuthConnectors : walletConnectors; const searchable = connectors.length > SEARCHABLE_TRESHOLD; return (_jsx(ExtendedWalletList, { searchable: searchable, onGoBack: () => setShowExtendedList(null), onConnect: onConnect, connectors: connectors, title: showExtendedList === 'social' ? 'Continue with a social account' : 'Choose a wallet' })); } if (waasStatusData?.errorResponse) { const errorMessage = waasStatusData.errorResponse.status === 451 ? 'Service unavailable due to legal and geographic restrictions' : `Something went wrong. (${waasStatusData.errorResponse.msg})`; return (_jsx("div", { className: isInline ? 'p-0' : 'p-4', children: _jsxs("div", { className: "flex flex-col justify-center text-primary items-center font-medium", style: { marginTop: isInline ? '0' : '2px' }, children: [_jsx(TitleWrapper, { isInline: isInline, children: _jsx(Text, { color: "secondary", children: isLoading ? `Connecting...` : hasConnectedSocialOrSequenceUniversal ? 'Wallets' : `Connect ${projectName ? `to ${projectName}` : ''}` }) }), _jsx("div", { className: "relative flex flex-col items-center justify-center p-8", children: _jsx("div", { className: "flex flex-col items-center gap-4 mt-2 mb-2", children: _jsx(Text, { color: "secondary", className: "text-center text-lg font-medium text-negative", children: errorMessage }) }) })] }) })); } return (_jsxs("div", { className: isInline ? 'p-0' : 'p-4', children: [_jsxs("div", { className: "flex flex-col justify-center text-primary items-center font-medium", style: { marginTop: isInline ? '0' : '2px' }, children: [_jsx(TitleWrapper, { isInline: isInline, children: _jsx(Text, { color: "secondary", children: isLoading ? `Connecting...` : hasConnectedSocialOrSequenceUniversal ? 'Wallets' : `Connect ${projectName ? `to ${projectName}` : ''}` }) }), isSigningLinkMessage && (_jsx("div", { className: "mt-4", children: _jsx(Text, { variant: "small", color: "muted", children: "Confirm the signature request to link your account" }) }))] }), isLoading ? (_jsx("div", { className: "flex justify-center items-center pt-4", children: _jsx(Spinner, {}) })) : (_jsxs(_Fragment, { children: [!hideConnectedWallets && wallets.length > 0 && !showEmailWaasPinInput && (_jsxs(_Fragment, { children: [_jsx(ConnectedWallets, { wallets: wallets, linkedWallets: linkedWallets, disconnectWallet: disconnectWallet, unlinkWallet: handleUnlinkWallet, connectWallet: handleConnect, connectors: walletConnectors, embeddedWalletTitle: config.embeddedWalletTitle }), _jsx(_Fragment, { children: !hideExternalConnectOptions && (_jsxs(_Fragment, { children: [_jsx(Divider, { className: "text-background-raised w-full" }), _jsx("div", { className: "flex justify-center", children: _jsx(Text, { variant: "small", color: "muted", children: !hasConnectedSocialOrSequenceUniversal ? 'Connect with a social account' : 'Connect another wallet' }) })] })) })] })), showEmailWaasPinInput ? (_jsx(EmailWaasVerify, { sendEmailCode: sendEmailCode, error: emailAuthError, isLoading: emailAuthLoading, onConfirm: sendChallengeAnswer, resetError: resetError })) : (_jsxs(_Fragment, { children: [!hasConnectedSocialOrSequenceUniversal && (_jsxs(_Fragment, { children: [_jsx(Banner, { config: config }), showWalletAuthOptionsFirst && !hideExternalConnectOptions && walletConnectors.length > 0 && (_jsx(WalletConnectorsSection, {})), _jsx("div", { className: "flex mt-6 gap-6 flex-col", children: _jsxs(_Fragment, { children: [!hideSocialConnectOptions && showSocialConnectorSection && (_jsxs("div", { className: `flex gap-2 justify-center items-center ${descriptiveSocials ? 'flex-col' : 'flex-row'}`, children: [socialAuthConnectors.slice(0, socialConnectorsPerRow).map(connector => { return (_jsx("div", { className: "w-full", children: connector._wallet.id === 'guest-waas' ? (_jsx(GuestWaasConnectButton, { isDescriptive: descriptiveSocials, connector: connector, onConnect: onConnect, setIsLoading: setIsLoading })) : connector._wallet.id === 'google-waas' ? (_jsx(GoogleWaasConnectButton, { isDescriptive: descriptiveSocials, connector: connector, onConnect: onConnect })) : connector._wallet.id === 'apple-waas' ? (_jsx(AppleWaasConnectButton, { isDescriptive: descriptiveSocials, connector: connector, onConnect: onConnect })) : connector._wallet.id === 'epic-waas' ? (_jsx(EpicWaasConnectButton, { isDescriptive: descriptiveSocials, connector: connector, onConnect: onConnect })) : connector._wallet.id === 'X-waas' ? (_jsx(XWaasConnectButton, { isDescriptive: descriptiveSocials, connector: connector, onConnect: onConnect })) : (_jsx(ConnectButton, { disableTooltip: config?.signIn?.disableTooltipForDescriptiveSocials, isDescriptive: descriptiveSocials, connector: connector, onConnect: onConnect })) }, connector.uid)); }), showMoreSocialOptions && (_jsx("div", { className: "w-full", children: _jsx(ShowAllWalletsButton, { onClick: () => setShowExtendedList('social') }) }))] })), !hideSocialConnectOptions && showSocialConnectorSection && showEmailInputSection && (_jsxs("div", { className: "flex gap-4 flex-row justify-center items-center", children: [_jsx(Divider, { className: "mx-0 my-0 text-background-secondary grow" }), _jsx(Text, { className: "leading-4 h-4 text-sm", variant: "normal", fontWeight: "medium", color: "muted", children: "or" }), _jsx(Divider, { className: "mx-0 my-0 text-background-secondary grow" })] })), showEmailInputSection && (_jsx(_Fragment, { children: _jsx("form", { onSubmit: onConnectInlineEmail, children: _jsx(TextInput, { autoFocus: true, onChange: onChangeEmail, value: email, name: "email", placeholder: "Email address", controls: _jsx(_Fragment, { children: emailAuthInProgress ? (_jsx(Spinner, {})) : (_jsx(IconButton, { type: "submit", size: "xs", icon: ArrowRightIcon, disabled: !isEmailValid(email) })) }), "data-1p-ignore": true }) }) }))] }) })] })), !showWalletAuthOptionsFirst && !hideExternalConnectOptions && walletConnectors.length > 0 && (_jsx(WalletConnectorsSection, {})), _jsx("div", { className: "mt-6", children: _jsx(PoweredBySequence, {}) })] }))] }))] })); }; const TitleWrapper = ({ children, isInline }) => { if (isInline) { return _jsx(_Fragment, { children: children }); } return _jsx(ModalPrimitive.Title, { asChild: true, children: children }); }; const getUserIdForEvent = (address) => { return genUserId(address.toLowerCase(), false, { privacy: { userIdHash: true } }).userId; }; //# sourceMappingURL=Connect.js.map