'use client';
import { Avatar, Name } from '@/identity';
import { ReactNode, useCallback, useContext, useMemo } from 'react';
import { useEffect, useState } from 'react';
import { useAccount, useConnect } from 'wagmi';
import { useAnalytics } from '@/core/analytics/hooks/useAnalytics';
import { WalletEvent } from '@/core/analytics/types';
import { IdentityProvider } from '@/identity/components/IdentityProvider';
import { Spinner } from '@/internal/components/Spinner';
import { cn, pressable, text } from '../../styles/theme';
import { useOnchainKit } from '@/useOnchainKit';
import { WalletModal } from './WalletModal';
import {
useWalletContext,
WalletContext,
WalletProvider,
type WalletContextType,
} from './WalletProvider';
import { WithRenderProps } from '@/internal/types';
import { MiniKitContext } from '@/minikit/MiniKitProvider';
import { Button } from '@/ui/Button';
import { IfInMiniApp } from '@/minikit/components/IfInMiniApp';
import { useMiniKit } from '@/minikit';
export type ConnectWalletProps = WithRenderProps<{
/** Children can be utilized to display customized content when the wallet is connected. */
children?: ReactNode;
/** Optional className override for button element */
className?: string;
/** Optional callback function to execute when the wallet is connected. */
onConnect?: () => void;
/** Optional disconnected display override */
disconnectedLabel?: ReactNode;
/** Custom render function for complete control of button rendering */
render?: ({
label,
onClick,
context,
status,
isLoading,
}: {
label: ReactNode;
onClick: () => void;
context: WalletContextType;
status: 'disconnected' | 'connecting' | 'connected';
isLoading: boolean;
}) => ReactNode;
}>;
function MiniAppDefaultChildren() {
const { context } = useMiniKit();
return (
<>
>
);
}
function ConnectWalletContent({
children = (
>
}
>
),
className,
onConnect,
disconnectedLabel = 'Connect Wallet',
render,
}: ConnectWalletProps) {
const { config = { wallet: { display: undefined } } } = useOnchainKit();
const walletContext = useWalletContext();
const {
isConnectModalOpen,
setIsConnectModalOpen,
isSubComponentOpen,
setIsSubComponentOpen,
handleClose,
} = walletContext;
const {
address: accountAddress,
status,
connector: accountConnector,
} = useAccount();
const { connectors, connect, status: connectStatus } = useConnect();
const { sendAnalytics } = useAnalytics();
const [hasClickedConnect, setHasClickedConnect] = useState(false);
const miniKitContext = useContext(MiniKitContext);
const isWalletModalEnabled = useMemo(() => {
return config?.wallet?.display === 'modal' && !miniKitContext?.context;
}, [config?.wallet?.display, miniKitContext?.context]);
const connector = accountConnector || connectors[0];
const isLoading = connectStatus === 'pending' || status === 'connecting';
const handleToggle = useCallback(() => {
if (isSubComponentOpen) {
handleClose();
} else {
setIsSubComponentOpen(true);
}
}, [isSubComponentOpen, handleClose, setIsSubComponentOpen]);
const handleCloseConnectModal = useCallback(() => {
setIsConnectModalOpen(false);
}, [setIsConnectModalOpen]);
const handleOpenConnectModal = useCallback(() => {
setIsConnectModalOpen(true);
setHasClickedConnect(true);
}, [setIsConnectModalOpen]);
const handleAnalyticsSuccess = useCallback(() => {
if (!accountAddress || !connector) return;
sendAnalytics(WalletEvent.ConnectSuccess, {
address: accountAddress,
walletProvider: connector?.name,
});
}, [sendAnalytics, connector, accountAddress]);
const handleAnalyticsError = useCallback(
(errorMessage: string, component: string) => {
const walletProvider = connector?.name;
sendAnalytics(WalletEvent.ConnectError, {
error: errorMessage,
metadata: {
connector: walletProvider,
component,
},
});
},
[sendAnalytics, connector],
);
useEffect(() => {
if (status !== 'connected') return;
if (hasClickedConnect && onConnect) {
onConnect();
setHasClickedConnect(false);
}
handleAnalyticsSuccess();
}, [
status,
hasClickedConnect,
onConnect,
accountAddress,
connector,
handleAnalyticsSuccess,
]);
const handleConnectClick = useCallback(() => {
if (isWalletModalEnabled) {
handleOpenConnectModal();
setHasClickedConnect(true);
sendAnalytics(WalletEvent.ConnectInitiated, {
component: 'WalletModal',
});
return;
}
sendAnalytics(WalletEvent.ConnectInitiated, {
component: 'ConnectWallet',
});
connect(
{ connector },
{
onSuccess: () => {
onConnect?.();
handleAnalyticsSuccess();
},
onError: (error) => {
handleAnalyticsError(error.message, 'ConnectWallet');
},
},
);
}, [
isWalletModalEnabled,
connect,
connector,
handleAnalyticsError,
handleAnalyticsSuccess,
handleOpenConnectModal,
onConnect,
sendAnalytics,
]);
const buttonContent = useMemo(() => {
if (isLoading) return ;
if (status === 'disconnected') return disconnectedLabel;
return (
{children}
);
}, [isLoading, status, disconnectedLabel, children]);
if (render) {
return (
);
}
if (status === 'disconnected') {
return (
{isWalletModalEnabled && (
)}
);
}
if (isLoading) {
return (
);
}
return (
);
}
function ConnectWalletRenderHandler({
label,
onClick,
isLoading,
render,
isWalletModalEnabled,
}: {
label: ReactNode;
onClick: () => void;
isLoading: boolean;
render: NonNullable;
isWalletModalEnabled: boolean;
}) {
const walletContext = useWalletContext();
const { isConnectModalOpen, setIsConnectModalOpen } = walletContext;
const { status, address } = useAccount();
if (status === 'disconnected') {
return (
<>
{render({
label,
onClick,
context: walletContext,
status: 'disconnected',
isLoading,
})}
{isWalletModalEnabled && (
setIsConnectModalOpen(false)}
/>
)}
>
);
}
if (isLoading) {
return render({
label: ,
onClick,
context: walletContext,
status: 'connecting',
isLoading,
});
}
return (
{render({
label,
onClick,
context: walletContext,
status: 'connected',
isLoading,
})}
);
}
export function ConnectWallet(props: ConnectWalletProps) {
// Using `useContext` because `useWalletContext` will throw if there is no
// Provider up the tree.
const walletContext = useContext(WalletContext);
if (!walletContext) {
return (
);
}
return ;
}