@daimo/pay
Version:
Seamless crypto payments. Onboard users from any chain, any coin into your app with one click.
272 lines (269 loc) • 12.7 kB
JavaScript
import { jsx } from 'react/jsx-runtime';
import { useWallet } from '@solana/wallet-adapter-react';
import { useEffect } from 'react';
import { useAccount } from 'wagmi';
import { ROUTES } from '../../constants/routes.js';
import { getAppName } from '../../defaultConfig.js';
import { useChainIsSupported } from '../../hooks/useChainIsSupported.js';
import { useDaimoPay } from '../../hooks/useDaimoPay.js';
import useIsMobile from '../../hooks/useIsMobile.js';
import { usePayContext } from '../../hooks/usePayContext.js';
import Modal from '../Common/Modal/index.js';
import { DaimoPayThemeProvider } from '../DaimoPayThemeProvider/DaimoPayThemeProvider.js';
import About from '../Pages/About/index.js';
import Confirmation from '../Pages/Confirmation/index.js';
import Wallets from '../Pages/Connectors/index.js';
import DownloadApp from '../Pages/DownloadApp/index.js';
import ErrorPage from '../Pages/Error/index.js';
import MobileConnectors from '../Pages/MobileConnectors/index.js';
import Introduction from '../Pages/Onboarding/index.js';
import PayWithToken from '../Pages/PayWithToken/index.js';
import SelectAmount from '../Pages/SelectAmount/index.js';
import SelectDepositAddressAmount from '../Pages/SelectDepositAddressAmount/index.js';
import SelectDepositAddressChain from '../Pages/SelectDepositAddressChain/index.js';
import SelectExchange from '../Pages/SelectExchange/index.js';
import SelectExternalAmount from '../Pages/SelectExternalAmount/index.js';
import SelectMethod from '../Pages/SelectMethod/index.js';
import SelectToken from '../Pages/SelectToken/index.js';
import SelectWalletAmount from '../Pages/SelectWalletAmount/index.js';
import SelectWalletChain from '../Pages/SelectWalletChain/index.js';
import SelectZKP from '../Pages/SelectZKP/index.js';
import ConnectSolana from '../Pages/Solana/ConnectorSolana/index.js';
import PayWithSolanaToken from '../Pages/Solana/PayWithSolanaToken/index.js';
import SelectSolanaAmount from '../Pages/Solana/SelectSolanaAmount/index.js';
import SwitchNetworks from '../Pages/SwitchNetworks/index.js';
import WaitingDepositAddress from '../Pages/WaitingDepositAddress/index.js';
import WaitingExternal from '../Pages/WaitingExternal/index.js';
import WaitingWallet from '../Pages/WaitingWallet/index.js';
import ConnectUsing from './ConnectUsing.js';
const DaimoPayModal = ({ mode, theme, customTheme, lang, disableMobileInjector, }) => {
const context = usePayContext();
const { setMode, setTheme, setCustomTheme, setLang, setDisableMobileInjector, } = context;
const paymentState = context.paymentState;
const { generatePreviewOrder, isDepositFlow, showSolanaPaymentMethod, setPaymentWaitingMessage, setSelectedExternalOption, setSelectedTokenOption, setSelectedSolanaTokenOption, setSelectedDepositAddressOption, setSelectedWallet, } = paymentState;
const { paymentState: paymentFsmState } = useDaimoPay();
const { isConnected: isEthConnected, connector, chain, address, } = useAccount();
const { connected: isSolanaConnected } = useWallet();
const chainIsSupported = useChainIsSupported(chain?.id);
//if chain is unsupported we enforce a "switch chain" prompt
const closeable = !(context.options?.enforceSupportedChains &&
isEthConnected &&
!chainIsSupported);
const showBackButton = closeable &&
context.route !== ROUTES.SELECT_METHOD &&
context.route !== ROUTES.CONFIRMATION &&
context.route !== ROUTES.SELECT_TOKEN &&
context.route !== ROUTES.ERROR &&
paymentFsmState !== "error";
const onBack = () => {
const meta = { event: "click-back" };
if (context.route === ROUTES.DOWNLOAD) {
context.setRoute(ROUTES.CONNECT, meta);
}
else if (context.route === ROUTES.CONNECTORS) {
context.setRoute(ROUTES.SELECT_METHOD, meta);
}
else if (context.route === ROUTES.SELECT_AMOUNT) {
setSelectedTokenOption(undefined);
context.setRoute(ROUTES.SELECT_TOKEN, meta);
}
else if (context.route === ROUTES.SELECT_EXTERNAL_AMOUNT) {
setSelectedExternalOption(undefined);
context.setRoute(ROUTES.SELECT_METHOD, meta);
}
else if (context.route === ROUTES.SELECT_DEPOSIT_ADDRESS_AMOUNT) {
setSelectedDepositAddressOption(undefined);
context.setRoute(ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN, meta);
}
else if (context.route === ROUTES.SELECT_ZKP2P) {
context.setRoute(ROUTES.SELECT_METHOD, meta);
}
else if (context.route === ROUTES.WAITING_EXTERNAL) {
setPaymentWaitingMessage(undefined);
if (isDepositFlow) {
generatePreviewOrder();
context.setRoute(ROUTES.SELECT_EXTERNAL_AMOUNT, meta);
}
else {
setSelectedExternalOption(undefined);
context.setRoute(ROUTES.SELECT_METHOD, meta);
}
}
else if (context.route === ROUTES.PAY_WITH_TOKEN) {
if (isDepositFlow) {
generatePreviewOrder();
context.setRoute(ROUTES.SELECT_AMOUNT, meta);
}
else {
setSelectedTokenOption(undefined);
context.setRoute(ROUTES.SELECT_TOKEN, meta);
}
}
else if (context.route === ROUTES.ONBOARDING) {
context.setRoute(ROUTES.CONNECTORS, meta);
}
else if (context.route === ROUTES.WAITING_DEPOSIT_ADDRESS) {
if (isDepositFlow) {
if (paymentState.selectedDepositAddressOption === undefined) {
context.setRoute(ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN, meta);
}
else {
generatePreviewOrder();
context.setRoute(ROUTES.SELECT_DEPOSIT_ADDRESS_AMOUNT, meta);
}
}
else {
setSelectedDepositAddressOption(undefined);
context.setRoute(ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN, meta);
}
}
else if (context.route === ROUTES.WAITING_WALLET) {
if (isDepositFlow) {
generatePreviewOrder();
context.setRoute(ROUTES.SELECT_WALLET_AMOUNT, meta);
}
else {
setSelectedWallet(undefined);
context.setRoute(ROUTES.SELECT_METHOD, meta);
}
}
else if (context.route === ROUTES.SOLANA_SELECT_AMOUNT) {
setSelectedSolanaTokenOption(undefined);
context.setRoute(ROUTES.SELECT_TOKEN, meta);
}
else if (context.route === ROUTES.SOLANA_PAY_WITH_TOKEN) {
if (isDepositFlow) {
generatePreviewOrder();
context.setRoute(ROUTES.SOLANA_SELECT_AMOUNT, meta);
}
else {
setSelectedSolanaTokenOption(undefined);
context.setRoute(ROUTES.SELECT_TOKEN, meta);
}
}
else {
context.setRoute(ROUTES.SELECT_METHOD, meta);
}
};
const pages = {
[ROUTES.SELECT_METHOD]: jsx(SelectMethod, {}),
[ROUTES.SELECT_TOKEN]: jsx(SelectToken, {}),
[ROUTES.SELECT_AMOUNT]: jsx(SelectAmount, {}),
[ROUTES.SELECT_EXTERNAL_AMOUNT]: jsx(SelectExternalAmount, {}),
[ROUTES.SELECT_EXCHANGE]: jsx(SelectExchange, {}),
[ROUTES.SELECT_DEPOSIT_ADDRESS_AMOUNT]: jsx(SelectDepositAddressAmount, {}),
[ROUTES.SELECT_WALLET_AMOUNT]: jsx(SelectWalletAmount, {}),
[ROUTES.SELECT_WALLET_CHAIN]: jsx(SelectWalletChain, {}),
[ROUTES.WAITING_EXTERNAL]: jsx(WaitingExternal, {}),
[ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN]: jsx(SelectDepositAddressChain, {}),
[ROUTES.WAITING_DEPOSIT_ADDRESS]: jsx(WaitingDepositAddress, {}),
[ROUTES.SELECT_ZKP2P]: jsx(SelectZKP, {}),
[ROUTES.WAITING_WALLET]: jsx(WaitingWallet, {}),
[ROUTES.CONFIRMATION]: jsx(Confirmation, {}),
[ROUTES.ERROR]: jsx(ErrorPage, {}),
[ROUTES.PAY_WITH_TOKEN]: jsx(PayWithToken, {}),
[ROUTES.SOLANA_CONNECTOR]: jsx(ConnectSolana, {}),
[ROUTES.SOLANA_SELECT_AMOUNT]: jsx(SelectSolanaAmount, {}),
[ROUTES.SOLANA_PAY_WITH_TOKEN]: jsx(PayWithSolanaToken, {}),
// Unused routes. Kept to minimize connectkit merge conflicts.
[ROUTES.ONBOARDING]: jsx(Introduction, {}),
[ROUTES.ABOUT]: jsx(About, {}),
[ROUTES.DOWNLOAD]: jsx(DownloadApp, {}),
[ROUTES.CONNECTORS]: jsx(Wallets, {}),
[ROUTES.MOBILECONNECTORS]: jsx(MobileConnectors, {}),
[ROUTES.CONNECT]: jsx(ConnectUsing, {}),
[ROUTES.SWITCHNETWORKS]: jsx(SwitchNetworks, {}),
};
function hide() {
if (isDepositFlow) {
generatePreviewOrder();
}
context.setOpen(false, { event: "click-close" });
}
const { isMobile } = useIsMobile();
// If the user has a wallet already connected upon opening the modal, go
// straight to the select token screen
useEffect(() => {
if (!context.open)
return;
if (context.route !== ROUTES.SELECT_METHOD)
return;
// Skip to token selection if exactly one wallet is connected. If both
// wallets are connected, stay on the SELECT_METHOD screen to allow the
// user to select which wallet to use
// If mobile injector is disabled, don't show the connected wallets.
if (isEthConnected &&
!isSolanaConnected &&
(!isMobile || !disableMobileInjector)) {
paymentState.setTokenMode("evm");
context.setRoute(ROUTES.SELECT_TOKEN, {
event: "eth_connected_on_open",
walletId: connector?.id,
chainId: chain?.id,
address,
});
}
else if (isSolanaConnected &&
!isEthConnected &&
showSolanaPaymentMethod &&
!disableMobileInjector) {
paymentState.setTokenMode("solana");
context.setRoute(ROUTES.SELECT_TOKEN, {
event: "solana_connected_on_open",
});
}
// Don't include context.route in the dependency array otherwise the user
// can't go back from the select token screen to the select method screen
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
context.open,
paymentState.walletPaymentOptions.options,
paymentState.solanaPaymentOptions.options,
showSolanaPaymentMethod,
address,
chain?.id,
connector?.id,
]);
// If we're on the connect page and the user successfully connects their
// wallet, go to the select token page
useEffect(() => {
if (context.route === ROUTES.CONNECT ||
context.route === ROUTES.CONNECTORS ||
context.route === ROUTES.MOBILECONNECTORS) {
if (isEthConnected) {
paymentState.setTokenMode("evm");
context.setRoute(ROUTES.SELECT_TOKEN, {
event: "connected",
walletId: connector?.id,
chainId: chain?.id,
address,
});
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isEthConnected, context.route, connector?.id, chain?.id, address]);
useEffect(() => setMode(mode), [mode, setMode]);
useEffect(() => setTheme(theme), [theme, setTheme]);
useEffect(() => setCustomTheme(customTheme), [customTheme, setCustomTheme]);
useEffect(() => setLang(lang), [lang, setLang]);
useEffect(() => setDisableMobileInjector(disableMobileInjector), [disableMobileInjector, setDisableMobileInjector]);
useEffect(() => {
const appName = getAppName();
if (!appName || !context.open)
return;
const title = document.createElement("meta");
title.setAttribute("property", "og:title");
title.setAttribute("content", appName);
document.head.prepend(title);
return () => {
try {
document.head.removeChild(title);
}
catch { }
//if (appIcon) document.head.removeChild(icon);
};
}, [context.open]);
return (jsx(DaimoPayThemeProvider, { theme: theme, customTheme: customTheme, mode: mode, children: jsx(Modal, { open: context.open, pages: pages, pageId: context.route, onClose: closeable ? hide : undefined, onInfo: undefined, onBack: showBackButton ? onBack : undefined }) }));
};
export { DaimoPayModal };
//# sourceMappingURL=index.js.map