@guettonenrique/passwordless-auth 0.0.0-stage → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,617 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/react-native/index.ts
21
+ var react_native_exports = {};
22
+ __export(react_native_exports, {
23
+ EmailOtpModal: () => EmailOtpModal,
24
+ OtpInput: () => OtpInput,
25
+ requestEmailOtp: () => requestEmailOtp,
26
+ signInWithEmailOtp: () => signInWithEmailOtp,
27
+ verifyEmailOtp: () => verifyEmailOtp
28
+ });
29
+ module.exports = __toCommonJS(react_native_exports);
30
+
31
+ // src/react-native/api.ts
32
+ var import_functions = require("@react-native-firebase/functions");
33
+ var import_auth = require("@react-native-firebase/auth");
34
+ async function requestEmailOtp(email) {
35
+ const functions = (0, import_functions.getFunctions)();
36
+ const callable = (0, import_functions.httpsCallable)(
37
+ functions,
38
+ "requestEmailOtp"
39
+ );
40
+ const result = await callable({ email: email.trim().toLowerCase() });
41
+ return result.data;
42
+ }
43
+ async function verifyEmailOtp(email, code) {
44
+ const functions = (0, import_functions.getFunctions)();
45
+ const callable = (0, import_functions.httpsCallable)(
46
+ functions,
47
+ "verifyEmailOtp"
48
+ );
49
+ const result = await callable({
50
+ email: email.trim().toLowerCase(),
51
+ code: code.trim()
52
+ });
53
+ return result.data.customToken;
54
+ }
55
+ async function signInWithEmailOtp(email, code) {
56
+ const customToken = await verifyEmailOtp(email, code);
57
+ await (0, import_auth.signInWithCustomToken)((0, import_auth.getAuth)(), customToken);
58
+ }
59
+
60
+ // src/react-native/OtpInput.tsx
61
+ var import_react = require("react");
62
+ var import_react_native = require("react-native");
63
+
64
+ // src/shared/constants.ts
65
+ var CODE_LENGTH = 6;
66
+ var RESEND_COOLDOWN_MS = 60 * 1e3;
67
+ var OTP_EXPIRY_MS = 10 * 60 * 1e3;
68
+
69
+ // src/shared/utils.ts
70
+ var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
71
+ function normalizeEmail(raw) {
72
+ return raw.trim().toLowerCase();
73
+ }
74
+ function isValidEmail(raw) {
75
+ return EMAIL_REGEX.test(normalizeEmail(raw));
76
+ }
77
+
78
+ // src/react-native/OtpInput.tsx
79
+ var import_jsx_runtime = require("react/jsx-runtime");
80
+ var Haptics = null;
81
+ try {
82
+ Haptics = require("expo-haptics");
83
+ } catch {
84
+ Haptics = null;
85
+ }
86
+ var impact = (style) => {
87
+ try {
88
+ if (style === "light") {
89
+ Haptics?.impactAsync?.("light").catch(() => null);
90
+ } else {
91
+ Haptics?.notificationAsync?.("success").catch(() => null);
92
+ }
93
+ } catch {
94
+ }
95
+ };
96
+ function OtpInput({
97
+ length = CODE_LENGTH,
98
+ value,
99
+ onChange,
100
+ onComplete,
101
+ disabled = false,
102
+ error = false,
103
+ autoFocus = true
104
+ }) {
105
+ const inputRef = (0, import_react.useRef)(null);
106
+ const [isFocused, setIsFocused] = (0, import_react.useState)(false);
107
+ const [cursorVisible, setCursorVisible] = (0, import_react.useState)(true);
108
+ (0, import_react.useEffect)(() => {
109
+ if (!isFocused) return;
110
+ const interval = setInterval(() => {
111
+ setCursorVisible((prev) => !prev);
112
+ }, 550);
113
+ return () => clearInterval(interval);
114
+ }, [isFocused]);
115
+ const handleChangeText = (text) => {
116
+ const cleaned = text.replace(/[^0-9]/g, "").slice(0, length);
117
+ onChange(cleaned);
118
+ if (cleaned.length > value.length) impact("light");
119
+ if (cleaned.length === length) {
120
+ impact("success");
121
+ onComplete?.(cleaned);
122
+ }
123
+ };
124
+ const slots = Array.from({ length }, (_, i) => i);
125
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
126
+ import_react_native.TouchableOpacity,
127
+ {
128
+ style: { width: "100%" },
129
+ activeOpacity: 1,
130
+ onPress: () => {
131
+ if (!disabled) inputRef.current?.focus();
132
+ },
133
+ children: [
134
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
135
+ import_react_native.View,
136
+ {
137
+ style: {
138
+ flexDirection: "row",
139
+ width: "100%",
140
+ justifyContent: "space-between",
141
+ alignItems: "center",
142
+ marginVertical: 12
143
+ },
144
+ children: slots.map((index) => {
145
+ const char = value[index] ?? "";
146
+ const isSlotActive = isFocused && index === value.length && !disabled;
147
+ const isFilled = char !== "";
148
+ let borderColor = "#e5e7eb";
149
+ let backgroundColor = "#f8fafc";
150
+ if (error) {
151
+ borderColor = "#f43f5e";
152
+ backgroundColor = "#fff1f2";
153
+ } else if (isSlotActive) {
154
+ borderColor = "#3b82f6";
155
+ backgroundColor = "#ffffff";
156
+ } else if (isFilled) {
157
+ borderColor = "#94a3b8";
158
+ backgroundColor = "#ffffff";
159
+ }
160
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
161
+ import_react_native.View,
162
+ {
163
+ style: {
164
+ flex: 1,
165
+ maxWidth: 48,
166
+ height: 56,
167
+ borderRadius: 8,
168
+ alignItems: "center",
169
+ justifyContent: "center",
170
+ marginHorizontal: 4,
171
+ borderWidth: isSlotActive ? 2 : 1,
172
+ borderColor,
173
+ backgroundColor
174
+ },
175
+ children: char ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
176
+ import_react_native.Text,
177
+ {
178
+ style: {
179
+ fontSize: 24,
180
+ fontWeight: "700",
181
+ color: error ? "#e11d48" : "#1e293b"
182
+ },
183
+ children: char
184
+ }
185
+ ) : isSlotActive && cursorVisible ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
186
+ import_react_native.View,
187
+ {
188
+ style: {
189
+ width: 2,
190
+ height: 24,
191
+ backgroundColor: "#3b82f6",
192
+ borderRadius: 2
193
+ }
194
+ }
195
+ ) : null
196
+ },
197
+ index
198
+ );
199
+ })
200
+ }
201
+ ),
202
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
203
+ import_react_native.TextInput,
204
+ {
205
+ ref: inputRef,
206
+ value,
207
+ onChangeText: handleChangeText,
208
+ keyboardType: "number-pad",
209
+ textContentType: "oneTimeCode",
210
+ autoComplete: import_react_native.Platform.OS === "android" ? "one-time-code" : void 0,
211
+ maxLength: length,
212
+ editable: !disabled,
213
+ autoFocus,
214
+ onFocus: () => setIsFocused(true),
215
+ onBlur: () => setIsFocused(false),
216
+ caretHidden: true,
217
+ style: {
218
+ position: "absolute",
219
+ top: 0,
220
+ bottom: 0,
221
+ left: 0,
222
+ right: 0,
223
+ opacity: 0
224
+ },
225
+ accessibilityLabel: `${length}-digit verification code`
226
+ }
227
+ )
228
+ ]
229
+ }
230
+ );
231
+ }
232
+
233
+ // src/react-native/EmailOtpModal.tsx
234
+ var import_react2 = require("react");
235
+ var import_react_native2 = require("react-native");
236
+ var import_react_native_keyboard_controller = require("react-native-keyboard-controller");
237
+ var import_lucide_react_native = require("lucide-react-native");
238
+ var import_jsx_runtime2 = require("react/jsx-runtime");
239
+ function EmailOtpModal({
240
+ visible,
241
+ onClose,
242
+ initialEmail = "",
243
+ appName,
244
+ cooldownSeconds = Math.round(RESEND_COOLDOWN_MS / 1e3)
245
+ }) {
246
+ const [step, setStep] = (0, import_react2.useState)("email");
247
+ const [email, setEmail] = (0, import_react2.useState)(initialEmail);
248
+ const [otp, setOtp] = (0, import_react2.useState)("");
249
+ const [loading, setLoading] = (0, import_react2.useState)(false);
250
+ const [resending, setResending] = (0, import_react2.useState)(false);
251
+ const [error, setError] = (0, import_react2.useState)(null);
252
+ const [cooldown, setCooldown] = (0, import_react2.useState)(0);
253
+ const [prevVisible, setPrevVisible] = (0, import_react2.useState)(false);
254
+ const isVerifyingRef = (0, import_react2.useRef)(false);
255
+ const emailRef = (0, import_react2.useRef)(null);
256
+ if (visible !== prevVisible) {
257
+ setPrevVisible(visible);
258
+ if (visible) {
259
+ setStep("email");
260
+ setEmail(initialEmail);
261
+ setOtp("");
262
+ setError(null);
263
+ setLoading(false);
264
+ setResending(false);
265
+ setCooldown(0);
266
+ isVerifyingRef.current = false;
267
+ }
268
+ }
269
+ (0, import_react2.useEffect)(() => {
270
+ if (cooldown <= 0) return;
271
+ const timer = setInterval(() => {
272
+ setCooldown((prev) => prev > 0 ? prev - 1 : 0);
273
+ }, 1e3);
274
+ return () => clearInterval(timer);
275
+ }, [cooldown]);
276
+ const cleanErrorMessage = (err, fallback) => {
277
+ const raw = err instanceof Error ? err.message : typeof err === "string" ? err : "";
278
+ if (raw.length > 0) {
279
+ return raw.replace(/^\[.*?\]\s*/, "").trim();
280
+ }
281
+ return fallback;
282
+ };
283
+ const handleSendCode = async () => {
284
+ const trimmedEmail = email.trim().toLowerCase();
285
+ if (!trimmedEmail) {
286
+ setError("Please enter your email address.");
287
+ return;
288
+ }
289
+ if (!isValidEmail(trimmedEmail)) {
290
+ setError("Please enter a valid email address.");
291
+ return;
292
+ }
293
+ setLoading(true);
294
+ setError(null);
295
+ emailRef.current?.blur();
296
+ try {
297
+ await requestEmailOtp(trimmedEmail);
298
+ setStep("otp");
299
+ setCooldown(cooldownSeconds);
300
+ setOtp("");
301
+ } catch (err) {
302
+ console.error("[passwordless-auth] requestEmailOtp failed:", err);
303
+ setError(cleanErrorMessage(err, "Failed to send verification code. Please try again."));
304
+ } finally {
305
+ setLoading(false);
306
+ }
307
+ };
308
+ const handleResendCode = async () => {
309
+ if (cooldown > 0 || resending || loading) return;
310
+ setResending(true);
311
+ setError(null);
312
+ try {
313
+ await requestEmailOtp(email.trim().toLowerCase());
314
+ setCooldown(cooldownSeconds);
315
+ setOtp("");
316
+ } catch (err) {
317
+ console.error("[passwordless-auth] resend failed:", err);
318
+ setError(cleanErrorMessage(err, "Failed to resend verification code."));
319
+ } finally {
320
+ setResending(false);
321
+ }
322
+ };
323
+ const handleClose = () => {
324
+ setError(null);
325
+ setLoading(false);
326
+ setResending(false);
327
+ onClose();
328
+ };
329
+ const handleVerifyOtp = async (codeToVerify) => {
330
+ const code = (codeToVerify ?? otp).trim();
331
+ if (code.length < CODE_LENGTH || isVerifyingRef.current) return;
332
+ isVerifyingRef.current = true;
333
+ setLoading(true);
334
+ setError(null);
335
+ try {
336
+ await signInWithEmailOtp(email.trim().toLowerCase(), code);
337
+ handleClose();
338
+ } catch (err) {
339
+ console.error("[passwordless-auth] verifyEmailOtp failed:", err);
340
+ setError(
341
+ cleanErrorMessage(err, "Invalid or expired verification code. Please try again.")
342
+ );
343
+ setOtp("");
344
+ } finally {
345
+ setLoading(false);
346
+ isVerifyingRef.current = false;
347
+ }
348
+ };
349
+ const handleBackToEmail = () => {
350
+ if (loading) return;
351
+ setStep("email");
352
+ setOtp("");
353
+ setError(null);
354
+ };
355
+ const buttonBase = {
356
+ flex: 1,
357
+ paddingVertical: 14,
358
+ borderRadius: 12,
359
+ alignItems: "center",
360
+ flexDirection: "row",
361
+ justifyContent: "center",
362
+ gap: 8
363
+ };
364
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Modal, { visible, transparent: true, animationType: "fade", onRequestClose: handleClose, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
365
+ import_react_native_keyboard_controller.KeyboardAwareScrollView,
366
+ {
367
+ contentContainerStyle: { flexGrow: 1 },
368
+ keyboardShouldPersistTaps: "handled",
369
+ bottomOffset: 24,
370
+ showsVerticalScrollIndicator: false,
371
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
372
+ import_react_native2.TouchableOpacity,
373
+ {
374
+ style: {
375
+ flex: 1,
376
+ backgroundColor: "rgba(0,0,0,0.5)",
377
+ justifyContent: "center",
378
+ alignItems: "center",
379
+ padding: 16
380
+ },
381
+ activeOpacity: 1,
382
+ onPress: handleClose,
383
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
384
+ import_react_native2.TouchableOpacity,
385
+ {
386
+ activeOpacity: 1,
387
+ style: {
388
+ backgroundColor: "#fff",
389
+ width: "100%",
390
+ maxWidth: 420,
391
+ borderRadius: 16,
392
+ overflow: "hidden",
393
+ shadowColor: "#000",
394
+ shadowOpacity: 0.2,
395
+ shadowRadius: 12,
396
+ elevation: 10
397
+ },
398
+ children: [
399
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
400
+ import_react_native2.View,
401
+ {
402
+ style: {
403
+ flexDirection: "row",
404
+ alignItems: "center",
405
+ justifyContent: "space-between",
406
+ paddingHorizontal: 24,
407
+ paddingVertical: 16,
408
+ borderBottomWidth: 1,
409
+ borderBottomColor: "#f3f4f6"
410
+ },
411
+ children: [
412
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: { flexDirection: "row", alignItems: "center", gap: 12 }, children: [
413
+ step === "otp" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.TouchableOpacity, { onPress: handleBackToEmail, disabled: loading, activeOpacity: 0.7, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react_native.ArrowLeft, { size: 20, color: "#64748b" }) }),
414
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { children: [
415
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: { fontSize: 12, color: "#94a3b8", marginBottom: 2 }, children: step === "email" ? "Instant Sign In" : "Verification" }),
416
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: { fontSize: 18, fontWeight: "700", color: "#1e293b" }, children: step === "email" ? appName ? `${appName} \u2014 email code` : "Sign in with email code" : "Enter verification code" })
417
+ ] })
418
+ ] }),
419
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.TouchableOpacity, { onPress: handleClose, activeOpacity: 0.7, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react_native.X, { size: 20, color: "#94a3b8" }) })
420
+ ]
421
+ }
422
+ ),
423
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: { padding: 24 }, children: [
424
+ error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
425
+ import_react_native2.View,
426
+ {
427
+ style: {
428
+ flexDirection: "row",
429
+ alignItems: "center",
430
+ gap: 8,
431
+ padding: 12,
432
+ marginBottom: 16,
433
+ borderRadius: 12,
434
+ backgroundColor: "#fef2f2",
435
+ borderWidth: 1,
436
+ borderColor: "#fecaca"
437
+ },
438
+ children: [
439
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react_native.AlertCircle, { size: 16, color: "#dc2626" }),
440
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: { fontSize: 12, color: "#dc2626", flex: 1, fontWeight: "500" }, children: error })
441
+ ]
442
+ }
443
+ ) : null,
444
+ step === "email" ? (
445
+ /* ---------- Step 1: Email ---------- */
446
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { children: [
447
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: { fontSize: 13, color: "#64748b", marginBottom: 16, lineHeight: 18 }, children: "We'll send a 6-digit verification code to your email. No password needed." }),
448
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: { marginBottom: 24 }, children: [
449
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: { fontSize: 12, fontWeight: "600", color: "#334155", marginBottom: 8 }, children: "Email" }),
450
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
451
+ import_react_native2.View,
452
+ {
453
+ style: {
454
+ flexDirection: "row",
455
+ alignItems: "center",
456
+ paddingHorizontal: 16,
457
+ backgroundColor: "#f8fafc",
458
+ borderRadius: 12,
459
+ borderWidth: 1,
460
+ borderColor: "#e5e7eb",
461
+ height: 56
462
+ },
463
+ children: [
464
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
465
+ import_react_native2.TextInput,
466
+ {
467
+ ref: emailRef,
468
+ style: { flex: 1, fontSize: 14, fontWeight: "600", color: "#1e293b" },
469
+ placeholder: "name@email.com",
470
+ placeholderTextColor: "#94a3b8",
471
+ value: email,
472
+ onChangeText: (val) => {
473
+ setEmail(val);
474
+ if (error) setError(null);
475
+ },
476
+ keyboardType: "email-address",
477
+ autoCapitalize: "none",
478
+ autoCorrect: false,
479
+ returnKeyType: "send",
480
+ onSubmitEditing: handleSendCode,
481
+ autoFocus: true,
482
+ editable: !loading
483
+ }
484
+ ),
485
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react_native.Mail, { size: 20, color: "#cbd5e1" })
486
+ ]
487
+ }
488
+ )
489
+ ] }),
490
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: { flexDirection: "row", gap: 12 }, children: [
491
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
492
+ import_react_native2.TouchableOpacity,
493
+ {
494
+ onPress: handleClose,
495
+ style: { ...buttonBase, borderWidth: 1, borderColor: "#e5e7eb", backgroundColor: "transparent" },
496
+ activeOpacity: 0.7,
497
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: { color: "#64748b", fontWeight: "600", fontSize: 12, textTransform: "uppercase", letterSpacing: 1 }, children: "Cancel" })
498
+ }
499
+ ),
500
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
501
+ import_react_native2.TouchableOpacity,
502
+ {
503
+ onPress: handleSendCode,
504
+ disabled: loading,
505
+ style: { ...buttonBase, backgroundColor: loading ? "#93c5fd" : "#3b82f6" },
506
+ activeOpacity: 0.7,
507
+ children: [
508
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.ActivityIndicator, { size: "small", color: "#fff" }) : null,
509
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: { color: "#fff", fontWeight: "600", fontSize: 12, textTransform: "uppercase", letterSpacing: 1 }, children: loading ? "Sending\u2026" : "Send Code" })
510
+ ]
511
+ }
512
+ )
513
+ ] })
514
+ ] })
515
+ ) : (
516
+ /* ---------- Step 2: Code ---------- */
517
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { children: [
518
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.Text, { style: { fontSize: 13, color: "#64748b", marginBottom: 16, lineHeight: 18 }, children: [
519
+ "We sent a 6-digit code to",
520
+ " ",
521
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: { fontWeight: "600", color: "#1e293b" }, children: email.trim() }),
522
+ " ",
523
+ ". Enter it below to sign in."
524
+ ] }),
525
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: { alignItems: "center", marginBottom: 16 }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
526
+ OtpInput,
527
+ {
528
+ value: otp,
529
+ onChange: (val) => {
530
+ setOtp(val);
531
+ if (error) setError(null);
532
+ },
533
+ onComplete: (code) => handleVerifyOtp(code),
534
+ disabled: loading,
535
+ error: !!error,
536
+ autoFocus: true
537
+ }
538
+ ) }),
539
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
540
+ import_react_native2.View,
541
+ {
542
+ style: {
543
+ flexDirection: "row",
544
+ alignItems: "center",
545
+ justifyContent: "space-between",
546
+ marginBottom: 24,
547
+ paddingHorizontal: 4
548
+ },
549
+ children: [
550
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.TouchableOpacity, { onPress: handleBackToEmail, disabled: loading, activeOpacity: 0.7, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: { fontSize: 12, fontWeight: "600", color: "#64748b" }, children: "Change email" }) }),
551
+ cooldown > 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.Text, { style: { fontSize: 12, color: "#94a3b8", fontWeight: "500" }, children: [
552
+ "Resend in ",
553
+ cooldown,
554
+ "s"
555
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
556
+ import_react_native2.TouchableOpacity,
557
+ {
558
+ onPress: handleResendCode,
559
+ disabled: resending || loading,
560
+ style: { flexDirection: "row", alignItems: "center", gap: 6 },
561
+ activeOpacity: 0.7,
562
+ children: [
563
+ resending ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.ActivityIndicator, { size: "small", color: "#2563eb" }) : null,
564
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: { fontSize: 12, fontWeight: "600", color: "#2563eb" }, children: "Resend Code" })
565
+ ]
566
+ }
567
+ )
568
+ ]
569
+ }
570
+ ),
571
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { style: { flexDirection: "row", gap: 12 }, children: [
572
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
573
+ import_react_native2.TouchableOpacity,
574
+ {
575
+ onPress: handleClose,
576
+ style: { ...buttonBase, borderWidth: 1, borderColor: "#e5e7eb", backgroundColor: "transparent" },
577
+ activeOpacity: 0.7,
578
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: { color: "#64748b", fontWeight: "600", fontSize: 12, textTransform: "uppercase", letterSpacing: 1 }, children: "Cancel" })
579
+ }
580
+ ),
581
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
582
+ import_react_native2.TouchableOpacity,
583
+ {
584
+ onPress: () => handleVerifyOtp(),
585
+ disabled: loading || otp.length < CODE_LENGTH,
586
+ style: {
587
+ ...buttonBase,
588
+ backgroundColor: loading || otp.length < CODE_LENGTH ? "#93c5fd" : "#3b82f6"
589
+ },
590
+ activeOpacity: 0.7,
591
+ children: [
592
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.ActivityIndicator, { size: "small", color: "#fff" }) : null,
593
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: { color: "#fff", fontWeight: "600", fontSize: 12, textTransform: "uppercase", letterSpacing: 1 }, children: loading ? "Verifying\u2026" : "Verify & Sign In" })
594
+ ]
595
+ }
596
+ )
597
+ ] })
598
+ ] })
599
+ )
600
+ ] })
601
+ ]
602
+ }
603
+ )
604
+ }
605
+ )
606
+ }
607
+ ) });
608
+ }
609
+ // Annotate the CommonJS export names for ESM import in node:
610
+ 0 && (module.exports = {
611
+ EmailOtpModal,
612
+ OtpInput,
613
+ requestEmailOtp,
614
+ signInWithEmailOtp,
615
+ verifyEmailOtp
616
+ });
617
+ //# sourceMappingURL=index.cjs.map