UNPKG

@turnkey/react-wallet-kit

Version:

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

300 lines (297 loc) 12.1 kB
import { slicedToArray as _slicedToArray, asyncToGenerator as _asyncToGenerator, regenerator as _regenerator, objectSpread2 as _objectSpread2 } from '../../../_virtual/_rollupPluginBabelHelpers.mjs'; import { jsx, jsxs } from 'react/jsx-runtime'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faQrcode } from '@fortawesome/free-solid-svg-icons'; import { useState, useRef, useMemo, useCallback, useEffect } from 'react'; import clsx from 'clsx'; import { Chain } from '@turnkey/core'; import { useModal } from '../../../providers/modal/Hook.mjs'; import { useTurnkey } from '../../../providers/client/Hook.mjs'; import { useWalletConnect } from '../../../providers/WalletConnectProvider.mjs'; import { useDebouncedCallback, findWalletConnectProvider } from '../../../utils/utils.mjs'; import { WalletButton } from './WalletButton.mjs'; import { WalletConnectAppChainSelector } from './WalletConnectAppChainSelector.mjs'; import { SearchInputBox } from '../../design/Inputs.mjs'; import { Spinner } from '../../design/Spinners.mjs'; import { INITIAL_VISIBLE_ROWS, ROW_STRIDE, BUFFER_SIZE } from './constants.mjs'; import { WalletButtonSkeleton } from './WalletButtonSkeleton.mjs'; var sameRange = function sameRange(a, b) { return a.start === b.start && a.end === b.end; }; function ShowAllWalletsScreen(props) { var onSelect = props.onSelect, onSelectQRCode = props.onSelectQRCode; var _useTurnkey = useTurnkey(), walletProviders = _useTurnkey.walletProviders, disconnectWalletAccount = _useTurnkey.disconnectWalletAccount; var _useWalletConnect = useWalletConnect(), walletConnectApps = _useWalletConnect.walletConnectApps, isLoadingApps = _useWalletConnect.isLoadingApps; var _useModal = useModal(), isMobile = _useModal.isMobile, pushPage = _useModal.pushPage, popPage = _useModal.popPage; var _useState = useState(""), _useState2 = _slicedToArray(_useState, 2), searchQuery = _useState2[0], setSearchQuery = _useState2[1]; // renderRange (buffered, debounced) governs which real WalletButtons are mounted. // viewportRange (unbuffered, rAF-synchronous) governs the skeleton layer behind them. var _useState3 = useState({ start: 0, end: INITIAL_VISIBLE_ROWS }), _useState4 = _slicedToArray(_useState3, 2), renderRange = _useState4[0], setRenderRange = _useState4[1]; var _useState5 = useState({ start: 0, end: INITIAL_VISIBLE_ROWS }), _useState6 = _slicedToArray(_useState5, 2), viewportRange = _useState6[0], setViewportRange = _useState6[1]; var scrollContainerRef = useRef(null); var rafIdRef = useRef(null); // Group wallet apps by id (same wallet across different chains) var grouped = useMemo(function () { return walletConnectApps.reduce(function (acc, app) { if (!acc[app.id]) acc[app.id] = []; acc[app.id].push(app); return acc; }, {}); }, [walletConnectApps]); // Filter by search query var walletEntries = useMemo(function () { var entries = Object.entries(grouped); if (!searchQuery.trim()) return entries; var query = searchQuery.toLowerCase(); return entries.filter(function (_ref) { var _group$0$name$toLower, _group$, _group$$name; var _ref2 = _slicedToArray(_ref, 2); _ref2[0]; var group = _ref2[1]; var walletName = (_group$0$name$toLower = (_group$ = group[0]) === null || _group$ === void 0 ? void 0 : (_group$$name = _group$.name) === null || _group$$name === void 0 ? void 0 : _group$$name.toLowerCase()) !== null && _group$0$name$toLower !== void 0 ? _group$0$name$toLower : ""; return walletName.includes(query); }); }, [grouped, searchQuery]); // The QR-code row takes the slot at global row 0 when not searching. // renderRange/viewportRange are in *global* row indices, so wallet entry i // lives at global row (qrRowCount + i). var qrRowCount = searchQuery ? 0 : 1; var totalRows = walletEntries.length + qrRowCount; /** * Merges WalletConnectAppEntry (display info) with the actual WalletProvider * and triggers selection. Disconnects any existing session first. */ var selectApp = useCallback(/*#__PURE__*/function () { var _ref3 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(targetApp) { var provider, mergedProvider; return _regenerator().w(function (_context) { while (1) switch (_context.n) { case 0: provider = findWalletConnectProvider(walletProviders, targetApp.chain); if (provider) { _context.n = 1; break; } // WalletConnect provider not ready - go back popPage(); return _context.a(2); case 1: if (!(provider.connectedAddresses.length > 0)) { _context.n = 2; break; } _context.n = 2; return disconnectWalletAccount(provider); case 2: // Merge app display info with the actual provider mergedProvider = _objectSpread2(_objectSpread2({}, provider), {}, { uri: targetApp.uri, info: _objectSpread2(_objectSpread2({}, provider.info), {}, { name: targetApp.name, icon: targetApp.icon }) }); _context.n = 3; return onSelect(mergedProvider); case 3: return _context.a(2); } }, _callee); })); return function (_x) { return _ref3.apply(this, arguments); }; }(), [walletProviders, disconnectWalletAccount, onSelect, popPage]); var handleSelectGroup = useCallback(function (group) { if (group.length === 1) { selectApp(group[0]); } else { pushPage({ key: "Select chain", content: jsx(WalletConnectAppChainSelector, { apps: group, onSelect: selectApp }) }); } }, [selectApp, pushPage]); var computeRange = useCallback(function (buffer) { if (!scrollContainerRef.current) return null; var _scrollContainerRef$c = scrollContainerRef.current, scrollTop = _scrollContainerRef$c.scrollTop, clientHeight = _scrollContainerRef$c.clientHeight; return { start: Math.max(0, Math.floor(scrollTop / ROW_STRIDE) - buffer), end: Math.min(totalRows, Math.ceil((scrollTop + clientHeight) / ROW_STRIDE) + buffer) }; }, [totalRows]); var handleScroll = useDebouncedCallback(function () { var next = computeRange(BUFFER_SIZE); if (next) setRenderRange(function (prev) { return sameRange(prev, next) ? prev : next; }); }, 50); var onScroll = useCallback(function () { if (rafIdRef.current === null) { rafIdRef.current = requestAnimationFrame(function () { rafIdRef.current = null; var next = computeRange(0); if (next) setViewportRange(function (prev) { return sameRange(prev, next) ? prev : next; }); }); } handleScroll(); }, [computeRange, handleScroll]); useEffect(function () { return function () { if (rafIdRef.current !== null) { cancelAnimationFrame(rafIdRef.current); } }; }, []); // Reset scroll when search results change (or QR row toggles) useEffect(function () { var initial = { start: 0, end: Math.min(INITIAL_VISIBLE_ROWS, totalRows) }; setRenderRange(initial); setViewportRange(initial); if (scrollContainerRef.current) { scrollContainerRef.current.scrollTop = 0; } }, [totalRows]); var debouncedSetSearch = useDebouncedCallback(function (value) { return setSearchQuery(value); }, 300); // renderRange is in global row indices; subtract qrRowCount to slice walletEntries var visibleItems = useMemo(function () { var sliceStart = Math.max(0, renderRange.start - qrRowCount); var sliceEnd = Math.max(0, renderRange.end - qrRowCount); return walletEntries.slice(sliceStart, sliceEnd); }, [walletEntries, renderRange, qrRowCount]); var totalHeight = totalRows * ROW_STRIDE; var offsetY = renderRange.start * ROW_STRIDE; if (isLoadingApps) { return jsx("div", { className: "flex flex-col items-center py-5", children: jsx(Spinner, { strokeWidth: 2, className: "w-12 h-12" }) }); } return jsxs("div", { className: clsx("flex flex-col mt-10 gap-3", isMobile ? "w-full" : "w-80"), children: [jsx(SearchInputBox, { value: searchQuery, onChange: debouncedSetSearch, onClear: function onClear() { return setSearchQuery(""); }, placeholder: "Search wallets..." }), jsxs("div", { ref: scrollContainerRef, onScroll: onScroll, className: clsx("min-h-42 overflow-y-auto tk-scrollbar p-0.5", isMobile ? "max-h-80" : "max-h-72"), children: [jsxs("div", { style: { height: totalHeight, position: "relative" }, children: [jsx("div", { style: { transform: "translateY(".concat(viewportRange.start * ROW_STRIDE, "px)"), position: "absolute", width: "100%" }, className: "flex flex-col gap-2", "aria-hidden": "true", children: Array.from({ length: Math.max(0, viewportRange.end - viewportRange.start) }).map(function (_, i) { return jsx(WalletButtonSkeleton, {}, "skeleton-".concat(viewportRange.start + i)); }) }), jsxs("div", { style: { transform: "translateY(".concat(offsetY, "px)"), position: "absolute", width: "100%" }, className: "flex flex-col gap-2", children: [qrRowCount > 0 && renderRange.start === 0 && jsx(WalletButton, { icon: jsx("div", { className: "flex items-center justify-center size-6 rounded-full text-icon-text-light dark:text-icon-text-dark bg-icon-background-light dark:bg-icon-background-dark", children: jsx(FontAwesomeIcon, { icon: faQrcode }) }), name: "Scan QR Code", chains: [{ namespace: Chain.Ethereum, isConnected: false }, { namespace: Chain.Solana, isConnected: false }], onClick: onSelectQRCode, isMobile: isMobile }, "qr-code-walletconnect"), visibleItems.map(function (_ref4) { var _first$icon, _first$name; var _ref5 = _slicedToArray(_ref4, 2), id = _ref5[0], group = _ref5[1]; var first = group[0]; return jsx(WalletButton, { icon: (_first$icon = first === null || first === void 0 ? void 0 : first.icon) !== null && _first$icon !== void 0 ? _first$icon : "", name: (_first$name = first === null || first === void 0 ? void 0 : first.name) !== null && _first$name !== void 0 ? _first$name : "", chains: group.map(function (app) { return { namespace: app.chain, isConnected: false }; }), onClick: function onClick() { return handleSelectGroup(group); }, shouldShowDisconnect: false, isMobile: isMobile }, id); })] })] }), searchQuery && walletEntries.length === 0 && jsxs("div", { className: "flex items-center justify-center w-full h-40 text-center text-icon-text-light dark:text-icon-text-dark", children: ["No wallets found matching \"", searchQuery, "\""] })] }), searchQuery && walletEntries.length > 0 && jsxs("div", { className: "text-xs text-center text-icon-text-light dark:text-icon-text-dark", children: ["Showing ", walletEntries.length, " wallet", walletEntries.length !== 1 ? "s" : ""] })] }); } export { ShowAllWalletsScreen }; //# sourceMappingURL=ShowAllWalletsScreen.mjs.map