UNPKG

@turnkey/react-wallet-kit

Version:

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

298 lines (295 loc) 12 kB
import { slicedToArray as _slicedToArray, toConsumableArray as _toConsumableArray, asyncToGenerator as _asyncToGenerator, regenerator as _regenerator, objectSpread2 as _objectSpread2 } from '../../_virtual/_rollupPluginBabelHelpers.mjs'; import { jsxs, jsx } from 'react/jsx-runtime'; import { useState, useRef } from 'react'; import { useModal } from '../../providers/modal/Hook.mjs'; import { useTurnkey } from '../../providers/client/Hook.mjs'; import { Spinner } from '../design/Spinners.mjs'; import { Input } from '@headlessui/react'; import { BaseButton } from '../design/Buttons.mjs'; import { OtpType, TurnkeyErrorCodes } from '@turnkey/core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faEnvelope, faPhone } from '@fortawesome/free-solid-svg-icons'; import clsx from 'clsx'; function OtpVerification(props) { var contact = props.contact, otpType = props.otpType, _props$otpLength = props.otpLength, otpLength = _props$otpLength === void 0 ? 6 : _props$otpLength, _props$alphanumeric = props.alphanumeric, alphanumeric = _props$alphanumeric === void 0 ? true : _props$alphanumeric, formattedContact = props.formattedContact, sessionKey = props.sessionKey, _props$onContinue = props.onContinue, onContinue = _props$onContinue === void 0 ? null : _props$onContinue; var _useTurnkey = useTurnkey(), initOtp = _useTurnkey.initOtp, completeOtp = _useTurnkey.completeOtp; var _useModal = useModal(), closeModal = _useModal.closeModal, isMobile = _useModal.isMobile; var _useState = useState(false), _useState2 = _slicedToArray(_useState, 2), submitting = _useState2[0], setSubmitting = _useState2[1]; var _useState3 = useState(false), _useState4 = _slicedToArray(_useState3, 2), resending = _useState4[0], setResending = _useState4[1]; var _useState5 = useState(false), _useState6 = _slicedToArray(_useState5, 2), resent = _useState6[0], setResent = _useState6[1]; var _useState7 = useState(props.otpId), _useState8 = _slicedToArray(_useState7, 2), otpId = _useState8[0], setOtpId = _useState8[1]; var _useState9 = useState(props.otpEncryptionTargetBundle), _useState0 = _slicedToArray(_useState9, 2), otpEncryptionTargetBundle = _useState0[0], setOtpEncryptionTargetBundle = _useState0[1]; var _useState1 = useState(null), _useState10 = _slicedToArray(_useState1, 2), error = _useState10[0], setError = _useState10[1]; var _useState11 = useState(false), _useState12 = _slicedToArray(_useState11, 2), shaking = _useState12[0], setShaking = _useState12[1]; var shakeInput = function shakeInput() { setShaking(true); setTimeout(function () { return setShaking(false); }, 250); }; var handleContinue = /*#__PURE__*/function () { var _ref = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(otpCode) { var niceError, _t; return _regenerator().w(function (_context) { while (1) switch (_context.p = _context.n) { case 0: _context.p = 0; setSubmitting(true); if (!onContinue) { _context.n = 2; break; } _context.n = 1; return onContinue(otpCode); case 1: _context.n = 4; break; case 2: _context.n = 3; return completeOtp(_objectSpread2({ otpId: otpId, otpCode: otpCode, otpEncryptionTargetBundle: otpEncryptionTargetBundle, contact: contact, otpType: otpType }, sessionKey && { sessionKey: sessionKey })); case 3: closeModal(); case 4: _context.n = 6; break; case 5: _context.p = 5; _t = _context.v; niceError = _t.code === TurnkeyErrorCodes.INVALID_OTP_CODE ? "Invalid OTP code" : "An error has occurred"; setError(niceError); shakeInput(); throw new Error("Error completing OTP: ".concat(_t)); case 6: _context.p = 6; setSubmitting(false); return _context.f(6); case 7: return _context.a(2); } }, _callee, null, [[0, 5, 6, 7]]); })); return function handleContinue(_x) { return _ref.apply(this, arguments); }; }(); var handleResend = /*#__PURE__*/function () { var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() { var _yield$initOtp, _otpId, _otpEncryptionTargetBundle, _t2; return _regenerator().w(function (_context2) { while (1) switch (_context2.p = _context2.n) { case 0: setResending(true); _context2.p = 1; _context2.n = 2; return initOtp({ otpType: otpType, contact: contact }); case 2: _yield$initOtp = _context2.v; _otpId = _yield$initOtp.otpId; _otpEncryptionTargetBundle = _yield$initOtp.otpEncryptionTargetBundle; setOtpId(_otpId); setOtpEncryptionTargetBundle(_otpEncryptionTargetBundle); setResent(true); _context2.n = 4; break; case 3: _context2.p = 3; _t2 = _context2.v; throw new Error("Error resending OTP: ".concat(_t2)); case 4: _context2.p = 4; setResending(false); return _context2.f(4); case 5: return _context2.a(2); } }, _callee2, null, [[1, 3, 4, 5]]); })); return function handleResend() { return _ref2.apply(this, arguments); }; }(); return jsxs("div", { className: clsx("flex items-center justify-center py-3", isMobile ? "w-full" : "min-w-96"), children: [jsxs("div", { className: "flex flex-col items-center justify-center gap-6 transition-all duration-300 ".concat(submitting && "opacity-30 blur"), children: [jsx(FontAwesomeIcon, { size: "3x", icon: otpType === OtpType.Email ? faEnvelope : otpType === OtpType.Sms ? faPhone : faEnvelope }), jsxs("div", { className: "flex flex-col text-center", children: [jsx("span", { className: "text-lg font-medium", children: "Enter the ".concat(otpLength, "-digit code we sent to") }), jsx("span", { className: "text-base font-semibold", children: formattedContact !== null && formattedContact !== void 0 ? formattedContact : contact })] }), jsx("div", { className: "transition-all flex justify-center ".concat(shaking ? "animate-shake" : ""), children: jsx(OtpInput, { otpLength: otpLength, onContinue: handleContinue, alphanumeric: alphanumeric }) }), error && jsx("div", { className: "text-red-400 text-center text-sm", children: error }), jsx(BaseButton, { onClick: handleResend, disabled: resending || resent, className: "text-xs text-inherit font-semibold bg-transparent border-none ".concat(resent && "opacity-30"), children: resending ? jsxs("span", { className: "flex items-center gap-2.5", children: [jsx(Spinner, { className: "size-3" }), "Resending..."] }) : resent ? "Code sent!" : "Resend Code" })] }), submitting && jsx("div", { className: "absolute flex w-full h-full justify-center items-center", children: jsx(Spinner, { strokeWidth: 1, className: "size-1/2" }) })] }); } function OtpInput(props) { var otpLength = props.otpLength, onContinue = props.onContinue, _props$alphanumeric2 = props.alphanumeric, alphanumeric = _props$alphanumeric2 === void 0 ? false : _props$alphanumeric2; var _useModal2 = useModal(), isMobile = _useModal2.isMobile; var _useState13 = useState(Array(otpLength).fill("")), _useState14 = _slicedToArray(_useState13, 2), values = _useState14[0], setValues = _useState14[1]; var inputsRef = useRef([]); var handleChange = function handleChange(index, value) { if (!alphanumeric) value = value.replace(/[^0-9]/g, "");else value = value.replace(/[^a-zA-Z0-9]/g, ""); var newValues = _toConsumableArray(values); newValues[index] = value.slice(-1); // only last char setValues(newValues); if (value && index < otpLength - 1) { var _inputsRef$current; (_inputsRef$current = inputsRef.current[index + 1]) === null || _inputsRef$current === void 0 ? void 0 : _inputsRef$current.focus(); } if (value && newValues.every(function (v) { return v; })) { onContinue(newValues.join("")); } }; var handleKeyDown = function handleKeyDown(e, index) { if (e.key === "Backspace" && !values[index] && index > 0) { var _inputsRef$current2; (_inputsRef$current2 = inputsRef.current[index - 1]) === null || _inputsRef$current2 === void 0 ? void 0 : _inputsRef$current2.focus(); } if (e.key === "ArrowLeft" && index > 0) { var _inputsRef$current3; (_inputsRef$current3 = inputsRef.current[index - 1]) === null || _inputsRef$current3 === void 0 ? void 0 : _inputsRef$current3.focus(); } if (e.key === "ArrowRight" && index < otpLength - 1) { var _inputsRef$current4; (_inputsRef$current4 = inputsRef.current[index + 1]) === null || _inputsRef$current4 === void 0 ? void 0 : _inputsRef$current4.focus(); } if (e.key === "Enter") { onContinue(values.join("")); } }; var handlePaste = function handlePaste(e) { var _inputsRef$current$la; e.preventDefault(); var paste = e.clipboardData.getData("text").trim(); var cleaned = alphanumeric ? paste.replace(/[^a-zA-Z0-9]/g, "") : paste.replace(/[^0-9]/g, ""); var sliced = cleaned.slice(0, otpLength).split(""); var newValues = _toConsumableArray(values); for (var i = 0; i < sliced.length; i++) { var _sliced$i; newValues[i] = (_sliced$i = sliced[i]) !== null && _sliced$i !== void 0 ? _sliced$i : ""; if (inputsRef.current[i]) { var _sliced$i2; inputsRef.current[i].value = (_sliced$i2 = sliced[i]) !== null && _sliced$i2 !== void 0 ? _sliced$i2 : ""; } } setValues(newValues); // Focus the last filled box var lastIndex = Math.min(sliced.length, otpLength - 1); (_inputsRef$current$la = inputsRef.current[lastIndex]) === null || _inputsRef$current$la === void 0 ? void 0 : _inputsRef$current$la.focus(); onContinue(newValues.join("")); }; return jsx("div", { className: clsx("flex items-center justify-center space-x-2", isMobile ? "w-[95%]" : "w-full"), children: Array.from({ length: otpLength }).map(function (_, i) { var _values$i; return jsx(Input, { type: alphanumeric ? "text" : "text", inputMode: alphanumeric ? "text" : "numeric", maxLength: 1, value: (_values$i = values[i]) === null || _values$i === void 0 ? void 0 : _values$i.toUpperCase(), autoComplete: "off", onChange: function onChange(e) { return handleChange(i, e.target.value.toUpperCase()); }, onKeyDown: function onKeyDown(e) { return handleKeyDown(e, i); }, onPaste: handlePaste, ref: function ref(el) { return inputsRef.current[i] = el; }, className: clsx("text-center text-lg rounded-md border border-modal-background-dark/20 dark:border-modal-background-light/20 bg-button-light dark:bg-button-dark text-inherit focus:outline-primary-light focus:dark:outline-primary-dark focus:outline-[1px] focus:outline-offset-0 transition-all", isMobile ? "w-full h-10" : "h-12 w-12") }, i); }) }); } export { OtpInput, OtpVerification }; //# sourceMappingURL=OTP.mjs.map