"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,
};
});