@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
JavaScript
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