UNPKG

@turnkey/react-wallet-kit

Version:

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

122 lines (118 loc) 5.43 kB
'use strict'; var sdkTypes = require('@turnkey/sdk-types'); var react = require('react'); var core = require('@turnkey/core'); const SESSION_WARNING_THRESHOLD_MS = 60 * 1000; // 1 minute in milliseconds const useDebouncedCallback = (fn, wait = 100) => { const timer = react.useRef(null); const fnRef = react.useRef(fn); fnRef.current = fn; return react.useCallback((...args) => { if (timer.current) clearTimeout(timer.current); timer.current = setTimeout(() => { fnRef.current(...args); timer.current = null; }, wait); }, [wait]); }; const isValidSession = session => { return session?.expiry !== undefined && session.expiry * 1000 > Date.now(); }; async function withTurnkeyErrorHandling(fn, sessionExpireFn, callbacks, fallbackMessage = "An unknown error occurred", fallbackCode = sdkTypes.TurnkeyErrorCodes.UNKNOWN) { try { return await fn(); } catch (error) { let tkError; if (error instanceof sdkTypes.TurnkeyError) { tkError = error; if (tkError.code === sdkTypes.TurnkeyErrorCodes.SESSION_EXPIRED) { if (sessionExpireFn) { await sessionExpireFn(); } else { // this should never happen. If it does, it means an SDK function is making // a session-based API call without providing a `sessionExpireFn()` for handling // SESSION_EXPIRED errors in `withTurnkeyErrorHandling()` throw new sdkTypes.TurnkeyError("SESSION_EXPIRED received without sessionExpireFn handler", sdkTypes.TurnkeyErrorCodes.INTERNAL_ERROR, error); } } // skip onError for WalletConnect expired errors if (tkError.code !== sdkTypes.TurnkeyErrorCodes.WALLET_CONNECT_EXPIRED) { callbacks?.onError?.(tkError); } throw tkError; } // we wrap non-Turnkey errors tkError = new sdkTypes.TurnkeyError(fallbackMessage, fallbackCode, error); callbacks?.onError?.(tkError); throw tkError; } } // Custom hook to get the current screen size function useScreenSize() { const [width, setWidth] = react.useState(typeof window !== "undefined" ? window.innerWidth : 1024); const [height, setHeight] = react.useState(typeof window !== "undefined" ? window.innerHeight : 768); react.useEffect(() => { const handleResize = () => { setWidth(window.innerWidth); setHeight(window.innerHeight); }; window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize); }, []); return { width, height, // I have no idea why but, Tailwind's responsive design breakpoints do not work. Throughout the modal components, you will see conditional styling using this `isMobile` variable. // This is fine since we only need to style for 2 screen sizes: mobile and desktop. If anyone can figure out why Tailwind's responsive design breakpoints do not work, please fix it and restyle the components accordingly, changing the `isMobile` to the Tailwind stuff when applicable. isMobile: width < 640 }; } function isWalletConnect(wallet) { return wallet.interfaceType == core.WalletInterfaceType.WalletConnect; } // Finds and returns the WalletConnect provider for the specified chain function findWalletConnectProvider(walletProviders, chain) { return walletProviders.find(p => p.interfaceType === core.WalletInterfaceType.WalletConnect && p.chainInfo.namespace === chain); } function useWalletProviderState(initialState = []) { const [walletProviders, setWalletProviders] = react.useState(initialState); const prevProvidersRef = react.useRef(initialState); function isSameWalletProvider(a, b) { if (a.length !== b.length) return false; const key = provider => { const name = provider.info.name; const namespace = provider.chainInfo.namespace; const interfaceType = provider.interfaceType; const connectedAddresses = [...provider.connectedAddresses].map(x => x.toLowerCase()).sort().join(","); const uri = provider.uri || ""; return `${namespace}|${interfaceType}|${name}|${connectedAddresses}|${uri}`; }; const A = a.map(key).sort(); const B = b.map(key).sort(); for (let i = 0; i < A.length; i++) if (A[i] !== B[i]) return false; return true; } const updateWalletProviders = react.useCallback(newProviders => { if (!isSameWalletProvider(prevProvidersRef.current, newProviders)) { prevProvidersRef.current = newProviders; setWalletProviders(newProviders); } // we do nothing if the wallet providers are the same }, []); return [walletProviders, updateWalletProviders]; } function mergeWalletsWithoutDuplicates(existingWallets, newWallets) { const existingWalletIds = new Set(existingWallets.map(w => w.walletId)); const uniqueNewWallets = newWallets.filter(w => !existingWalletIds.has(w.walletId)); return [...existingWallets, ...uniqueNewWallets]; } exports.SESSION_WARNING_THRESHOLD_MS = SESSION_WARNING_THRESHOLD_MS; exports.findWalletConnectProvider = findWalletConnectProvider; exports.isValidSession = isValidSession; exports.isWalletConnect = isWalletConnect; exports.mergeWalletsWithoutDuplicates = mergeWalletsWithoutDuplicates; exports.useDebouncedCallback = useDebouncedCallback; exports.useScreenSize = useScreenSize; exports.useWalletProviderState = useWalletProviderState; exports.withTurnkeyErrorHandling = withTurnkeyErrorHandling; //# sourceMappingURL=utils.js.map