UNPKG

@turnkey/react-wallet-kit

Version:

The easiest and most powerful way to integrate Turnkey's Embedded Wallets into your React applications.

510 lines (507 loc) 22.9 kB
import { slicedToArray as _slicedToArray, asyncToGenerator as _asyncToGenerator, regeneratorRuntime as _regeneratorRuntime } from '../../_virtual/_rollupPluginBabelHelpers.mjs'; import { jsxs, jsx } from 'react/jsx-runtime'; import { ActionButton } from '../design/Buttons.mjs'; import { useModal } from '../../providers/modal/Hook.mjs'; import { EthereumLogo, SolanaLogo } from '../design/Svg.mjs'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faClose, faChevronRight, faMobileScreen, faLaptop } from '@fortawesome/free-solid-svg-icons'; import { useEffect, useState, useRef, useMemo } from 'react'; import clsx from 'clsx'; import { QRCodeSVG } from 'qrcode.react'; import { SuccessPage } from '../design/Success.mjs'; import { isEthereumProvider } from '@turnkey/core'; import { useTurnkey } from '../../providers/client/Hook.mjs'; function WalletAuthButton(props) { var onContinue = props.onContinue; var _useState = useState(false), _useState2 = _slicedToArray(_useState, 2), isLoading = _useState2[0], setIsLoading = _useState2[1]; var handleContinue = /*#__PURE__*/function () { var _ref = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee() { return _regeneratorRuntime().wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: setIsLoading(true); _context.prev = 1; _context.next = 4; return Promise.resolve(onContinue()); case 4: _context.prev = 4; setIsLoading(false); return _context.finish(4); case 7: case "end": return _context.stop(); } }, _callee, null, [[1,, 4, 7]]); })); return function handleContinue() { return _ref.apply(this, arguments); }; }(); return jsx("div", { className: "flex flex-col w-full", children: jsx(ActionButton, { onClick: handleContinue, loading: isLoading, className: "w-full text-inherit bg-button-light dark:bg-button-dark", children: "Continue with wallet" }) }); } var canUnlink = function canUnlink(provider, shouldShowUnlink) { return shouldShowUnlink && provider.connectedAddresses && provider.connectedAddresses.length > 0; }; function ExternalWalletChainSelector(props) { var _providers$, _providers$0$info$nam, _providers$2; var providers = props.providers, onSelect = props.onSelect, onUnlink = props.onUnlink; var _useModal = useModal(), isMobile = _useModal.isMobile; var shouldShowUnlink = onUnlink !== undefined; var handleSelect = function handleSelect(provider) { if (canUnlink(provider, shouldShowUnlink)) { onUnlink(provider); } else { onSelect(provider); } }; return jsxs("div", { className: clsx("flex flex-col w-72 gap-4 mt-11 items-center justify-center", isMobile ? "w-full" : "w-72"), children: [jsx("img", { src: (_providers$ = providers[0]) === null || _providers$ === void 0 ? void 0 : _providers$.info.icon, className: "size-14 rounded-full" }), jsxs("span", { className: "text-sm text-center text-icon-text-light dark:text-icon-text-dark", children: [(_providers$0$info$nam = (_providers$2 = providers[0]) === null || _providers$2 === void 0 ? void 0 : _providers$2.info.name) !== null && _providers$0$info$nam !== void 0 ? _providers$0$info$nam : "This wallet provider", " supports multiple chains. Select which chain you would like to use."] }), jsx("div", { className: "w-full flex flex-col gap-2", children: providers.map(function (p) { var _p$connectedAddresses, _p$connectedAddresses2; var _useState3 = useState(false), _useState4 = _slicedToArray(_useState3, 2), isHovering = _useState4[0], setIsHovering = _useState4[1]; return jsxs(ActionButton, { onClick: function onClick() { return handleSelect(p); }, onMouseEnter: function onMouseEnter() { return setIsHovering(true); }, onMouseLeave: function onMouseLeave() { return setIsHovering(false); }, className: "relative overflow-hidden flex items-center justify-start gap-2 w-full text-inherit bg-button-light dark:bg-button-dark", children: [isEthereumProvider(p) ? jsxs("div", { className: "relative", children: [jsx(EthereumLogo, { className: "size-5" }), canUnlink(p, shouldShowUnlink) && jsx(ConnectedIndicator, { isPinging: true })] }) : jsxs("div", { className: "relative", children: [jsx(SolanaLogo, { className: "size-5" }), canUnlink(p, shouldShowUnlink) && jsx(ConnectedIndicator, { isPinging: true })] }), jsxs("div", { className: "flex flex-col items-start", children: [isEthereumProvider(p) ? "EVM" : "Solana", canUnlink(p, shouldShowUnlink) && jsxs("span", { className: "text-xs text-icon-text-light dark:text-icon-text-dark", children: ["Connected: ", (_p$connectedAddresses = p.connectedAddresses[0]) === null || _p$connectedAddresses === void 0 ? void 0 : _p$connectedAddresses.slice(0, 4), "...", (_p$connectedAddresses2 = p.connectedAddresses[0]) === null || _p$connectedAddresses2 === void 0 ? void 0 : _p$connectedAddresses2.slice(-3)] })] }), jsx(FontAwesomeIcon, { className: clsx("absolute transition-all duration-200", isHovering ? "right-4" : "-right-4", canUnlink(p, shouldShowUnlink) ? "text-danger-light dark:text-danger-dark" : "text-icon-text-light dark:text-icon-text-dark"), size: canUnlink(p, shouldShowUnlink) ? "lg" : "1x", icon: canUnlink(p, shouldShowUnlink) ? faClose : faChevronRight })] }, p.chainInfo.namespace); }) })] }); } function ExternalWalletSelector(props) { var providers = props.providers, onUnlink = props.onUnlink, onSelect = props.onSelect; var _useModal2 = useModal(), pushPage = _useModal2.pushPage, popPage = _useModal2.popPage, isMobile = _useModal2.isMobile; var shouldShowUnlink = onUnlink !== undefined; // Group providers by name var grouped = providers.reduce(function (acc, provider) { var name = provider.info.name; if (!acc[name]) acc[name] = []; acc[name].push(provider); return acc; }, {}); var handleSelectGroup = function handleSelectGroup(group) { if (group.length === 1) { if (canUnlink(group[0], shouldShowUnlink)) { onUnlink(group[0]); } else { onSelect(group[0]); } } else { pushPage({ key: "Select chain", content: jsx(ExternalWalletChainSelector, { providers: group, onUnlink: onUnlink, onSelect: onSelect }) }); } }; useEffect(function () { // Don't show the selector if there's only one wallet provider var groupedValues = Object.values(grouped); if (groupedValues.length === 1) { popPage(); // Remove this page from the stack so user doesn't get stuck in an infinite loop when pressing back handleSelectGroup(groupedValues[0]); } }, [grouped]); return Object.keys(grouped).length === 0 ? jsxs("div", { className: clsx("flex flex-col h-40 mt-4 gap-2 justify-center items-center text-xs text-center text-icon-text-light dark:text-icon-text-dark", isMobile ? "w-full" : "w-72"), children: [jsx("span", { className: "text-sm font-medium", children: "No wallet providers available." }), jsx("span", { children: "Only wallets supporting EIP-1193 (Ethereum) or the Solana wallet standard are supported." })] }) : jsx("div", { className: clsx("w-72 min-h-42 max-h-64 mt-12 overflow-y-auto tk-scrollbar p-0.5", isMobile ? "w-full" : "w-72"), children: jsx("div", { className: "flex flex-col gap-2", children: Object.entries(grouped).map(function (_ref2) { var _ref3 = _slicedToArray(_ref2, 2), name = _ref3[0], group = _ref3[1]; var _useState5 = useState(false), _useState6 = _slicedToArray(_useState5, 2), isHovering = _useState6[0], setIsHovering = _useState6[1]; var first = group[0]; return jsxs(ActionButton, { onMouseEnter: function onMouseEnter() { return setIsHovering(true); }, onMouseLeave: function onMouseLeave() { return setIsHovering(false); }, onClick: function onClick() { return handleSelectGroup(group); }, className: "relative flex items-center justify-between w-full text-inherit bg-button-light dark:bg-button-dark overflow-hidden", children: [jsxs("div", { className: "flex items-center gap-2 overflow-hidden", children: [jsx("img", { src: first === null || first === void 0 ? void 0 : first.info.icon, alt: first === null || first === void 0 ? void 0 : first.info.name, className: "size-6 rounded-full" }), first === null || first === void 0 ? void 0 : first.info.name] }), jsx("div", { className: clsx("flex items-center transition-all gap-1"), children: group.map(function (c, idx) { var Logo = isEthereumProvider(c) ? EthereumLogo : SolanaLogo; var delay = 50 + idx * 30; // Staggered delay: leftmost has largest return jsxs("div", { style: { transitionDelay: "".concat(delay, "ms") }, className: clsx("relative", "size-4", "transition-all duration-200", isHovering ? "-translate-x-8" : "translate-x-0"), children: [jsx(Logo, { className: "size-4" }), canUnlink(c, shouldShowUnlink) && jsx(ConnectedIndicator, { isPinging: isHovering })] }, c.chainInfo.namespace); }) }), jsx(FontAwesomeIcon, { className: clsx("absolute transition-all duration-200", isHovering ? "right-4" : "-right-4", group.length === 1 && canUnlink(group[0], shouldShowUnlink) ? "text-danger-light dark:text-danger-dark" : "text-icon-text-light dark:text-icon-text-dark"), size: group.length === 1 && canUnlink(group[0], shouldShowUnlink) ? "lg" : "1x", icon: group.length === 1 && canUnlink(group[0], shouldShowUnlink) ? faClose : faChevronRight })] }, name); }) }) }); } function UnlinkWalletScreen(props) { var _provider$info$icon; var provider = props.provider, onUnlink = props.onUnlink; var _useModal3 = useModal(), isMobile = _useModal3.isMobile; var _useState7 = useState(false), _useState8 = _slicedToArray(_useState7, 2), isLoading = _useState8[0], setIsLoading = _useState8[1]; var _useState9 = useState(false), _useState0 = _slicedToArray(_useState9, 2), hasError = _useState0[0], setHasError = _useState0[1]; var handleUnlink = /*#__PURE__*/function () { var _ref4 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee2() { return _regeneratorRuntime().wrap(function _callee2$(_context2) { while (1) switch (_context2.prev = _context2.next) { case 0: setIsLoading(true); setHasError(false); _context2.prev = 2; _context2.next = 5; return onUnlink(provider); case 5: _context2.next = 10; break; case 7: _context2.prev = 7; _context2.t0 = _context2["catch"](2); setHasError(true); case 10: _context2.prev = 10; setIsLoading(false); return _context2.finish(10); case 13: case "end": return _context2.stop(); } }, _callee2, null, [[2, 7, 10, 13]]); })); return function handleUnlink() { return _ref4.apply(this, arguments); }; }(); return jsxs("div", { className: clsx("mt-8", isMobile ? "w-full" : "w-96"), children: [jsxs("div", { className: "mt-6 mb-5 flex flex-col items-center gap-3", children: [jsx("img", { src: (_provider$info$icon = provider.info.icon) !== null && _provider$info$icon !== void 0 ? _provider$info$icon : "", className: "size-14 rounded-full" }), jsx("div", { className: clsx("text-2xl font-bold text-center", hasError && "text-danger-light dark:text-danger-dark"), children: hasError ? "You can't unlink this wallet!" : "Unlink ".concat(provider.info.name) }), jsx("div", { className: "text-icon-text-light dark:text-icon-text-dark text-center !p-0", children: hasError ? "Try unlinking directly from the ".concat(provider.info.name, " app") : "You can always link this wallet again later." })] }), jsx("div", { className: "flex my-2 mt-0", children: jsx(ActionButton, { onClick: handleUnlink, loading: isLoading, className: clsx("w-full max-w-md bg-danger-light dark:bg-danger-dark text-primary-text-light dark:text-primary-text-dark", hasError && "animate-shake opacity-50"), spinnerClassName: "text-primary-danger-text-light dark:text-primary-danger-text-dark", children: "Unlink Wallet" }) })] }); } function ConnectedIndicator(props) { var _props$isPinging = props.isPinging, isPinging = _props$isPinging === void 0 ? false : _props$isPinging; return jsxs("div", { className: "flex absolute top-[-2px] right-0", children: [isPinging && jsx("div", { className: "absolute animate-ping size-[6px] bg-success-light dark:bg-success-dark rounded-full border border-modal-background-light dark:border-modal-background-dark" }), jsx("div", { className: "size-[6px] bg-success-light dark:bg-success-dark rounded-full border border-modal-background-light dark:border-modal-background-dark" })] }); } function WalletConnectScreen(props) { var _walletConnectProvide; var provider = props.provider, successPageDuration = props.successPageDuration, onAction = props.onAction, onDisconnect = props.onDisconnect; var _useModal4 = useModal(), pushPage = _useModal4.pushPage, closeModal = _useModal4.closeModal, isMobile = _useModal4.isMobile; var _useTurnkey = useTurnkey(), getWalletProviders = _useTurnkey.getWalletProviders; var hasRan = useRef(false); var _useState1 = useState(), _useState10 = _slicedToArray(_useState1, 2), walletConnectProvider = _useState10[0], setWalletConnectProvider = _useState10[1]; var connectedAccount = walletConnectProvider === null || walletConnectProvider === void 0 ? void 0 : walletConnectProvider.connectedAddresses[0]; useEffect(function () { setWalletConnectProvider(provider); }, [provider]); var _useState11 = useState(false), _useState12 = _slicedToArray(_useState11, 2), isUnlinking = _useState12[0], setIsUnlinking = _useState12[1]; var _useState13 = useState(false), _useState14 = _slicedToArray(_useState13, 2), unlinkError = _useState14[0], setUnlinkError = _useState14[1]; // kick off authentication/pairing or signing on mount or when URI changes useMemo(function () { _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee3() { return _regeneratorRuntime().wrap(function _callee3$(_context3) { while (1) switch (_context3.prev = _context3.next) { case 0: if (!hasRan.current) { _context3.next = 2; break; } return _context3.abrupt("return"); case 2: _context3.prev = 2; _context3.next = 5; return onAction(walletConnectProvider !== null && walletConnectProvider !== void 0 ? walletConnectProvider : provider); case 5: hasRan.current = true; pushPage({ key: "Link Success", content: jsx(SuccessPage, { text: "Successfully linked WalletConnect!", onComplete: closeModal, duration: successPageDuration }), preventBack: true, showTitle: false }); _context3.next = 11; break; case 9: _context3.prev = 9; _context3.t0 = _context3["catch"](2); case 11: case "end": return _context3.stop(); } }, _callee3, null, [[2, 9]]); }))(); }, [walletConnectProvider === null || walletConnectProvider === void 0 ? void 0 : walletConnectProvider.uri]); var handleUnlink = /*#__PURE__*/function () { var _ref6 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee4() { var newProviders; return _regeneratorRuntime().wrap(function _callee4$(_context4) { while (1) switch (_context4.prev = _context4.next) { case 0: setIsUnlinking(true); setUnlinkError(false); _context4.prev = 2; _context4.next = 5; return onDisconnect === null || onDisconnect === void 0 ? void 0 : onDisconnect(walletConnectProvider !== null && walletConnectProvider !== void 0 ? walletConnectProvider : provider); case 5: _context4.next = 7; return getWalletProviders(); case 7: newProviders = _context4.sent; setWalletConnectProvider(newProviders.find(function (p) { return p.interfaceType === provider.interfaceType; })); _context4.next = 14; break; case 11: _context4.prev = 11; _context4.t0 = _context4["catch"](2); setUnlinkError(true); case 14: _context4.prev = 14; setIsUnlinking(false); return _context4.finish(14); case 17: case "end": return _context4.stop(); } }, _callee4, null, [[2, 11, 14, 17]]); })); return function handleUnlink() { return _ref6.apply(this, arguments); }; }(); return jsx("div", { className: "p-3 flex flex-col items-center", children: connectedAccount ? jsxs("div", { className: clsx("mt-8 flex flex-col items-center gap-3", isMobile ? "w-full" : "w-96"), children: [jsxs("div", { className: "w-full justify-between flex items-center flex-1", children: [jsx("div", { className: clsx("flex items-center justify-center bg-icon-background-light dark:bg-icon-background-dark rounded-full p-2 text-icon-text-light dark:text-icon-text-dark", isMobile ? "size-18" : "size-24"), children: jsx(FontAwesomeIcon, { icon: faMobileScreen, size: isMobile ? "3x" : "4x" }) }), jsxs("div", { className: "flex flex-row items-center justify-center space-x-2.5 font-medium text-icon-text-light dark:text-icon-text-dark", children: [jsxs("div", { className: "flex items-center justify-center", children: [jsx("img", { className: "size-5", src: walletConnectProvider.info.icon, alt: "Wallet connect logo" }), jsx("img", { className: "size-5 absolute animate-ping", src: walletConnectProvider.info.icon, alt: "Wallet connect logo" })] }), jsxs("span", { children: [connectedAccount === null || connectedAccount === void 0 ? void 0 : connectedAccount.slice(0, 3), "...", connectedAccount === null || connectedAccount === void 0 ? void 0 : connectedAccount.slice(-3)] })] }), jsx("div", { className: clsx("flex items-center justify-center bg-icon-background-light dark:bg-icon-background-dark rounded-full p-2 text-icon-text-light dark:text-icon-text-dark", isMobile ? "size-18" : "size-24"), children: jsx(FontAwesomeIcon, { icon: faLaptop, size: isMobile ? "3x" : "4x" }) })] }), jsx("div", { className: clsx("flex flex-row items-center mt-5 text-2xl font-bold text-center"), children: "Already connected" }), jsxs("div", { className: "text-icon-text-light dark:text-icon-text-dark text-center text-xs flex flex-col space-y-2 !p-0", children: [jsx("span", { children: "Please open the wallet app on your phone to sign the message." }), isUnlinking ? jsx("span", { className: "text-danger-light dark:text-danger-dark opacity-50", children: "Unlinking..." }) : unlinkError ? jsx("span", { className: "text-danger-light dark:text-danger-dark opacity-50", children: "Error unlinking wallet." }) : jsxs("span", { children: ["Need to connect a different wallet?", " ", jsx("span", { className: "text-danger-light dark:text-danger-dark cursor-pointer underline", onClick: handleUnlink, children: "Unlink" }), " ", "this wallet first."] })] })] }) : jsxs("div", { className: clsx("mt-8 flex flex-col items-center gap-3", isMobile ? "w-full" : "w-96"), children: [(walletConnectProvider === null || walletConnectProvider === void 0 ? void 0 : walletConnectProvider.uri) && // @ts-expect-error: qrcode.react uses a different React type version jsx(QRCodeSVG, { className: " \n border border-modal-background-dark/20 dark:border-modal-background-light/20\n shadow-[0_0_42px] shadow-primary-light/50\n dark:shadow-[0_0_42px] dark:shadow-primary-dark/50", value: walletConnectProvider.uri, imageSettings: { src: (_walletConnectProvide = walletConnectProvider.info.icon) !== null && _walletConnectProvide !== void 0 ? _walletConnectProvide : "", width: 24, height: 24, excavate: true }, size: 200 }), jsx("div", { className: clsx("text-2xl font-bold text-center"), children: "Use your phone" }), jsx("div", { className: "text-icon-text-light dark:text-icon-text-dark text-center !p-0", children: "Scan this QR code with your WalletConnect-compatible wallet to link" })] }) }); } export { ConnectedIndicator, ExternalWalletChainSelector, ExternalWalletSelector, UnlinkWalletScreen, WalletAuthButton, WalletConnectScreen }; //# sourceMappingURL=Wallet.mjs.map