"use client"; import { useEffect, useMemo } from "react"; import type { Chain } from "../../../../../chains/types.js"; import type { ThirdwebClient } from "../../../../../client/client.js"; import { getDefaultWallets } from "../../../../../wallets/defaultWallets.js"; import type { Wallet } from "../../../../../wallets/interfaces/wallet.js"; import type { SmartWalletOptions } from "../../../../../wallets/smart/types.js"; import type { WalletId } from "../../../../../wallets/wallet-types.js"; import { CustomThemeProvider, useCustomTheme, } from "../../../../core/design-system/CustomThemeProvider.js"; import { radius } from "../../../../core/design-system/index.js"; import { type SiweAuthOptions, useSiweAuth, } from "../../../../core/hooks/auth/useSiweAuth.js"; import type { ConnectEmbedProps } from "../../../../core/hooks/connection/ConnectEmbedProps.js"; import type { OnConnectCallback } from "../../../../core/hooks/connection/types.js"; import { useActiveAccount } from "../../../../core/hooks/wallets/useActiveAccount.js"; import { useActiveWallet } from "../../../../core/hooks/wallets/useActiveWallet.js"; import { useIsAutoConnecting } from "../../../../core/hooks/wallets/useIsAutoConnecting.js"; import { useConnectionManager } from "../../../../core/providers/connection-manager.js"; import { WalletUIStatesProvider } from "../../../providers/wallet-ui-states-provider.js"; import { canFitWideModal } from "../../../utils/canFitWideModal.js"; import { cls } from "../../../utils/cls.js"; import { usePreloadWalletProviders } from "../../../utils/usePreloadWalletProviders.js"; import { LoadingScreen } from "../../../wallets/shared/LoadingScreen.js"; import { AutoConnect } from "../../AutoConnect/AutoConnect.js"; import { DynamicHeight } from "../../components/DynamicHeight.js"; import { StyledDiv } from "../../design-system/elements.js"; import type { LocaleId } from "../../types.js"; import { modalMaxWidthCompact, modalMaxWidthWide, reservedScreens, wideModalMaxHeight, } from "../constants.js"; import { useConnectLocale } from "../locale/getConnectLocale.js"; import type { ConnectLocale } from "../locale/types.js"; import type { WelcomeScreen } from "../screens/types.js"; import { ConnectModalContent } from "./ConnectModalContent.js"; import { useSetupScreen } from "./screen.js"; /** * An inline wallet connection component that allows to: * * - Connect to 500+ external wallets * - Connect with email, phone, passkey or socials * - Convert any wallet to a ERC4337 smart wallet for gasless transactions * - Sign in with ethereum (Auth) * * It renders the same UI as the [`ConnectButton`](https://portal.thirdweb.com/react/v5/ConnectButton) component's modal - but directly inline in the page instead of being in a modal. * * Once connected, the component does not render any UI. It only renders UI if wallet is not connected. * * @example * * ## Default setup * * ```tsx * import { createThirdwebClient } from "thirdweb"; * import { ConnectEmbed } from "thirdweb/react"; * * const client = createThirdwebClient({ clientId: "YOUR_CLIENT_ID" }); * * * ``` * * [View all available config options](https://portal.thirdweb.com/references/typescript/v5/ConnectEmbedProps) * * ## Customization options * * ### Customizing wallet options * * ```tsx * * ``` * * [View all available wallets](https://portal.thirdweb.com/typescript/v5/supported-wallets) * * ### Customizing the default chain to connect to * * ```tsx * import { base } from "thirdweb/chains"; * * * ``` * * ### Enabling Account Abstraction * * By passing the `accountAbstraction` prop, ALL connected wallets will be converted to smart accounts. * And by setting `sponsorGas` to `true`, all transactions done with those smart accounts will be sponsored. * * ```tsx * ; * ``` * Note that this prop doesn't affect ecosystem wallets. Ecosystem wallets will only be converted to smart accounts if the ecosystem owner has enabled account abstraction. * * ### Enabling sign in with ethereum (Auth) * * ```tsx * { * console.log("checking if logged in!", { address }); * return await isLoggedIn(); * }, * doLogin: async (params) => { * console.log("logging in!"); * await login(params); * }, * getLoginPayload: async ({ address }) => * generatePayload({ address }), * doLogout: async () => { * console.log("logging out!"); * await logout(); * }, * }} * />; * ``` * * ### Customizing the theme * * ```tsx * * ``` * * For more granular control, you can also pass a custom theme object: * * ```tsx * * ``` * * [View all available themes properties](https://portal.thirdweb.com/references/typescript/v5/Theme) * * ### Overriding styles using class names * * Some elements in this component have classes with a `tw-` prefix. * You can target these classes in your own CSS stylesheet to override their styles. * * In some cases, you may need to use the `!important` flag for the override to take effect. Do not use on auto-generated class names, as they may change between builds. * * ```css * .tw-back-button { * background-color: red !important; * } * ``` * * ### Changing the display language * * ```tsx * * ``` * * [View all available locales](https://portal.thirdweb.com/references/typescript/v5/LocaleId) * * @param props - * The props for the `ConnectEmbed` component. * * Refer to the [`ConnectEmbedProps`](https://portal.thirdweb.com/references/typescript/v5/ConnectEmbedProps) type for more details * * @returns A JSX element that renders the component. * @component * @walletConnection */ export function ConnectEmbed(props: ConnectEmbedProps) { const activeWallet = useActiveWallet(); const activeAccount = useActiveAccount(); const siweAuth = useSiweAuth(activeWallet, activeAccount, props.auth); const show = !activeAccount || (siweAuth.requiresAuth && !siweAuth.isLoggedIn); const connectionManager = useConnectionManager(); // Add props.chain and props.chains to defined chains store useEffect(() => { if (props.chain) { connectionManager.defineChains([props.chain]); } }, [props.chain, connectionManager]); useEffect(() => { if (props.chains) { connectionManager.defineChains(props.chains); } }, [props.chains, connectionManager]); const wallets = useMemo( () => props.wallets || getDefaultWallets({ appMetadata: props.appMetadata, chains: props.chains, }), [props.wallets, props.appMetadata, props.chains], ); const localeId = props.locale || "en_US"; const localeQuery = useConnectLocale(localeId); usePreloadWalletProviders({ wallets, }); const modalSize = useMemo(() => { return !canFitWideModal() || wallets.length === 1 ? "compact" : props.modalSize || ("compact" as const); }, [wallets.length, props.modalSize]); const meta = useMemo(() => { return { privacyPolicyUrl: props.privacyPolicyUrl, requireApproval: props.requireApproval, showThirdwebBranding: props.showThirdwebBranding !== false, termsOfServiceUrl: props.termsOfServiceUrl, title: undefined, titleIconUrl: undefined, }; }, [ props.privacyPolicyUrl, props.showThirdwebBranding, props.termsOfServiceUrl, props.requireApproval, ]); const preferredChain = props.accountAbstraction?.chain || props.chain || props.chains?.[0]; const autoConnectComp = props.autoConnect !== false && ( ); if (show) { if (!localeQuery.data) { return ( <> {autoConnectComp} ); } return ( {autoConnectComp} ); } return
{autoConnectComp}
; } /** * @internal */ const ConnectEmbedContent = (props: { modalSize?: "compact" | "wide"; className?: string; style?: React.CSSProperties; // --- accountAbstraction: SmartWalletOptions | undefined; auth: SiweAuthOptions | undefined; chain: Chain | undefined; chains: Chain[] | undefined; client: ThirdwebClient; connectLocale: ConnectLocale; meta: { title?: string; titleIconUrl?: string; showThirdwebBranding?: boolean; termsOfServiceUrl?: string; privacyPolicyUrl?: string; }; size: "compact" | "wide"; header: | { title?: string; titleIcon?: string; } | true | undefined; localeId: LocaleId; onConnect: OnConnectCallback | undefined; recommendedWallets: Wallet[] | undefined; showAllWallets: boolean | undefined; hiddenWallets: WalletId[] | undefined; walletConnect: | { projectId?: string; } | undefined; wallets: Wallet[]; welcomeScreen: WelcomeScreen | undefined; }) => { // const requiresSignIn = false; const screenSetup = useSetupScreen({ size: props.size, wallets: props.wallets, welcomeScreen: undefined, }); const { setScreen, initialScreen, screen } = screenSetup; const activeWallet = useActiveWallet(); const activeAccount = useActiveAccount(); const siweAuth = useSiweAuth(activeWallet, activeAccount, props.auth); const isAutoConnecting = useIsAutoConnecting(); let content = null; // if sign in is required but connect embed is showing the initial screen - change to sign in screen useEffect(() => { if ( siweAuth.requiresAuth && !siweAuth.isLoggedIn && activeAccount && screen === initialScreen ) { setScreen(reservedScreens.signIn); } }, [siweAuth, setScreen, activeAccount, screen, initialScreen]); const modalSize = !canFitWideModal() ? "compact" : props.modalSize || ("compact" as const); // show spinner on page load and during auto connecting a wallet if (isAutoConnecting) { content = ; } else { content = ( { setScreen(initialScreen); }} onConnect={props.onConnect} recommendedWallets={props.recommendedWallets} screenSetup={screenSetup} setModalVisibility={() => { // no op }} shouldSetActive={true} showAllWallets={props.showAllWallets} size={props.size} walletConnect={props.walletConnect} walletIdsToHide={props.hiddenWallets} wallets={props.wallets} welcomeScreen={props.welcomeScreen} /> ); } return ( {modalSize === "wide" ? ( content ) : ( {content} )} ); }; export const EmbedContainer = /* @__PURE__ */ StyledDiv<{ modalSize: "compact" | "wide"; }>((props) => { const { modalSize } = props; const theme = useCustomTheme(); return { "& *": { boxSizing: "border-box", }, "& *::selection": { backgroundColor: theme.colors.selectedTextBg, color: theme.colors.selectedTextColor, }, background: theme.colors.modalBg, border: `1px solid ${theme.colors.borderColor}`, borderRadius: radius.xl, boxSizing: "border-box", color: theme.colors.primaryText, fontFamily: theme.fontFamily, height: modalSize === "compact" ? "auto" : wideModalMaxHeight, lineHeight: "normal", overflow: "hidden", position: "relative", maxWidth: "100%", width: modalSize === "compact" ? modalMaxWidthCompact : modalMaxWidthWide, }; });