@kryptos_connect/mobile-sdk 0.0.0

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.
package/dist/index.mjs ADDED
@@ -0,0 +1,3860 @@
1
+ // src/KryptosConnectButton.tsx
2
+ import React31 from "react";
3
+ import {
4
+ StyleSheet as StyleSheet16,
5
+ Text as Text15,
6
+ TouchableOpacity as TouchableOpacity7,
7
+ View as View16
8
+ } from "react-native";
9
+
10
+ // src/assets/LogoIcon.tsx
11
+ import React from "react";
12
+ import Svg, { Path } from "react-native-svg";
13
+ var LogoIcon = ({ size = 36 }) => {
14
+ return /* @__PURE__ */ React.createElement(Svg, { width: size, height: size, viewBox: "0 0 36 36", fill: "none" }, /* @__PURE__ */ React.createElement(
15
+ Path,
16
+ {
17
+ d: "M0 4.11429C0 1.84203 1.84203 0 4.11429 0H31.8857C34.158 0 36 1.84203 36 4.11429V31.8857C36 34.158 34.158 36 31.8857 36H4.11429C1.84203 36 0 34.158 0 31.8857V4.11429Z",
18
+ fill: "#00C693"
19
+ }
20
+ ), /* @__PURE__ */ React.createElement(
21
+ Path,
22
+ {
23
+ d: "M12.3916 28.2857H8.43388C8.03646 28.2857 7.71429 27.9886 7.71429 27.6221V22.92C7.71429 22.744 7.7901 22.5752 7.92505 22.4508L9.66229 20.8487C9.79724 20.7243 9.98027 20.6544 10.1711 20.6544H12.3916C12.789 20.6544 13.1112 20.9515 13.1112 21.318V27.6221C13.1112 27.9886 12.789 28.2857 12.3916 28.2857Z",
24
+ fill: "white"
25
+ }
26
+ ), /* @__PURE__ */ React.createElement(
27
+ Path,
28
+ {
29
+ d: "M27.5647 28.2857H22.0443C21.8535 28.2857 21.6704 28.2158 21.5355 28.0914L13.9798 21.1236C13.6988 20.8645 13.6988 20.4443 13.9798 20.1851L15.7788 18.5262C15.9137 18.4017 16.0968 18.3318 16.2876 18.3318H18.21C18.4009 18.3318 18.5839 18.4017 18.7189 18.5262L28.0735 27.1529C28.5268 27.5709 28.2058 28.2857 27.5647 28.2857Z",
30
+ fill: "white"
31
+ }
32
+ ), /* @__PURE__ */ React.createElement(
33
+ Path,
34
+ {
35
+ d: "M27.5647 7.71429H22.0443C21.8535 7.71429 21.6704 7.7842 21.5355 7.90865L13.9798 14.8764C13.6988 15.1355 13.6988 15.5557 13.9798 15.8149L15.7788 17.4738C15.9137 17.5983 16.0968 17.6682 16.2876 17.6682H18.21C18.4009 17.6682 18.5839 17.5983 18.7189 17.4738L28.0735 8.84711C28.5268 8.42907 28.2058 7.71429 27.5647 7.71429Z",
36
+ fill: "white"
37
+ }
38
+ ), /* @__PURE__ */ React.createElement(
39
+ Path,
40
+ {
41
+ d: "M12.3916 7.71429H8.43388C8.03646 7.71429 7.71429 8.01139 7.71429 8.37788V13.08C7.71429 13.256 7.7901 13.4248 7.92505 13.5492L9.66229 15.1513C9.79724 15.2757 9.98027 15.3456 10.1711 15.3456H12.3916C12.789 15.3456 13.1112 15.0485 13.1112 14.682V8.37788C13.1112 8.01139 12.789 7.71429 12.3916 7.71429Z",
42
+ fill: "white"
43
+ }
44
+ ));
45
+ };
46
+
47
+ // src/contexts/KryptosContext.tsx
48
+ import React2 from "react";
49
+ var KryptosContext = React2.createContext(
50
+ void 0
51
+ );
52
+ var KryptosConnectProvider = ({ children, config }) => {
53
+ const [isInitialized, setIsInitialized] = React2.useState(false);
54
+ const [linkToken, setLinkToken] = React2.useState("");
55
+ const [user, setUser] = React2.useState(null);
56
+ const [email, setEmail] = React2.useState("");
57
+ const [userConsent, setUserConsent] = React2.useState(
58
+ null
59
+ );
60
+ return /* @__PURE__ */ React2.createElement(
61
+ KryptosContext.Provider,
62
+ {
63
+ value: {
64
+ ...config,
65
+ isInitialized,
66
+ setIsInitialized,
67
+ linkToken,
68
+ setLinkToken,
69
+ user,
70
+ setUser,
71
+ email,
72
+ setEmail,
73
+ userConsent,
74
+ setUserConsent
75
+ }
76
+ },
77
+ children
78
+ );
79
+ };
80
+ var useKryptosConnect = () => {
81
+ const ctx = React2.useContext(KryptosContext);
82
+ if (!ctx)
83
+ throw new Error(
84
+ "useKryptosConnect must be used inside <KryptosConnectProvider>"
85
+ );
86
+ return ctx;
87
+ };
88
+
89
+ // src/hooks/useTheme.ts
90
+ import React3 from "react";
91
+
92
+ // src/theme/index.ts
93
+ var lightTheme = {
94
+ colors: {
95
+ primary: "#00C693",
96
+ primaryDark: "#00A67A",
97
+ background: "#FFFFFF",
98
+ surface: "#F8F9FA",
99
+ surfaceSecondary: "#F1F3F5",
100
+ text: "#1A1A1A",
101
+ textSecondary: "#6B7280",
102
+ textTertiary: "#9CA3AF",
103
+ border: "#E5E7EB",
104
+ borderLight: "#F3F4F6",
105
+ error: "#EF4444",
106
+ errorLight: "#FEE2E2",
107
+ success: "#10B981",
108
+ successLight: "#D1FAE5",
109
+ warning: "#F59E0B",
110
+ warningLight: "#FEF3C7",
111
+ overlay: "rgba(0, 0, 0, 0.5)",
112
+ white: "#FFFFFF",
113
+ black: "#000000"
114
+ },
115
+ spacing: {
116
+ xs: 4,
117
+ sm: 8,
118
+ md: 12,
119
+ lg: 16,
120
+ xl: 20,
121
+ xxl: 24,
122
+ xxxl: 32
123
+ },
124
+ borderRadius: {
125
+ xs: 4,
126
+ sm: 8,
127
+ md: 12,
128
+ lg: 16,
129
+ xl: 20,
130
+ full: 9999
131
+ },
132
+ fontSize: {
133
+ xs: 10,
134
+ sm: 12,
135
+ md: 14,
136
+ lg: 16,
137
+ xl: 18,
138
+ xxl: 20,
139
+ xxxl: 24,
140
+ display: 32
141
+ },
142
+ fontWeight: {
143
+ regular: "400",
144
+ medium: "500",
145
+ semibold: "600",
146
+ bold: "700"
147
+ },
148
+ shadow: {
149
+ sm: {
150
+ shadowColor: "#000",
151
+ shadowOffset: { width: 0, height: 1 },
152
+ shadowOpacity: 0.05,
153
+ shadowRadius: 2,
154
+ elevation: 1
155
+ },
156
+ md: {
157
+ shadowColor: "#000",
158
+ shadowOffset: { width: 0, height: 2 },
159
+ shadowOpacity: 0.1,
160
+ shadowRadius: 4,
161
+ elevation: 3
162
+ },
163
+ lg: {
164
+ shadowColor: "#000",
165
+ shadowOffset: { width: 0, height: 4 },
166
+ shadowOpacity: 0.15,
167
+ shadowRadius: 8,
168
+ elevation: 5
169
+ }
170
+ }
171
+ };
172
+ var darkTheme = {
173
+ colors: {
174
+ primary: "#00C693",
175
+ primaryDark: "#00A67A",
176
+ background: "#0F0F0F",
177
+ surface: "#1A1A1A",
178
+ surfaceSecondary: "#252525",
179
+ text: "#FFFFFF",
180
+ textSecondary: "#A1A1A1",
181
+ textTertiary: "#6B6B6B",
182
+ border: "#2D2D2D",
183
+ borderLight: "#1F1F1F",
184
+ error: "#EF4444",
185
+ errorLight: "#7F1D1D",
186
+ success: "#10B981",
187
+ successLight: "#065F46",
188
+ warning: "#F59E0B",
189
+ warningLight: "#78350F",
190
+ overlay: "rgba(0, 0, 0, 0.7)",
191
+ white: "#FFFFFF",
192
+ black: "#000000"
193
+ },
194
+ spacing: lightTheme.spacing,
195
+ borderRadius: lightTheme.borderRadius,
196
+ fontSize: lightTheme.fontSize,
197
+ fontWeight: lightTheme.fontWeight,
198
+ shadow: {
199
+ sm: {
200
+ shadowColor: "#000",
201
+ shadowOffset: { width: 0, height: 1 },
202
+ shadowOpacity: 0.2,
203
+ shadowRadius: 2,
204
+ elevation: 1
205
+ },
206
+ md: {
207
+ shadowColor: "#000",
208
+ shadowOffset: { width: 0, height: 2 },
209
+ shadowOpacity: 0.3,
210
+ shadowRadius: 4,
211
+ elevation: 3
212
+ },
213
+ lg: {
214
+ shadowColor: "#000",
215
+ shadowOffset: { width: 0, height: 4 },
216
+ shadowOpacity: 0.4,
217
+ shadowRadius: 8,
218
+ elevation: 5
219
+ }
220
+ }
221
+ };
222
+ var getTheme = (isDark) => {
223
+ return isDark ? darkTheme : lightTheme;
224
+ };
225
+
226
+ // src/hooks/useTheme.ts
227
+ var useTheme = () => {
228
+ const { theme } = useKryptosConnect();
229
+ const currentTheme = React3.useMemo(() => {
230
+ return getTheme(theme === "dark");
231
+ }, [theme]);
232
+ return currentTheme;
233
+ };
234
+
235
+ // src/molecules/Auth.tsx
236
+ import React13 from "react";
237
+ import { View as View6, Text as Text6, StyleSheet as StyleSheet6 } from "react-native";
238
+
239
+ // src/assets/LinkIcon.tsx
240
+ import React4 from "react";
241
+ import Svg2, { Path as Path2 } from "react-native-svg";
242
+ var LinkIcon = ({
243
+ size = 20,
244
+ color = "#00C693"
245
+ }) => {
246
+ return /* @__PURE__ */ React4.createElement(Svg2, { width: size, height: size, viewBox: "0 0 24 24", fill: "none" }, /* @__PURE__ */ React4.createElement(
247
+ Path2,
248
+ {
249
+ d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71",
250
+ stroke: color,
251
+ strokeWidth: 2,
252
+ strokeLinecap: "round",
253
+ strokeLinejoin: "round"
254
+ }
255
+ ), /* @__PURE__ */ React4.createElement(
256
+ Path2,
257
+ {
258
+ d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71",
259
+ stroke: color,
260
+ strokeWidth: 2,
261
+ strokeLinecap: "round",
262
+ strokeLinejoin: "round"
263
+ }
264
+ ));
265
+ };
266
+
267
+ // src/assets/ShieldIcon.tsx
268
+ import React5 from "react";
269
+ import Svg3, { Path as Path3 } from "react-native-svg";
270
+ var ShieldIcon = ({
271
+ size = 20,
272
+ color = "#00C693"
273
+ }) => {
274
+ return /* @__PURE__ */ React5.createElement(Svg3, { width: size, height: size, viewBox: "0 0 24 24", fill: "none" }, /* @__PURE__ */ React5.createElement(
275
+ Path3,
276
+ {
277
+ d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z",
278
+ stroke: color,
279
+ strokeWidth: 2,
280
+ strokeLinecap: "round",
281
+ strokeLinejoin: "round"
282
+ }
283
+ ), /* @__PURE__ */ React5.createElement(
284
+ Path3,
285
+ {
286
+ d: "m9 12 2 2 4-4",
287
+ stroke: color,
288
+ strokeWidth: 2,
289
+ strokeLinecap: "round",
290
+ strokeLinejoin: "round"
291
+ }
292
+ ));
293
+ };
294
+
295
+ // src/components/Alert.tsx
296
+ import React6 from "react";
297
+ import { StyleSheet, Text, View } from "react-native";
298
+ var Alert = ({
299
+ variant = "default",
300
+ children,
301
+ style
302
+ }) => {
303
+ const theme = useTheme();
304
+ const getVariantStyles = () => {
305
+ switch (variant) {
306
+ case "destructive":
307
+ return {
308
+ backgroundColor: theme.colors.errorLight,
309
+ borderColor: theme.colors.error
310
+ };
311
+ default:
312
+ return {
313
+ backgroundColor: theme.colors.surface,
314
+ borderColor: theme.colors.border
315
+ };
316
+ }
317
+ };
318
+ return /* @__PURE__ */ React6.createElement(
319
+ View,
320
+ {
321
+ style: [
322
+ styles.alert,
323
+ {
324
+ borderRadius: theme.borderRadius.md,
325
+ padding: theme.spacing.md
326
+ },
327
+ getVariantStyles(),
328
+ style
329
+ ]
330
+ },
331
+ children
332
+ );
333
+ };
334
+ var AlertDescription = ({
335
+ children,
336
+ style
337
+ }) => {
338
+ const theme = useTheme();
339
+ return /* @__PURE__ */ React6.createElement(
340
+ Text,
341
+ {
342
+ style: [
343
+ styles.description,
344
+ {
345
+ color: theme.colors.text,
346
+ fontSize: theme.fontSize.md
347
+ },
348
+ style
349
+ ]
350
+ },
351
+ children
352
+ );
353
+ };
354
+ var styles = StyleSheet.create({
355
+ alert: {
356
+ borderWidth: 1,
357
+ marginVertical: 12
358
+ // theme.spacing.md - consistent alert spacing
359
+ },
360
+ title: {
361
+ fontWeight: "600",
362
+ marginBottom: 4
363
+ // theme.spacing.xs
364
+ },
365
+ description: {
366
+ lineHeight: 18,
367
+ textAlign: "center"
368
+ }
369
+ });
370
+
371
+ // src/components/Button.tsx
372
+ import React7 from "react";
373
+ import {
374
+ TouchableOpacity,
375
+ Text as Text2,
376
+ StyleSheet as StyleSheet2,
377
+ ActivityIndicator,
378
+ View as View2
379
+ } from "react-native";
380
+ var Button = ({
381
+ variant = "primary",
382
+ size = "md",
383
+ children,
384
+ onPress,
385
+ disabled = false,
386
+ loading = false,
387
+ style,
388
+ textStyle
389
+ }) => {
390
+ const theme = useTheme();
391
+ const getVariantStyles = () => {
392
+ switch (variant) {
393
+ case "primary":
394
+ return {
395
+ backgroundColor: disabled ? theme.colors.textTertiary : theme.colors.primary,
396
+ borderWidth: 0
397
+ };
398
+ case "secondary":
399
+ return {
400
+ backgroundColor: disabled ? theme.colors.surfaceSecondary : theme.colors.surface,
401
+ borderWidth: 1,
402
+ borderColor: theme.colors.border
403
+ };
404
+ case "outline":
405
+ return {
406
+ backgroundColor: "transparent",
407
+ borderWidth: 1,
408
+ borderColor: disabled ? theme.colors.textTertiary : theme.colors.primary
409
+ };
410
+ case "ghost":
411
+ return {
412
+ backgroundColor: "transparent",
413
+ borderWidth: 0
414
+ };
415
+ case "success":
416
+ return {
417
+ backgroundColor: disabled ? theme.colors.textTertiary : theme.colors.success,
418
+ borderWidth: 0
419
+ };
420
+ case "error":
421
+ return {
422
+ backgroundColor: disabled ? theme.colors.textTertiary : theme.colors.error,
423
+ borderWidth: 0
424
+ };
425
+ default:
426
+ return {};
427
+ }
428
+ };
429
+ const getTextColor = () => {
430
+ if (disabled) {
431
+ return variant === "outline" || variant === "ghost" ? theme.colors.textTertiary : theme.colors.white;
432
+ }
433
+ switch (variant) {
434
+ case "primary":
435
+ case "success":
436
+ case "error":
437
+ return theme.colors.white;
438
+ case "secondary":
439
+ return theme.colors.text;
440
+ case "outline":
441
+ return theme.colors.primary;
442
+ case "ghost":
443
+ return theme.colors.text;
444
+ default:
445
+ return theme.colors.white;
446
+ }
447
+ };
448
+ const getSizeStyles = () => {
449
+ switch (size) {
450
+ case "sm":
451
+ return {
452
+ button: {
453
+ paddingVertical: theme.spacing.sm,
454
+ paddingHorizontal: theme.spacing.md,
455
+ borderRadius: theme.borderRadius.sm
456
+ },
457
+ text: {
458
+ fontSize: theme.fontSize.sm
459
+ }
460
+ };
461
+ case "md":
462
+ return {
463
+ button: {
464
+ paddingVertical: theme.spacing.md,
465
+ paddingHorizontal: theme.spacing.lg,
466
+ borderRadius: theme.borderRadius.md
467
+ },
468
+ text: {
469
+ fontSize: theme.fontSize.md
470
+ }
471
+ };
472
+ case "lg":
473
+ return {
474
+ button: {
475
+ paddingVertical: theme.spacing.lg,
476
+ paddingHorizontal: theme.spacing.xl,
477
+ borderRadius: theme.borderRadius.md
478
+ },
479
+ text: {
480
+ fontSize: theme.fontSize.lg
481
+ }
482
+ };
483
+ default:
484
+ return {
485
+ button: {},
486
+ text: {}
487
+ };
488
+ }
489
+ };
490
+ const sizeStyles = getSizeStyles();
491
+ return /* @__PURE__ */ React7.createElement(
492
+ TouchableOpacity,
493
+ {
494
+ onPress,
495
+ disabled: disabled || loading,
496
+ activeOpacity: 0.7,
497
+ style: [
498
+ styles2.button,
499
+ getVariantStyles(),
500
+ sizeStyles.button,
501
+ disabled && styles2.disabled,
502
+ style
503
+ ]
504
+ },
505
+ loading ? /* @__PURE__ */ React7.createElement(ActivityIndicator, { size: "small", color: getTextColor() }) : typeof children === "string" ? /* @__PURE__ */ React7.createElement(
506
+ Text2,
507
+ {
508
+ style: [
509
+ styles2.text,
510
+ { color: getTextColor() },
511
+ sizeStyles.text,
512
+ textStyle
513
+ ]
514
+ },
515
+ children
516
+ ) : /* @__PURE__ */ React7.createElement(View2, { style: styles2.contentContainer }, children)
517
+ );
518
+ };
519
+ var styles2 = StyleSheet2.create({
520
+ button: {
521
+ flexDirection: "row",
522
+ alignItems: "center",
523
+ justifyContent: "center",
524
+ minHeight: 44
525
+ },
526
+ text: {
527
+ fontWeight: "600",
528
+ textAlign: "center"
529
+ },
530
+ disabled: {
531
+ opacity: 0.6
532
+ },
533
+ contentContainer: {
534
+ flexDirection: "row",
535
+ alignItems: "center",
536
+ justifyContent: "center"
537
+ }
538
+ });
539
+
540
+ // src/components/Input.tsx
541
+ import React8 from "react";
542
+ import {
543
+ View as View3,
544
+ TextInput,
545
+ Text as Text3,
546
+ StyleSheet as StyleSheet3
547
+ } from "react-native";
548
+ var Input = ({
549
+ label,
550
+ error,
551
+ helperText,
552
+ status = "default",
553
+ containerStyle,
554
+ inputStyle,
555
+ labelStyle,
556
+ ...props
557
+ }) => {
558
+ const theme = useTheme();
559
+ const inputStatus = error ? "error" : status;
560
+ const getBorderColor = () => {
561
+ switch (inputStatus) {
562
+ case "error":
563
+ return theme.colors.error;
564
+ case "success":
565
+ return theme.colors.success;
566
+ default:
567
+ return theme.colors.border;
568
+ }
569
+ };
570
+ return /* @__PURE__ */ React8.createElement(View3, { style: [styles3.wrapper, containerStyle] }, label && /* @__PURE__ */ React8.createElement(
571
+ Text3,
572
+ {
573
+ style: [
574
+ styles3.label,
575
+ { color: theme.colors.text, fontSize: theme.fontSize.sm },
576
+ labelStyle
577
+ ]
578
+ },
579
+ label
580
+ ), /* @__PURE__ */ React8.createElement(
581
+ TextInput,
582
+ {
583
+ placeholderTextColor: theme.colors.textTertiary,
584
+ style: [
585
+ styles3.input,
586
+ {
587
+ backgroundColor: theme.colors.surface,
588
+ borderColor: getBorderColor(),
589
+ color: theme.colors.text,
590
+ fontSize: theme.fontSize.md,
591
+ borderRadius: theme.borderRadius.md,
592
+ paddingHorizontal: theme.spacing.lg,
593
+ paddingVertical: theme.spacing.md
594
+ },
595
+ inputStyle
596
+ ],
597
+ ...props
598
+ }
599
+ ), error && /* @__PURE__ */ React8.createElement(
600
+ Text3,
601
+ {
602
+ style: [
603
+ styles3.error,
604
+ { color: theme.colors.error, fontSize: theme.fontSize.sm }
605
+ ]
606
+ },
607
+ error
608
+ ), helperText && !error && /* @__PURE__ */ React8.createElement(
609
+ Text3,
610
+ {
611
+ style: [
612
+ styles3.helper,
613
+ {
614
+ color: theme.colors.textSecondary,
615
+ fontSize: theme.fontSize.sm
616
+ }
617
+ ]
618
+ },
619
+ helperText
620
+ ));
621
+ };
622
+ var styles3 = StyleSheet3.create({
623
+ wrapper: {
624
+ marginBottom: 16
625
+ // theme.spacing.lg - consistent form spacing
626
+ },
627
+ label: {
628
+ fontWeight: "500",
629
+ marginBottom: 8
630
+ // theme.spacing.sm
631
+ },
632
+ input: {
633
+ borderWidth: 1,
634
+ minHeight: 48
635
+ },
636
+ error: {
637
+ marginTop: 4
638
+ // theme.spacing.xs
639
+ },
640
+ helper: {
641
+ marginTop: 4
642
+ // theme.spacing.xs
643
+ }
644
+ });
645
+
646
+ // src/components/Modal.tsx
647
+ import React10 from "react";
648
+ import {
649
+ Modal as RNModal,
650
+ View as View4,
651
+ Text as Text4,
652
+ TouchableOpacity as TouchableOpacity2,
653
+ StyleSheet as StyleSheet4,
654
+ ScrollView,
655
+ KeyboardAvoidingView,
656
+ Platform,
657
+ Dimensions
658
+ } from "react-native";
659
+
660
+ // src/assets/CloseIcon.tsx
661
+ import React9 from "react";
662
+ import Svg4, { Path as Path4 } from "react-native-svg";
663
+ var CloseIcon = ({
664
+ size = 20,
665
+ color = "#000"
666
+ }) => {
667
+ return /* @__PURE__ */ React9.createElement(Svg4, { width: size, height: size, viewBox: "0 0 20 20", fill: "none" }, /* @__PURE__ */ React9.createElement(
668
+ Path4,
669
+ {
670
+ d: "M15 5L5 15M5 5L15 15",
671
+ stroke: color,
672
+ strokeWidth: 2,
673
+ strokeLinecap: "round",
674
+ strokeLinejoin: "round"
675
+ }
676
+ ));
677
+ };
678
+
679
+ // src/components/Modal.tsx
680
+ var { height: SCREEN_HEIGHT } = Dimensions.get("window");
681
+ var Modal = ({
682
+ isOpen,
683
+ onClose,
684
+ children,
685
+ size = "md",
686
+ closeOnOverlayClick = true,
687
+ disableClose = true,
688
+ style
689
+ }) => {
690
+ const theme = useTheme();
691
+ const getSizeStyles = () => {
692
+ switch (size) {
693
+ case "xs":
694
+ return { maxHeight: SCREEN_HEIGHT * 0.35 };
695
+ case "sm":
696
+ return { maxHeight: SCREEN_HEIGHT * 0.45 };
697
+ case "md":
698
+ return { maxHeight: SCREEN_HEIGHT * 0.55 };
699
+ case "lg":
700
+ return { maxHeight: SCREEN_HEIGHT * 0.65 };
701
+ case "xl":
702
+ return { maxHeight: SCREEN_HEIGHT * 0.75 };
703
+ case "full":
704
+ return { maxHeight: SCREEN_HEIGHT * 0.85 };
705
+ default:
706
+ return { maxHeight: SCREEN_HEIGHT * 0.6 };
707
+ }
708
+ };
709
+ const handleOverlayPress = () => {
710
+ if (!disableClose && closeOnOverlayClick) onClose();
711
+ };
712
+ return /* @__PURE__ */ React10.createElement(
713
+ KeyboardAvoidingView,
714
+ {
715
+ behavior: Platform.OS === "ios" ? "padding" : "height",
716
+ style: styles4.keyboardView
717
+ },
718
+ /* @__PURE__ */ React10.createElement(
719
+ RNModal,
720
+ {
721
+ visible: isOpen,
722
+ transparent: true,
723
+ animationType: "none",
724
+ statusBarTranslucent: true,
725
+ onRequestClose: disableClose ? void 0 : onClose
726
+ },
727
+ /* @__PURE__ */ React10.createElement(
728
+ TouchableOpacity2,
729
+ {
730
+ activeOpacity: 1,
731
+ style: [styles4.overlay, { backgroundColor: theme.colors.overlay }],
732
+ onPress: handleOverlayPress
733
+ },
734
+ /* @__PURE__ */ React10.createElement(
735
+ View4,
736
+ {
737
+ style: [
738
+ styles4.container,
739
+ {
740
+ backgroundColor: theme.colors.background,
741
+ borderTopLeftRadius: theme.borderRadius.xl,
742
+ borderTopRightRadius: theme.borderRadius.xl,
743
+ ...theme.shadow.lg,
744
+ paddingBottom: theme.spacing.xl
745
+ // 20
746
+ },
747
+ getSizeStyles(),
748
+ style
749
+ ],
750
+ onStartShouldSetResponder: () => true
751
+ },
752
+ children
753
+ )
754
+ )
755
+ )
756
+ );
757
+ };
758
+ var ModalHeader = ({
759
+ children,
760
+ onClose,
761
+ showCloseButton = true,
762
+ style
763
+ }) => {
764
+ const theme = useTheme();
765
+ return /* @__PURE__ */ React10.createElement(
766
+ View4,
767
+ {
768
+ style: [
769
+ styles4.header,
770
+ {
771
+ borderBottomColor: theme.colors.border,
772
+ paddingHorizontal: theme.spacing.lg,
773
+ paddingVertical: theme.spacing.md
774
+ },
775
+ style
776
+ ]
777
+ },
778
+ /* @__PURE__ */ React10.createElement(View4, { style: styles4.headerContent }, typeof children === "string" ? /* @__PURE__ */ React10.createElement(
779
+ Text4,
780
+ {
781
+ style: [
782
+ styles4.title,
783
+ { color: theme.colors.text, fontSize: theme.fontSize.lg }
784
+ ]
785
+ },
786
+ children
787
+ ) : children),
788
+ showCloseButton && onClose && /* @__PURE__ */ React10.createElement(
789
+ TouchableOpacity2,
790
+ {
791
+ onPress: onClose,
792
+ hitSlop: { top: 10, bottom: 10, left: 10, right: 10 },
793
+ style: [
794
+ styles4.closeButton,
795
+ { backgroundColor: theme.colors.surface }
796
+ ]
797
+ },
798
+ /* @__PURE__ */ React10.createElement(CloseIcon, { color: theme.colors.text, size: 20 })
799
+ )
800
+ );
801
+ };
802
+ var ModalBody = ({
803
+ children,
804
+ style,
805
+ scrollable = true
806
+ }) => {
807
+ const theme = useTheme();
808
+ if (scrollable) {
809
+ return /* @__PURE__ */ React10.createElement(
810
+ ScrollView,
811
+ {
812
+ style: styles4.bodyScroll,
813
+ contentContainerStyle: [
814
+ styles4.bodyContent,
815
+ { padding: theme.spacing.lg },
816
+ style
817
+ ],
818
+ showsVerticalScrollIndicator: false,
819
+ keyboardShouldPersistTaps: "handled"
820
+ },
821
+ children
822
+ );
823
+ }
824
+ return /* @__PURE__ */ React10.createElement(View4, { style: [styles4.body, { padding: theme.spacing.lg }, style] }, children);
825
+ };
826
+ var ModalFooter = ({
827
+ children,
828
+ style
829
+ }) => {
830
+ const theme = useTheme();
831
+ return /* @__PURE__ */ React10.createElement(
832
+ View4,
833
+ {
834
+ style: [
835
+ styles4.footer,
836
+ {
837
+ borderTopColor: theme.colors.border,
838
+ paddingHorizontal: theme.spacing.lg,
839
+ paddingVertical: theme.spacing.md
840
+ },
841
+ style
842
+ ]
843
+ },
844
+ children
845
+ );
846
+ };
847
+ var styles4 = StyleSheet4.create({
848
+ keyboardView: {
849
+ flex: 1
850
+ },
851
+ overlay: {
852
+ flex: 1,
853
+ justifyContent: "flex-end",
854
+ alignItems: "center"
855
+ // 🔥 ensures modal respects height
856
+ },
857
+ container: {
858
+ width: "100%",
859
+ overflow: "hidden",
860
+ alignSelf: "center",
861
+ flexDirection: "column",
862
+ maxWidth: "100%",
863
+ minHeight: 0,
864
+ flexShrink: 1,
865
+ flexGrow: 1
866
+ },
867
+ header: {
868
+ flexDirection: "row",
869
+ alignItems: "center",
870
+ justifyContent: "space-between",
871
+ borderBottomWidth: 1
872
+ },
873
+ headerContent: {
874
+ flex: 1,
875
+ flexDirection: "row",
876
+ alignItems: "center"
877
+ },
878
+ title: {
879
+ fontWeight: "600"
880
+ },
881
+ closeButton: {
882
+ width: 32,
883
+ height: 32,
884
+ borderRadius: 16,
885
+ alignItems: "center",
886
+ justifyContent: "center",
887
+ marginLeft: 12
888
+ },
889
+ bodyScroll: {
890
+ flexShrink: 1
891
+ },
892
+ bodyContent: {
893
+ flexGrow: 1,
894
+ paddingBottom: 20
895
+ },
896
+ body: {
897
+ flex: 1
898
+ },
899
+ footer: {
900
+ borderTopWidth: 1
901
+ }
902
+ });
903
+
904
+ // src/services/api.ts
905
+ import axios from "axios";
906
+
907
+ // src/config/index.ts
908
+ var BASE_URL = "https://connect-dev.kryptos.io/connect/";
909
+
910
+ // src/services/api.ts
911
+ var api = axios.create({
912
+ baseURL: BASE_URL,
913
+ headers: {
914
+ "Content-Type": "application/json"
915
+ }
916
+ });
917
+ var SCOPES = "openid offline_access profile email holdings:read transactions:read defi-portfolio:read nft-portfolio:read ledger:read tax:read integrations:read holdings:write transactions:write defi-portfolio:write nft-portfolio:write ledger:write tax:write integrations:write";
918
+ async function sendEmailOtp(linkToken, email, clientId) {
919
+ const res = await api.post(
920
+ "/v1/sendEmailOTP",
921
+ {
922
+ email,
923
+ purpose: "login",
924
+ clientId
925
+ },
926
+ {
927
+ headers: {
928
+ "X-Link-Token": linkToken
929
+ }
930
+ }
931
+ );
932
+ return res.data;
933
+ }
934
+ async function loginWithOtp(linkToken, email, code, clientId) {
935
+ const res = await api.post(
936
+ "/v1/loginUserUsingOTP",
937
+ {
938
+ email,
939
+ code,
940
+ clientId,
941
+ purpose: "login"
942
+ },
943
+ {
944
+ headers: {
945
+ "X-Link-Token": linkToken
946
+ }
947
+ }
948
+ );
949
+ return res.data;
950
+ }
951
+ async function createAnonymousUser(linkToken, clientId) {
952
+ const res = await api.post(
953
+ "/v1/anonymousAccountCreation",
954
+ { clientId },
955
+ {
956
+ headers: {
957
+ "X-Link-Token": linkToken
958
+ }
959
+ }
960
+ );
961
+ return res.data;
962
+ }
963
+ async function addUserIntegration(linkToken, integration) {
964
+ const res = await api.post(
965
+ "/v1/addUserIntegration",
966
+ { providers: [...integration] },
967
+ {
968
+ headers: {
969
+ "X-Link-Token": linkToken
970
+ }
971
+ }
972
+ );
973
+ return res.data;
974
+ }
975
+ async function giveUserConsent(linkToken) {
976
+ const res = await api.post(
977
+ "/v1/consent",
978
+ {
979
+ granted_scopes: SCOPES,
980
+ user_consent: true
981
+ },
982
+ {
983
+ headers: {
984
+ "X-Link-Token": linkToken
985
+ }
986
+ }
987
+ );
988
+ return res.data;
989
+ }
990
+ async function testCredentials(linkToken, data) {
991
+ const res = await api.post("/v1/api/testCredentials", data, {
992
+ headers: {
993
+ "X-Link-Token": linkToken
994
+ }
995
+ });
996
+ return res.data;
997
+ }
998
+ async function getSupportedProviders(linkToken) {
999
+ const res = await api.get("/v1/integrations", {
1000
+ headers: {
1001
+ "X-Link-Token": linkToken
1002
+ }
1003
+ });
1004
+ return res.data;
1005
+ }
1006
+ async function getUserIntegrations(linkToken) {
1007
+ const res = await api.get("/v1/userIntegrations", {
1008
+ headers: {
1009
+ "X-Link-Token": linkToken
1010
+ }
1011
+ });
1012
+ return res.data;
1013
+ }
1014
+ async function getUserUsedChains(linkToken, address) {
1015
+ const res = await api.get("/v1/api/getUserUsedChainV2", {
1016
+ headers: {
1017
+ "X-Link-Token": linkToken
1018
+ },
1019
+ params: {
1020
+ id: address
1021
+ }
1022
+ });
1023
+ return res.data;
1024
+ }
1025
+
1026
+ // src/molecules/ConnectLogo.tsx
1027
+ import React12, { isValidElement } from "react";
1028
+ import {
1029
+ Image,
1030
+ StyleSheet as StyleSheet5,
1031
+ Text as Text5,
1032
+ View as View5
1033
+ } from "react-native";
1034
+
1035
+ // src/assets/UnplugIcon.tsx
1036
+ import React11 from "react";
1037
+ import Svg5, { Path as Path5, Line } from "react-native-svg";
1038
+ var UnplugIcon = ({
1039
+ size = 24,
1040
+ color = "#6B7280"
1041
+ }) => {
1042
+ return /* @__PURE__ */ React11.createElement(Svg5, { width: size, height: size, viewBox: "0 0 24 24", fill: "none" }, /* @__PURE__ */ React11.createElement(
1043
+ Path5,
1044
+ {
1045
+ d: "m19 5 3-3",
1046
+ stroke: color,
1047
+ strokeWidth: 2,
1048
+ strokeLinecap: "round",
1049
+ strokeLinejoin: "round"
1050
+ }
1051
+ ), /* @__PURE__ */ React11.createElement(
1052
+ Path5,
1053
+ {
1054
+ d: "m2 22 3-3",
1055
+ stroke: color,
1056
+ strokeWidth: 2,
1057
+ strokeLinecap: "round",
1058
+ strokeLinejoin: "round"
1059
+ }
1060
+ ), /* @__PURE__ */ React11.createElement(
1061
+ Path5,
1062
+ {
1063
+ d: "M6.3 20.3a2.4 2.4 0 0 0 3.4 0L12 18l-6-6-2.3 2.3a2.4 2.4 0 0 0 0 3.4Z",
1064
+ stroke: color,
1065
+ strokeWidth: 2,
1066
+ strokeLinecap: "round",
1067
+ strokeLinejoin: "round"
1068
+ }
1069
+ ), /* @__PURE__ */ React11.createElement(
1070
+ Path5,
1071
+ {
1072
+ d: "m18 12-6-6 2.3-2.3a2.4 2.4 0 0 1 3.4 0l2.6 2.6a2.4 2.4 0 0 1 0 3.4Z",
1073
+ stroke: color,
1074
+ strokeWidth: 2,
1075
+ strokeLinecap: "round",
1076
+ strokeLinejoin: "round"
1077
+ }
1078
+ ), /* @__PURE__ */ React11.createElement(
1079
+ Line,
1080
+ {
1081
+ x1: 7.5,
1082
+ y1: 13.5,
1083
+ x2: 10.5,
1084
+ y2: 10.5,
1085
+ stroke: color,
1086
+ strokeWidth: 2,
1087
+ strokeLinecap: "round"
1088
+ }
1089
+ ));
1090
+ };
1091
+
1092
+ // src/molecules/ConnectLogo.tsx
1093
+ var KryptosLogo = () => {
1094
+ const theme = useTheme();
1095
+ return /* @__PURE__ */ React12.createElement(
1096
+ View5,
1097
+ {
1098
+ style: [styles5.logoContainer, { backgroundColor: theme.colors.surface }]
1099
+ },
1100
+ /* @__PURE__ */ React12.createElement(LogoIcon, { size: 36 })
1101
+ );
1102
+ };
1103
+ var ConnectLogo = () => {
1104
+ const { appName, appLogo } = useKryptosConnect();
1105
+ const theme = useTheme();
1106
+ const isValidUrl = (str) => {
1107
+ try {
1108
+ new URL(str);
1109
+ return true;
1110
+ } catch {
1111
+ return false;
1112
+ }
1113
+ };
1114
+ const renderLogo = () => {
1115
+ if (isValidElement(appLogo)) {
1116
+ return appLogo;
1117
+ } else if (typeof appLogo === "string" && isValidUrl(appLogo)) {
1118
+ return /* @__PURE__ */ React12.createElement(
1119
+ Image,
1120
+ {
1121
+ source: { uri: appLogo },
1122
+ style: styles5.appLogoImage,
1123
+ resizeMode: "contain"
1124
+ }
1125
+ );
1126
+ } else if (typeof appLogo === "number" || typeof appLogo === "object" && appLogo !== null) {
1127
+ return /* @__PURE__ */ React12.createElement(
1128
+ Image,
1129
+ {
1130
+ source: appLogo,
1131
+ style: styles5.appLogoImage,
1132
+ resizeMode: "contain"
1133
+ }
1134
+ );
1135
+ } else if (appName) {
1136
+ return /* @__PURE__ */ React12.createElement(Text5, { style: [styles5.appLogoText, { color: theme.colors.text }] }, appName.charAt(0).toUpperCase());
1137
+ }
1138
+ return /* @__PURE__ */ React12.createElement(Text5, { style: [styles5.appLogoText, { color: theme.colors.text }] }, "?");
1139
+ };
1140
+ return /* @__PURE__ */ React12.createElement(View5, { style: styles5.container }, /* @__PURE__ */ React12.createElement(KryptosLogo, null), /* @__PURE__ */ React12.createElement(View5, { style: styles5.iconContainer }, /* @__PURE__ */ React12.createElement(UnplugIcon, { size: 24, color: theme.colors.textSecondary })), /* @__PURE__ */ React12.createElement(
1141
+ View5,
1142
+ {
1143
+ style: [
1144
+ styles5.logoContainer,
1145
+ { backgroundColor: theme.colors.surface }
1146
+ ]
1147
+ },
1148
+ renderLogo()
1149
+ ));
1150
+ };
1151
+ var styles5 = StyleSheet5.create({
1152
+ container: {
1153
+ flexDirection: "row",
1154
+ alignItems: "center",
1155
+ justifyContent: "center",
1156
+ marginVertical: 24,
1157
+ // theme.spacing.xxl
1158
+ gap: 12
1159
+ // theme.spacing.md
1160
+ },
1161
+ logoContainer: {
1162
+ width: 56,
1163
+ height: 56,
1164
+ borderRadius: 12,
1165
+ // theme.borderRadius.md
1166
+ alignItems: "center",
1167
+ justifyContent: "center",
1168
+ overflow: "hidden"
1169
+ },
1170
+ iconContainer: {
1171
+ paddingHorizontal: 8
1172
+ // theme.spacing.sm
1173
+ },
1174
+ appLogoImage: {
1175
+ width: 32,
1176
+ height: 32
1177
+ },
1178
+ appLogoText: {
1179
+ fontSize: 24,
1180
+ // theme.fontSize.xxxl
1181
+ fontWeight: "700"
1182
+ }
1183
+ });
1184
+
1185
+ // src/molecules/Auth.tsx
1186
+ var Auth = ({
1187
+ open,
1188
+ onEmailSuccess,
1189
+ onGuestSuccess,
1190
+ onClose
1191
+ }) => {
1192
+ const { appName, linkToken, clientId, setUser, setEmail } = useKryptosConnect();
1193
+ const theme = useTheme();
1194
+ const [isLoading, setIsLoading] = React13.useState(false);
1195
+ const [errorMessage, setErrorMessage] = React13.useState("");
1196
+ const [emailValue, setEmailValue] = React13.useState("");
1197
+ const [emailError, setEmailError] = React13.useState("");
1198
+ const [loadingType, setLoadingType] = React13.useState(null);
1199
+ const validateEmail = (email) => {
1200
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1201
+ if (!email) {
1202
+ setEmailError("Email is required");
1203
+ return false;
1204
+ }
1205
+ if (!emailRegex.test(email)) {
1206
+ setEmailError("Invalid email address");
1207
+ return false;
1208
+ }
1209
+ setEmailError("");
1210
+ return true;
1211
+ };
1212
+ const handleClose = () => {
1213
+ onClose();
1214
+ setEmailValue("");
1215
+ setEmailError("");
1216
+ setErrorMessage("");
1217
+ };
1218
+ const handleEmailSubmit = async () => {
1219
+ if (!validateEmail(emailValue)) return;
1220
+ try {
1221
+ setIsLoading(true);
1222
+ setLoadingType("email");
1223
+ setErrorMessage("");
1224
+ await sendEmailOtp(linkToken, emailValue, clientId);
1225
+ setEmail(emailValue);
1226
+ setEmailError("");
1227
+ onEmailSuccess();
1228
+ } catch (error) {
1229
+ const err = error;
1230
+ setErrorMessage(
1231
+ err?.response?.data?.message || "Failed to send email OTP"
1232
+ );
1233
+ } finally {
1234
+ setIsLoading(false);
1235
+ setLoadingType(null);
1236
+ }
1237
+ };
1238
+ const handleContinueAsGuest = async () => {
1239
+ try {
1240
+ setIsLoading(true);
1241
+ setLoadingType("guest");
1242
+ setErrorMessage("");
1243
+ const res = await createAnonymousUser(linkToken, clientId);
1244
+ setUser(res);
1245
+ setEmailError("");
1246
+ onGuestSuccess();
1247
+ } catch (error) {
1248
+ const err = error;
1249
+ console.error(error);
1250
+ setErrorMessage(
1251
+ err?.response?.data?.message || "Failed to continue as guest"
1252
+ );
1253
+ } finally {
1254
+ setIsLoading(false);
1255
+ setLoadingType(null);
1256
+ }
1257
+ };
1258
+ const infoSections = [
1259
+ {
1260
+ icon: /* @__PURE__ */ React13.createElement(LinkIcon, { size: 20, color: theme.colors.primary }),
1261
+ title: "Simple and secure",
1262
+ text: "Connect your Web3 accounts with Kryptos in just a few clicks"
1263
+ },
1264
+ {
1265
+ icon: /* @__PURE__ */ React13.createElement(ShieldIcon, { size: 20, color: theme.colors.primary }),
1266
+ title: "Control what you share",
1267
+ text: "We never share your data without your permission"
1268
+ }
1269
+ ];
1270
+ return /* @__PURE__ */ React13.createElement(Modal, { isOpen: open, onClose: handleClose, size: "full" }, /* @__PURE__ */ React13.createElement(ModalHeader, { onClose: handleClose }, ""), /* @__PURE__ */ React13.createElement(ModalBody, null, /* @__PURE__ */ React13.createElement(View6, { style: styles6.container }, /* @__PURE__ */ React13.createElement(Text6, { style: [styles6.title, { color: theme.colors.text }] }, "Connect ", appName, " to your Kryptos account"), /* @__PURE__ */ React13.createElement(ConnectLogo, null), infoSections.map((section, index) => /* @__PURE__ */ React13.createElement(View6, { key: `info-${index}`, style: styles6.infoSection }, /* @__PURE__ */ React13.createElement(View6, { style: styles6.infoIcon }, section.icon), /* @__PURE__ */ React13.createElement(View6, { style: styles6.infoContent }, /* @__PURE__ */ React13.createElement(Text6, { style: [styles6.infoTitle, { color: theme.colors.text }] }, section.title), /* @__PURE__ */ React13.createElement(
1271
+ Text6,
1272
+ {
1273
+ style: [
1274
+ styles6.infoDescription,
1275
+ { color: theme.colors.textSecondary }
1276
+ ]
1277
+ },
1278
+ section.text
1279
+ )))), errorMessage ? /* @__PURE__ */ React13.createElement(Alert, { variant: "destructive" }, /* @__PURE__ */ React13.createElement(AlertDescription, null, errorMessage)) : null, /* @__PURE__ */ React13.createElement(
1280
+ Input,
1281
+ {
1282
+ placeholder: "Enter email address",
1283
+ value: emailValue,
1284
+ onChangeText: (text) => {
1285
+ setEmailValue(text);
1286
+ if (emailError) validateEmail(text);
1287
+ },
1288
+ error: emailError,
1289
+ keyboardType: "email-address",
1290
+ autoCapitalize: "none",
1291
+ autoCorrect: false
1292
+ }
1293
+ ), /* @__PURE__ */ React13.createElement(Alert, { variant: "default" }, /* @__PURE__ */ React13.createElement(AlertDescription, null, "Sign in or create your Kryptos account with your email for quicker access next time.")), /* @__PURE__ */ React13.createElement(
1294
+ Button,
1295
+ {
1296
+ variant: "outline",
1297
+ size: "lg",
1298
+ onPress: handleEmailSubmit,
1299
+ loading: loadingType === "email",
1300
+ disabled: isLoading,
1301
+ style: styles6.button
1302
+ },
1303
+ "Continue"
1304
+ ), /* @__PURE__ */ React13.createElement(Text6, { style: [styles6.footer, { color: theme.colors.textSecondary }] }, "By continuing, you agree to Kryptos", " ", /* @__PURE__ */ React13.createElement(
1305
+ Text6,
1306
+ {
1307
+ style: {
1308
+ color: theme.colors.primary,
1309
+ textDecorationLine: "underline"
1310
+ }
1311
+ },
1312
+ "Privacy Policy"
1313
+ )), /* @__PURE__ */ React13.createElement(
1314
+ Button,
1315
+ {
1316
+ variant: "ghost",
1317
+ size: "lg",
1318
+ onPress: handleContinueAsGuest,
1319
+ loading: loadingType === "guest",
1320
+ disabled: isLoading,
1321
+ style: styles6.button
1322
+ },
1323
+ "Continue as guest"
1324
+ ))));
1325
+ };
1326
+ var styles6 = StyleSheet6.create({
1327
+ container: {
1328
+ flex: 1
1329
+ },
1330
+ title: {
1331
+ fontSize: 18,
1332
+ // theme.fontSize.xl
1333
+ fontWeight: "600",
1334
+ textAlign: "center",
1335
+ marginBottom: 16
1336
+ // theme.spacing.lg - consistent section spacing
1337
+ },
1338
+ infoSection: {
1339
+ flexDirection: "row",
1340
+ marginBottom: 16,
1341
+ // theme.spacing.lg
1342
+ alignItems: "flex-start"
1343
+ },
1344
+ infoIcon: {
1345
+ width: 32,
1346
+ height: 32,
1347
+ borderRadius: 16,
1348
+ // theme.borderRadius.lg
1349
+ alignItems: "center",
1350
+ justifyContent: "center",
1351
+ marginRight: 12
1352
+ // theme.spacing.md
1353
+ },
1354
+ infoContent: {
1355
+ flex: 1
1356
+ },
1357
+ infoTitle: {
1358
+ fontSize: 14,
1359
+ // theme.fontSize.md
1360
+ fontWeight: "600",
1361
+ marginBottom: 4
1362
+ // theme.spacing.xs
1363
+ },
1364
+ infoDescription: {
1365
+ fontSize: 13,
1366
+ lineHeight: 18
1367
+ },
1368
+ button: {
1369
+ width: "100%",
1370
+ marginTop: 16
1371
+ // theme.spacing.lg - consistent button spacing
1372
+ },
1373
+ footer: {
1374
+ fontSize: 12,
1375
+ // theme.fontSize.sm
1376
+ textAlign: "center",
1377
+ marginTop: 16
1378
+ // theme.spacing.lg
1379
+ }
1380
+ });
1381
+
1382
+ // src/components/OTP.tsx
1383
+ import React14 from "react";
1384
+ import {
1385
+ View as View7,
1386
+ TextInput as TextInput2,
1387
+ Text as Text7,
1388
+ StyleSheet as StyleSheet7
1389
+ } from "react-native";
1390
+ var OTP = ({
1391
+ length = 6,
1392
+ value = "",
1393
+ onChange,
1394
+ onComplete,
1395
+ error,
1396
+ label,
1397
+ disabled = false,
1398
+ containerStyle,
1399
+ inputStyle,
1400
+ setErrorMessage
1401
+ }) => {
1402
+ const theme = useTheme();
1403
+ const AUTO_SUBMIT_DELAY = 500;
1404
+ const [otp, setOtp] = React14.useState(
1405
+ value.split("").concat(Array(length).fill("")).slice(0, length)
1406
+ );
1407
+ const inputRefs = React14.useRef([]);
1408
+ React14.useEffect(() => {
1409
+ const isComplete = otp.every((digit) => digit !== "");
1410
+ let timer;
1411
+ if (isComplete && onComplete) {
1412
+ timer = setTimeout(() => {
1413
+ onComplete(otp.join(""));
1414
+ }, AUTO_SUBMIT_DELAY);
1415
+ }
1416
+ return () => {
1417
+ if (timer) clearTimeout(timer);
1418
+ };
1419
+ }, [otp, onComplete]);
1420
+ React14.useEffect(() => {
1421
+ setTimeout(() => {
1422
+ inputRefs.current[0]?.focus();
1423
+ }, 100);
1424
+ }, []);
1425
+ const handleChange = React14.useCallback(
1426
+ (index, val) => {
1427
+ if (disabled) return;
1428
+ setErrorMessage("");
1429
+ const numericValue = val.replace(/[^0-9]/g, "");
1430
+ const newValue = numericValue.slice(-1);
1431
+ if (val && !numericValue) {
1432
+ return;
1433
+ }
1434
+ const newOtp = [...otp];
1435
+ newOtp[index] = newValue;
1436
+ setOtp(newOtp);
1437
+ const otpString = newOtp.join("");
1438
+ onChange?.(otpString);
1439
+ if (newValue && index < length - 1) {
1440
+ inputRefs.current[index + 1]?.focus();
1441
+ }
1442
+ if (otpString.length === length && !otpString.includes("")) {
1443
+ onComplete?.(otpString);
1444
+ }
1445
+ },
1446
+ [otp, length, onChange, onComplete, disabled]
1447
+ );
1448
+ const handleKeyPress = React14.useCallback(
1449
+ (index, e) => {
1450
+ if (disabled) return;
1451
+ if (e.nativeEvent.key === "Backspace") {
1452
+ if (!otp[index] && index > 0) {
1453
+ inputRefs.current[index - 1]?.focus();
1454
+ } else {
1455
+ const newOtp = [...otp];
1456
+ newOtp[index] = "";
1457
+ setOtp(newOtp);
1458
+ onChange?.(newOtp.join(""));
1459
+ }
1460
+ }
1461
+ },
1462
+ [otp, onChange, disabled]
1463
+ );
1464
+ const getBorderColor = (index) => {
1465
+ if (error) return theme.colors.error;
1466
+ if (otp[index]) return theme.colors.success;
1467
+ return theme.colors.border;
1468
+ };
1469
+ return /* @__PURE__ */ React14.createElement(View7, { style: [styles7.wrapper, containerStyle] }, label && /* @__PURE__ */ React14.createElement(
1470
+ Text7,
1471
+ {
1472
+ style: [
1473
+ styles7.label,
1474
+ { color: theme.colors.text, fontSize: theme.fontSize.sm }
1475
+ ]
1476
+ },
1477
+ label
1478
+ ), /* @__PURE__ */ React14.createElement(View7, { style: styles7.container }, Array.from({ length }, (_, index) => /* @__PURE__ */ React14.createElement(
1479
+ TextInput2,
1480
+ {
1481
+ key: index,
1482
+ ref: (el) => inputRefs.current[index] = el,
1483
+ style: [
1484
+ styles7.input,
1485
+ {
1486
+ backgroundColor: theme.colors.surface,
1487
+ borderColor: getBorderColor(index),
1488
+ color: theme.colors.text,
1489
+ fontSize: theme.fontSize.xxl,
1490
+ borderRadius: theme.borderRadius.md
1491
+ },
1492
+ inputStyle
1493
+ ],
1494
+ keyboardType: "numeric",
1495
+ maxLength: 1,
1496
+ value: otp[index] || "",
1497
+ onChangeText: (val) => handleChange(index, val),
1498
+ onKeyPress: (e) => handleKeyPress(index, e),
1499
+ editable: !disabled,
1500
+ selectTextOnFocus: true,
1501
+ caretHidden: true
1502
+ }
1503
+ ))), error && /* @__PURE__ */ React14.createElement(
1504
+ Text7,
1505
+ {
1506
+ style: [
1507
+ styles7.error,
1508
+ { color: theme.colors.error, fontSize: theme.fontSize.sm }
1509
+ ]
1510
+ },
1511
+ error
1512
+ ));
1513
+ };
1514
+ var styles7 = StyleSheet7.create({
1515
+ wrapper: {
1516
+ marginBottom: 16
1517
+ // theme.spacing.lg
1518
+ },
1519
+ label: {
1520
+ fontWeight: "500",
1521
+ marginBottom: 12,
1522
+ // theme.spacing.md - consistent label spacing
1523
+ textAlign: "center"
1524
+ },
1525
+ container: {
1526
+ flexDirection: "row",
1527
+ justifyContent: "center",
1528
+ gap: 8
1529
+ // theme.spacing.sm
1530
+ },
1531
+ input: {
1532
+ width: 48,
1533
+ height: 56,
1534
+ borderWidth: 1,
1535
+ textAlign: "center",
1536
+ fontWeight: "600"
1537
+ },
1538
+ error: {
1539
+ marginTop: 12,
1540
+ // theme.spacing.md - consistent error spacing
1541
+ textAlign: "center"
1542
+ }
1543
+ });
1544
+
1545
+ // src/molecules/Init.tsx
1546
+ import React15 from "react";
1547
+ import { ActivityIndicator as ActivityIndicator2, StyleSheet as StyleSheet8, Text as Text8, View as View8 } from "react-native";
1548
+ var Init = ({
1549
+ open,
1550
+ onSuccess,
1551
+ onClose,
1552
+ generateLinkToken
1553
+ }) => {
1554
+ const { setIsInitialized, isInitialized, setLinkToken } = useKryptosConnect();
1555
+ const theme = useTheme();
1556
+ const [isFetching, setIsFetching] = React15.useState(false);
1557
+ const [error, setError] = React15.useState(null);
1558
+ const fetchLinkToken = React15.useCallback(async () => {
1559
+ if (!open) return;
1560
+ setIsFetching(true);
1561
+ setError(null);
1562
+ try {
1563
+ const linkToken = await generateLinkToken();
1564
+ if (!linkToken) {
1565
+ setIsInitialized(false);
1566
+ setError("Failed to fetch link token. Please try again.");
1567
+ return;
1568
+ }
1569
+ setLinkToken(linkToken);
1570
+ setIsInitialized(true);
1571
+ onSuccess();
1572
+ } catch (err) {
1573
+ console.error("Failed to fetch link token:", err);
1574
+ setIsInitialized(false);
1575
+ setError("Failed to fetch link token. Please try again.");
1576
+ } finally {
1577
+ setIsFetching(false);
1578
+ }
1579
+ }, [generateLinkToken, open, setIsInitialized, setLinkToken, onSuccess]);
1580
+ React15.useEffect(() => {
1581
+ fetchLinkToken();
1582
+ }, [fetchLinkToken]);
1583
+ return /* @__PURE__ */ React15.createElement(Modal, { isOpen: open, onClose, size: "xs" }, /* @__PURE__ */ React15.createElement(ModalHeader, { onClose }, "Kryptos Connect"), /* @__PURE__ */ React15.createElement(ModalBody, null, /* @__PURE__ */ React15.createElement(View8, { style: styles8.container }, isFetching && /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(
1584
+ ActivityIndicator2,
1585
+ {
1586
+ size: "large",
1587
+ color: theme.colors.primary,
1588
+ style: styles8.spinner
1589
+ }
1590
+ ), /* @__PURE__ */ React15.createElement(Text8, { style: [styles8.message, { color: theme.colors.text }] }, isInitialized ? "Fetching link token..." : "Initializing...")), !isFetching && error && /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(Alert, { variant: "destructive" }, /* @__PURE__ */ React15.createElement(AlertDescription, null, error)), /* @__PURE__ */ React15.createElement(
1591
+ Button,
1592
+ {
1593
+ variant: "primary",
1594
+ size: "lg",
1595
+ onPress: fetchLinkToken,
1596
+ style: styles8.retryButton
1597
+ },
1598
+ "Retry"
1599
+ )))));
1600
+ };
1601
+ var styles8 = StyleSheet8.create({
1602
+ container: {
1603
+ flex: 1,
1604
+ alignItems: "center",
1605
+ justifyContent: "center",
1606
+ paddingVertical: 16
1607
+ // theme.spacing.lg
1608
+ },
1609
+ spinner: {
1610
+ marginBottom: 16
1611
+ // theme.spacing.lg
1612
+ },
1613
+ message: {
1614
+ fontSize: 16,
1615
+ // theme.fontSize.lg
1616
+ fontWeight: "500"
1617
+ },
1618
+ errorText: {
1619
+ textAlign: "center"
1620
+ },
1621
+ retryButton: {
1622
+ marginTop: 12,
1623
+ width: "100%"
1624
+ }
1625
+ });
1626
+
1627
+ // src/molecules/Integration.tsx
1628
+ import React27 from "react";
1629
+ import {
1630
+ FlatList,
1631
+ Image as Image3,
1632
+ StyleSheet as StyleSheet12,
1633
+ Text as Text11,
1634
+ TouchableOpacity as TouchableOpacity5,
1635
+ View as View12
1636
+ } from "react-native";
1637
+
1638
+ // src/assets/ArrowLeftIcon.tsx
1639
+ import React16 from "react";
1640
+ import Svg6, { Path as Path6 } from "react-native-svg";
1641
+ var ArrowLeftIcon = ({
1642
+ size = 20,
1643
+ color = "#000"
1644
+ }) => {
1645
+ return /* @__PURE__ */ React16.createElement(Svg6, { width: size, height: size, viewBox: "0 0 24 24", fill: "none" }, /* @__PURE__ */ React16.createElement(
1646
+ Path6,
1647
+ {
1648
+ d: "M19 12H5M12 19l-7-7 7-7",
1649
+ stroke: color,
1650
+ strokeWidth: 2,
1651
+ strokeLinecap: "round",
1652
+ strokeLinejoin: "round"
1653
+ }
1654
+ ));
1655
+ };
1656
+
1657
+ // src/assets/CheckCircleIcon.tsx
1658
+ import React17 from "react";
1659
+ import Svg7, { Path as Path7, Circle } from "react-native-svg";
1660
+ var CheckCircleIcon = ({
1661
+ size = 20,
1662
+ color = "#10B981"
1663
+ }) => {
1664
+ return /* @__PURE__ */ React17.createElement(Svg7, { width: size, height: size, viewBox: "0 0 24 24", fill: "none" }, /* @__PURE__ */ React17.createElement(
1665
+ Circle,
1666
+ {
1667
+ cx: 12,
1668
+ cy: 12,
1669
+ r: 10,
1670
+ stroke: color,
1671
+ strokeWidth: 2
1672
+ }
1673
+ ), /* @__PURE__ */ React17.createElement(
1674
+ Path7,
1675
+ {
1676
+ d: "m9 12 2 2 4-4",
1677
+ stroke: color,
1678
+ strokeWidth: 2,
1679
+ strokeLinecap: "round",
1680
+ strokeLinejoin: "round"
1681
+ }
1682
+ ));
1683
+ };
1684
+
1685
+ // src/assets/LoaderIcon.tsx
1686
+ import React18 from "react";
1687
+ import { Animated, Easing } from "react-native";
1688
+ import Svg8, { Path as Path8 } from "react-native-svg";
1689
+ var AnimatedSvg = Animated.createAnimatedComponent(Svg8);
1690
+ var LoaderIcon = ({
1691
+ size = 20,
1692
+ color = "#00C693"
1693
+ }) => {
1694
+ const rotateAnim = React18.useRef(new Animated.Value(0)).current;
1695
+ React18.useEffect(() => {
1696
+ Animated.loop(
1697
+ Animated.timing(rotateAnim, {
1698
+ toValue: 1,
1699
+ duration: 1e3,
1700
+ easing: Easing.linear,
1701
+ useNativeDriver: true
1702
+ })
1703
+ ).start();
1704
+ }, [rotateAnim]);
1705
+ const spin = rotateAnim.interpolate({
1706
+ inputRange: [0, 1],
1707
+ outputRange: ["0deg", "360deg"]
1708
+ });
1709
+ return /* @__PURE__ */ React18.createElement(
1710
+ AnimatedSvg,
1711
+ {
1712
+ width: size,
1713
+ height: size,
1714
+ viewBox: "0 0 24 24",
1715
+ fill: "none",
1716
+ style: { transform: [{ rotate: spin }] }
1717
+ },
1718
+ /* @__PURE__ */ React18.createElement(
1719
+ Path8,
1720
+ {
1721
+ d: "M21 12a9 9 0 1 1-6.219-8.56",
1722
+ stroke: color,
1723
+ strokeWidth: 2,
1724
+ strokeLinecap: "round",
1725
+ strokeLinejoin: "round"
1726
+ }
1727
+ )
1728
+ );
1729
+ };
1730
+
1731
+ // src/assets/SuccessIcon.tsx
1732
+ import React19 from "react";
1733
+ import Svg9, { Circle as Circle2, Path as Path9 } from "react-native-svg";
1734
+ var SuccessIcon = ({ size = 64 }) => {
1735
+ return /* @__PURE__ */ React19.createElement(Svg9, { width: size, height: size, viewBox: "0 0 64 64", fill: "none" }, /* @__PURE__ */ React19.createElement(
1736
+ Circle2,
1737
+ {
1738
+ cx: 32,
1739
+ cy: 32,
1740
+ r: 30,
1741
+ fill: "#00C693",
1742
+ opacity: 0.1
1743
+ }
1744
+ ), /* @__PURE__ */ React19.createElement(
1745
+ Circle2,
1746
+ {
1747
+ cx: 32,
1748
+ cy: 32,
1749
+ r: 24,
1750
+ fill: "#00C693"
1751
+ }
1752
+ ), /* @__PURE__ */ React19.createElement(
1753
+ Path9,
1754
+ {
1755
+ d: "M24 32l6 6 12-12",
1756
+ stroke: "white",
1757
+ strokeWidth: 3,
1758
+ strokeLinecap: "round",
1759
+ strokeLinejoin: "round"
1760
+ }
1761
+ ));
1762
+ };
1763
+
1764
+ // src/assets/ErrorIcon.tsx
1765
+ import React20 from "react";
1766
+ import Svg10, { Circle as Circle3, Path as Path10 } from "react-native-svg";
1767
+ var ErrorIcon = ({ size = 64 }) => {
1768
+ return /* @__PURE__ */ React20.createElement(Svg10, { width: size, height: size, viewBox: "0 0 64 64", fill: "none" }, /* @__PURE__ */ React20.createElement(
1769
+ Circle3,
1770
+ {
1771
+ cx: 32,
1772
+ cy: 32,
1773
+ r: 30,
1774
+ fill: "#EF4444",
1775
+ opacity: 0.1
1776
+ }
1777
+ ), /* @__PURE__ */ React20.createElement(
1778
+ Circle3,
1779
+ {
1780
+ cx: 32,
1781
+ cy: 32,
1782
+ r: 24,
1783
+ fill: "#EF4444"
1784
+ }
1785
+ ), /* @__PURE__ */ React20.createElement(
1786
+ Path10,
1787
+ {
1788
+ d: "M24 24l16 16M40 24l-16 16",
1789
+ stroke: "white",
1790
+ strokeWidth: 3,
1791
+ strokeLinecap: "round",
1792
+ strokeLinejoin: "round"
1793
+ }
1794
+ ));
1795
+ };
1796
+
1797
+ // src/assets/SearchIcon.tsx
1798
+ import React21 from "react";
1799
+ import Svg11, { Circle as Circle4, Path as Path11 } from "react-native-svg";
1800
+
1801
+ // src/assets/PlusIcon.tsx
1802
+ import React22 from "react";
1803
+ import Svg12, { Path as Path12 } from "react-native-svg";
1804
+ var PlusIcon = ({
1805
+ size = 14,
1806
+ color = "#6B7280"
1807
+ }) => {
1808
+ return /* @__PURE__ */ React22.createElement(Svg12, { width: size, height: size, viewBox: "0 0 14 14", fill: "none" }, /* @__PURE__ */ React22.createElement(
1809
+ Path12,
1810
+ {
1811
+ d: "M7 3.5v7M3.5 7h7",
1812
+ stroke: color,
1813
+ strokeWidth: 2,
1814
+ strokeLinecap: "round"
1815
+ }
1816
+ ));
1817
+ };
1818
+
1819
+ // src/wallet-connect/index.tsx
1820
+ import { useAccount, useAppKit } from "@reown/appkit-react-native";
1821
+ import React24, { useState } from "react";
1822
+ import {
1823
+ ScrollView as ScrollView2,
1824
+ StyleSheet as StyleSheet9,
1825
+ Text as Text9,
1826
+ TouchableOpacity as TouchableOpacity3,
1827
+ View as View9
1828
+ } from "react-native";
1829
+
1830
+ // src/utils/uuid.ts
1831
+ function generateUUID() {
1832
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
1833
+ const r = Math.random() * 16 | 0;
1834
+ const v = c === "x" ? r : r & 3 | 8;
1835
+ return v.toString(16);
1836
+ });
1837
+ }
1838
+
1839
+ // src/wallet-connect/wallet-connect.tsx
1840
+ import React23 from "react";
1841
+ import { AppKit, AppKitProvider } from "@reown/appkit-react-native";
1842
+
1843
+ // src/wallet-connect/AppKitConfig.ts
1844
+ import "@walletconnect/react-native-compat";
1845
+ import AsyncStorage from "@react-native-async-storage/async-storage";
1846
+ import { createAppKit } from "@reown/appkit-react-native";
1847
+
1848
+ // node_modules/@walletconnect/safe-json/dist/esm/index.js
1849
+ var JSONStringify = (data) => JSON.stringify(data, (_, value) => typeof value === "bigint" ? value.toString() + "n" : value);
1850
+ var JSONParse = (json) => {
1851
+ const numbersBiggerThanMaxInt = /([\[:])?(\d{17,}|(?:[9](?:[1-9]07199254740991|0[1-9]7199254740991|00[8-9]199254740991|007[2-9]99254740991|007199[3-9]54740991|0071992[6-9]4740991|00719925[5-9]740991|007199254[8-9]40991|0071992547[5-9]0991|00719925474[1-9]991|00719925474099[2-9])))([,\}\]])/g;
1852
+ const serializedData = json.replace(numbersBiggerThanMaxInt, '$1"$2n"$3');
1853
+ return JSON.parse(serializedData, (_, value) => {
1854
+ const isCustomFormatBigInt = typeof value === "string" && value.match(/^\d+n$/);
1855
+ if (isCustomFormatBigInt)
1856
+ return BigInt(value.substring(0, value.length - 1));
1857
+ return value;
1858
+ });
1859
+ };
1860
+ function safeJsonParse(value) {
1861
+ if (typeof value !== "string") {
1862
+ throw new Error(`Cannot safe json parse value of type ${typeof value}`);
1863
+ }
1864
+ try {
1865
+ return JSONParse(value);
1866
+ } catch (_a) {
1867
+ return value;
1868
+ }
1869
+ }
1870
+ function safeJsonStringify(value) {
1871
+ return typeof value === "string" ? value : JSONStringify(value) || "";
1872
+ }
1873
+
1874
+ // src/wallet-connect/AppKitConfig.ts
1875
+ import { EthersAdapter } from "@reown/appkit-ethers-react-native";
1876
+ import {
1877
+ arbitrum,
1878
+ avalanche,
1879
+ base,
1880
+ bsc,
1881
+ fantom,
1882
+ gnosis,
1883
+ mainnet,
1884
+ optimism,
1885
+ polygon
1886
+ } from "viem/chains";
1887
+ var ethersAdapter = new EthersAdapter();
1888
+ var storage = {
1889
+ getKeys: async () => {
1890
+ return await AsyncStorage.getAllKeys();
1891
+ },
1892
+ getEntries: async () => {
1893
+ const keys = await AsyncStorage.getAllKeys();
1894
+ return Promise.all(
1895
+ keys.map(async (key) => {
1896
+ const value = await AsyncStorage.getItem(key);
1897
+ return [key, safeJsonParse(value || "")];
1898
+ })
1899
+ );
1900
+ },
1901
+ setItem: async (key, value) => {
1902
+ await AsyncStorage.setItem(key, safeJsonStringify(value));
1903
+ },
1904
+ getItem: async (key) => {
1905
+ const raw = await AsyncStorage.getItem(key);
1906
+ if (!raw) return void 0;
1907
+ return safeJsonParse(raw);
1908
+ },
1909
+ removeItem: async (key) => {
1910
+ await AsyncStorage.removeItem(key);
1911
+ }
1912
+ };
1913
+ var createAppKitInstance = (projectId) => {
1914
+ if (!projectId) {
1915
+ throw new Error("walletConnectProjectId is required to initialize AppKit");
1916
+ }
1917
+ return createAppKit({
1918
+ projectId,
1919
+ networks: [
1920
+ mainnet,
1921
+ arbitrum,
1922
+ avalanche,
1923
+ bsc,
1924
+ fantom,
1925
+ gnosis,
1926
+ optimism,
1927
+ polygon,
1928
+ base
1929
+ ],
1930
+ adapters: [ethersAdapter],
1931
+ features: {
1932
+ swaps: false,
1933
+ socials: false,
1934
+ onramp: false
1935
+ },
1936
+ metadata: {
1937
+ name: "Kryptos Connect",
1938
+ description: "Kryptos Connect",
1939
+ url: "https://kryptos.com",
1940
+ icons: ["https://kryptos.com/icon.png"]
1941
+ },
1942
+ storage
1943
+ });
1944
+ };
1945
+
1946
+ // src/wallet-connect/wallet-connect.tsx
1947
+ var WalletConnectWrapper = ({ children }) => {
1948
+ const { walletConnectProjectId } = useKryptosConnect();
1949
+ const appKit = React23.useMemo(() => {
1950
+ if (!walletConnectProjectId) {
1951
+ console.warn(
1952
+ "walletConnectProjectId is missing in KryptosConnectProvider config"
1953
+ );
1954
+ return null;
1955
+ }
1956
+ return createAppKitInstance(walletConnectProjectId);
1957
+ }, [walletConnectProjectId]);
1958
+ if (!appKit) {
1959
+ return /* @__PURE__ */ React23.createElement(React23.Fragment, null, children);
1960
+ }
1961
+ return /* @__PURE__ */ React23.createElement(AppKitProvider, { instance: appKit }, /* @__PURE__ */ React23.createElement(AppKit, null), children);
1962
+ };
1963
+ var wallet_connect_default = WalletConnectWrapper;
1964
+
1965
+ // src/wallet-connect/index.tsx
1966
+ var WalletConnectComponent = ({
1967
+ integration,
1968
+ onClose,
1969
+ onAddHandle,
1970
+ handleClose,
1971
+ modalOpen,
1972
+ setAddIntegrationMode
1973
+ }) => {
1974
+ const { walletConnectProjectId } = useKryptosConnect();
1975
+ const theme = useTheme();
1976
+ if (!walletConnectProjectId) {
1977
+ return /* @__PURE__ */ React24.createElement(Modal, { isOpen: modalOpen, onClose: handleClose, size: "full" }, /* @__PURE__ */ React24.createElement(ModalHeader, { onClose: handleClose }, /* @__PURE__ */ React24.createElement(View9, { style: styles9.headerContent }, /* @__PURE__ */ React24.createElement(
1978
+ TouchableOpacity3,
1979
+ {
1980
+ onPress: () => {
1981
+ setAddIntegrationMode(null);
1982
+ },
1983
+ style: styles9.backButton
1984
+ },
1985
+ /* @__PURE__ */ React24.createElement(ArrowLeftIcon, { size: 20, color: theme.colors.text })
1986
+ ), /* @__PURE__ */ React24.createElement(Text9, { style: [styles9.headerTitle, { color: theme.colors.text }] }, "Integration"))), /* @__PURE__ */ React24.createElement(ModalBody, { scrollable: false, style: styles9.contentContainer }, /* @__PURE__ */ React24.createElement(View9, { style: styles9.emptyState }, /* @__PURE__ */ React24.createElement(
1987
+ Text9,
1988
+ {
1989
+ style: [styles9.emptyStateTitle, { color: theme.colors.text }]
1990
+ },
1991
+ "WalletConnect is not configured"
1992
+ ), /* @__PURE__ */ React24.createElement(
1993
+ Text9,
1994
+ {
1995
+ style: [
1996
+ styles9.infoText,
1997
+ { color: theme.colors.textSecondary, textAlign: "center" }
1998
+ ]
1999
+ },
2000
+ "Please add a walletConnectProjectId to KryptosConnectProvider to enable wallet connections."
2001
+ ), /* @__PURE__ */ React24.createElement(
2002
+ Button,
2003
+ {
2004
+ variant: "outline",
2005
+ size: "sm",
2006
+ onPress: () => setAddIntegrationMode(null),
2007
+ style: styles9.emptyStateButton
2008
+ },
2009
+ "Go back"
2010
+ ))));
2011
+ }
2012
+ return /* @__PURE__ */ React24.createElement(wallet_connect_default, null, /* @__PURE__ */ React24.createElement(
2013
+ ConnectButton,
2014
+ {
2015
+ integration,
2016
+ onAddHandle,
2017
+ onClose,
2018
+ handleClose,
2019
+ modalOpen,
2020
+ setAddIntegrationMode
2021
+ }
2022
+ ));
2023
+ };
2024
+ function ConnectButton({
2025
+ integration,
2026
+ onAddHandle,
2027
+ handleClose,
2028
+ modalOpen,
2029
+ setAddIntegrationMode
2030
+ }) {
2031
+ const theme = useTheme();
2032
+ const { open, disconnect } = useAppKit();
2033
+ const { address, isConnected, chainId } = useAccount();
2034
+ const { linkToken, user, clientId } = useKryptosConnect();
2035
+ const [selectedChains, setSelectedChains] = useState(/* @__PURE__ */ new Set());
2036
+ const [errorMessage, setErrorMessage] = useState("");
2037
+ const [chainErrors, setChainErrors] = useState({});
2038
+ const [isLoading, setIsLoading] = useState(false);
2039
+ const userUsedChains = integration?.walletSupportedChains || [];
2040
+ const validateForm = () => {
2041
+ if (!address) {
2042
+ setErrorMessage("Please connect a wallet");
2043
+ return false;
2044
+ }
2045
+ if (selectedChains.size === 0) {
2046
+ setErrorMessage("Select at least one chain");
2047
+ return false;
2048
+ }
2049
+ return true;
2050
+ };
2051
+ const onSubmitWalletConnect = async () => {
2052
+ if (!validateForm()) return;
2053
+ try {
2054
+ setIsLoading(true);
2055
+ setErrorMessage("");
2056
+ setChainErrors({});
2057
+ const chainsToProcess = userUsedChains.filter(
2058
+ (c) => selectedChains.has(c.id)
2059
+ );
2060
+ const integrationsToAdd = [];
2061
+ const errors = {};
2062
+ const walletTestsPayload = chainsToProcess.map((chain) => {
2063
+ const walletId = generateUUID();
2064
+ const displaySuffix = address ? address?.length > 8 ? `${address.slice(0, 4)}...${address.slice(-4)}` : address : "";
2065
+ const alias = `${integration.id} - ${chain.id} (${displaySuffix})`;
2066
+ return {
2067
+ chain,
2068
+ walletId,
2069
+ alias,
2070
+ credential: {
2071
+ source: integration.id,
2072
+ credential: {
2073
+ address,
2074
+ userId: user?.user?.uid || "0",
2075
+ projectId: integration.projectId,
2076
+ apiKey: "0",
2077
+ secret: "0",
2078
+ privateKey: "0",
2079
+ alias,
2080
+ walletId,
2081
+ exchange: integration.id
2082
+ }
2083
+ }
2084
+ };
2085
+ });
2086
+ const results = await Promise.allSettled(
2087
+ walletTestsPayload.map(
2088
+ (data) => testCredentials(linkToken, data.credential)
2089
+ )
2090
+ );
2091
+ results.forEach((result, index) => {
2092
+ const { chain, walletId, alias } = walletTestsPayload[index];
2093
+ if (result.status === "fulfilled") {
2094
+ const data = {
2095
+ alias,
2096
+ exchange: integration.id.toLowerCase(),
2097
+ id: integration.id,
2098
+ public_name: integration.public_name,
2099
+ sync_time: (/* @__PURE__ */ new Date()).getTime(),
2100
+ fetchAll: true,
2101
+ logo: integration.logo || null,
2102
+ startTime: null,
2103
+ endTime: null,
2104
+ uid: user?.user?.uid || "",
2105
+ walletId,
2106
+ clientMetadata: {
2107
+ clientId,
2108
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2109
+ },
2110
+ metadata: {
2111
+ environment: "sandbox"
2112
+ },
2113
+ addedOn: (/* @__PURE__ */ new Date()).getTime(),
2114
+ default_chain: chain.name,
2115
+ default_chain_logo: chain.logo || null,
2116
+ type: integration.type,
2117
+ isNftSupported: integration.isEvmWallet || integration.nftSupport || false,
2118
+ chainId: chain.chainId || chain.id,
2119
+ address
2120
+ };
2121
+ integrationsToAdd.push(data);
2122
+ } else {
2123
+ errors[chain.id] = "Wallet verification failed";
2124
+ }
2125
+ });
2126
+ if (integrationsToAdd.length > 0) {
2127
+ onAddHandle(integrationsToAdd);
2128
+ setChainErrors({});
2129
+ setErrorMessage("");
2130
+ } else {
2131
+ setErrorMessage("No integrations could be added. Please try again.");
2132
+ }
2133
+ } catch (error) {
2134
+ const err = error;
2135
+ console.error(error);
2136
+ setErrorMessage(
2137
+ err?.response?.data?.message || "Failed to add integration"
2138
+ );
2139
+ } finally {
2140
+ setIsLoading(false);
2141
+ }
2142
+ };
2143
+ const toggleChainSelection = (chainId2) => {
2144
+ const newSelected = new Set(selectedChains);
2145
+ if (newSelected.has(chainId2)) {
2146
+ newSelected.delete(chainId2);
2147
+ } else {
2148
+ newSelected.add(chainId2);
2149
+ }
2150
+ setSelectedChains(newSelected);
2151
+ if (chainErrors[chainId2]) {
2152
+ const newErrors = { ...chainErrors };
2153
+ delete newErrors[chainId2];
2154
+ setChainErrors(newErrors);
2155
+ }
2156
+ };
2157
+ return /* @__PURE__ */ React24.createElement(Modal, { isOpen: modalOpen, onClose: handleClose, size: "full" }, /* @__PURE__ */ React24.createElement(ModalHeader, { onClose: handleClose }, /* @__PURE__ */ React24.createElement(View9, { style: styles9.headerContent }, /* @__PURE__ */ React24.createElement(
2158
+ TouchableOpacity3,
2159
+ {
2160
+ onPress: () => {
2161
+ setAddIntegrationMode(null);
2162
+ },
2163
+ style: styles9.backButton
2164
+ },
2165
+ /* @__PURE__ */ React24.createElement(ArrowLeftIcon, { size: 20, color: theme.colors.text })
2166
+ ), /* @__PURE__ */ React24.createElement(Text9, { style: [styles9.headerTitle, { color: theme.colors.text }] }, "Integration"))), /* @__PURE__ */ React24.createElement(ModalBody, { scrollable: false, style: styles9.contentContainer }, !isConnected ? /* @__PURE__ */ React24.createElement(View9, null, /* @__PURE__ */ React24.createElement(Text9, { style: [styles9.infoText, { color: theme.colors.text }] }, "Connect your wallet to continue"), /* @__PURE__ */ React24.createElement(
2167
+ Button,
2168
+ {
2169
+ variant: "primary",
2170
+ size: "sm",
2171
+ onPress: () => open({ view: "Connect" })
2172
+ },
2173
+ "Connect Wallet"
2174
+ )) : /* @__PURE__ */ React24.createElement(View9, null, /* @__PURE__ */ React24.createElement(Text9, { style: [styles9.connectedTitle, { color: theme.colors.text }] }, "Wallet Connected"), /* @__PURE__ */ React24.createElement(Text9, { style: [styles9.connectedText, { color: theme.colors.text }] }, "Address: ", address), /* @__PURE__ */ React24.createElement(Text9, { style: [styles9.connectedText, { color: theme.colors.text }] }, "Chain: ", chainId), /* @__PURE__ */ React24.createElement(Button, { variant: "ghost", size: "sm", onPress: () => disconnect() }, "Disconnect Wallet"), userUsedChains.length > 0 && address && /* @__PURE__ */ React24.createElement(View9, { style: styles9.chainSelection }, /* @__PURE__ */ React24.createElement(Text9, { style: [styles9.chainTitle, { color: theme.colors.text }] }, "Select Chains to Add:"), /* @__PURE__ */ React24.createElement(ScrollView2, { contentContainerStyle: styles9.scrollViewContent }, /* @__PURE__ */ React24.createElement(View9, { style: styles9.chainChips }, userUsedChains.map((chain) => {
2175
+ const isSelected = selectedChains.has(chain.id);
2176
+ const hasError = chainErrors[chain.id];
2177
+ return /* @__PURE__ */ React24.createElement(
2178
+ TouchableOpacity3,
2179
+ {
2180
+ onPress: () => toggleChainSelection(chain.id),
2181
+ style: styles9.chainButton,
2182
+ key: chain.id
2183
+ },
2184
+ /* @__PURE__ */ React24.createElement(
2185
+ View9,
2186
+ {
2187
+ style: [
2188
+ styles9.chainChip,
2189
+ {
2190
+ backgroundColor: hasError ? theme.colors.errorLight : isSelected ? theme.colors.primary + "20" : theme.colors.surface,
2191
+ borderColor: hasError ? theme.colors.error : isSelected ? theme.colors.primary : theme.colors.border
2192
+ }
2193
+ ]
2194
+ },
2195
+ /* @__PURE__ */ React24.createElement(
2196
+ Text9,
2197
+ {
2198
+ style: [
2199
+ styles9.chainName,
2200
+ {
2201
+ color: hasError ? theme.colors.error : isSelected ? theme.colors.primary : theme.colors.text
2202
+ }
2203
+ ]
2204
+ },
2205
+ chain.id
2206
+ ),
2207
+ isSelected ? /* @__PURE__ */ React24.createElement(
2208
+ CloseIcon,
2209
+ {
2210
+ size: 12,
2211
+ color: hasError ? theme.colors.error : theme.colors.primary
2212
+ }
2213
+ ) : /* @__PURE__ */ React24.createElement(
2214
+ PlusIcon,
2215
+ {
2216
+ size: 12,
2217
+ color: theme.colors.textSecondary
2218
+ }
2219
+ )
2220
+ )
2221
+ );
2222
+ }))), Object.keys(chainErrors).length > 0 && /* @__PURE__ */ React24.createElement(View9, { style: styles9.chainErrorsContainer }, /* @__PURE__ */ React24.createElement(
2223
+ Text9,
2224
+ {
2225
+ style: [
2226
+ styles9.chainErrorsTitle,
2227
+ { color: theme.colors.error }
2228
+ ]
2229
+ },
2230
+ "Errors:"
2231
+ ), Object.entries(chainErrors).map(([chainId2, error]) => {
2232
+ const chain = userUsedChains.find(
2233
+ (c) => c.id === chainId2
2234
+ );
2235
+ return /* @__PURE__ */ React24.createElement(
2236
+ Text9,
2237
+ {
2238
+ key: chainId2,
2239
+ style: [
2240
+ styles9.chainErrorItem,
2241
+ { color: theme.colors.error }
2242
+ ]
2243
+ },
2244
+ "\u2022 ",
2245
+ chain?.name,
2246
+ ": ",
2247
+ error
2248
+ );
2249
+ }))), errorMessage ? /* @__PURE__ */ React24.createElement(Alert, { variant: "destructive" }, /* @__PURE__ */ React24.createElement(AlertDescription, null, errorMessage)) : null)), userUsedChains.length > 0 && address && /* @__PURE__ */ React24.createElement(ModalFooter, null, /* @__PURE__ */ React24.createElement(
2250
+ Button,
2251
+ {
2252
+ variant: "outline",
2253
+ size: "lg",
2254
+ onPress: onSubmitWalletConnect,
2255
+ loading: isLoading,
2256
+ disabled: isLoading || !!address && userUsedChains.length > 0 && selectedChains.size === 0,
2257
+ style: styles9.button
2258
+ },
2259
+ selectedChains.size > 0 ? `Add ${selectedChains.size} Chain${selectedChains.size > 1 ? "s" : ""}` : "Add Integration"
2260
+ )));
2261
+ }
2262
+ var styles9 = StyleSheet9.create({
2263
+ connectedTitle: { fontSize: 18, fontWeight: "600", marginBottom: 4 },
2264
+ connectedText: { fontSize: 14, marginBottom: 4 },
2265
+ infoText: {
2266
+ fontSize: 16,
2267
+ fontWeight: "600",
2268
+ marginBottom: 8,
2269
+ textAlign: "center"
2270
+ },
2271
+ scrollViewContent: {
2272
+ paddingBottom: 100,
2273
+ flexGrow: 1
2274
+ },
2275
+ headerContent: {
2276
+ flexDirection: "row",
2277
+ alignItems: "center"
2278
+ },
2279
+ backButton: {
2280
+ padding: 4,
2281
+ // theme.spacing.xs
2282
+ marginRight: 8
2283
+ // theme.spacing.sm
2284
+ },
2285
+ headerTitle: {
2286
+ fontSize: 16,
2287
+ // theme.fontSize.lg
2288
+ fontWeight: "600"
2289
+ },
2290
+ contentContainer: {
2291
+ padding: 20,
2292
+ // theme.spacing.xl
2293
+ paddingBottom: 40,
2294
+ width: "100%",
2295
+ overflow: "hidden",
2296
+ alignSelf: "center",
2297
+ flexDirection: "column",
2298
+ flex: 1
2299
+ },
2300
+ chainSelection: {
2301
+ marginBottom: 16
2302
+ // theme.spacing.lg
2303
+ },
2304
+ chainTitle: {
2305
+ fontSize: 14,
2306
+ // theme.fontSize.md
2307
+ fontWeight: "500",
2308
+ marginBottom: 12
2309
+ // theme.spacing.md - consistent label spacing
2310
+ },
2311
+ chainChips: {
2312
+ flexDirection: "row",
2313
+ flexWrap: "wrap",
2314
+ gap: 6
2315
+ // theme.spacing.sm
2316
+ },
2317
+ chainChip: {
2318
+ flexDirection: "row",
2319
+ alignItems: "center",
2320
+ paddingHorizontal: 8,
2321
+ // theme.spacing.sm
2322
+ paddingVertical: 5,
2323
+ // theme.spacing.xs
2324
+ borderRadius: 12,
2325
+ // theme.borderRadius.md
2326
+ borderWidth: 1
2327
+ },
2328
+ chainName: {
2329
+ fontSize: 12,
2330
+ fontWeight: "500",
2331
+ marginRight: 6
2332
+ // theme.spacing.xs
2333
+ },
2334
+ chainButton: {
2335
+ padding: 2
2336
+ // theme.spacing.xs
2337
+ },
2338
+ chainErrorsContainer: {
2339
+ marginTop: 12
2340
+ // theme.spacing.md - consistent spacing
2341
+ },
2342
+ chainErrorsTitle: {
2343
+ fontSize: 13,
2344
+ fontWeight: "500",
2345
+ marginBottom: 4
2346
+ // theme.spacing.xs
2347
+ },
2348
+ chainErrorItem: {
2349
+ fontSize: 12,
2350
+ // theme.fontSize.sm
2351
+ marginBottom: 4
2352
+ // theme.spacing.xs
2353
+ },
2354
+ button: {
2355
+ width: "100%",
2356
+ marginTop: 16
2357
+ // theme.spacing.lg - consistent button spacing
2358
+ },
2359
+ emptyState: {
2360
+ flex: 1,
2361
+ alignItems: "center",
2362
+ justifyContent: "center",
2363
+ gap: 12
2364
+ },
2365
+ emptyStateTitle: {
2366
+ fontSize: 18,
2367
+ fontWeight: "600"
2368
+ },
2369
+ emptyStateButton: {
2370
+ marginTop: 8
2371
+ }
2372
+ });
2373
+
2374
+ // src/molecules/IntegrationForm.tsx
2375
+ import React25 from "react";
2376
+ import {
2377
+ Image as Image2,
2378
+ ScrollView as ScrollView3,
2379
+ StyleSheet as StyleSheet10,
2380
+ Text as Text10,
2381
+ TouchableOpacity as TouchableOpacity4,
2382
+ View as View10
2383
+ } from "react-native";
2384
+ var IntegrationForm = ({
2385
+ metadata,
2386
+ onAddHandle,
2387
+ open,
2388
+ setAddIntegrationMode,
2389
+ handleClose
2390
+ }) => {
2391
+ const { clientId, linkToken, user } = useKryptosConnect();
2392
+ const theme = useTheme();
2393
+ const [isLoading, setIsLoading] = React25.useState(false);
2394
+ const [userUsedChains, setUserUsedChains] = React25.useState([]);
2395
+ const [selectedChains, setSelectedChains] = React25.useState(
2396
+ /* @__PURE__ */ new Set()
2397
+ );
2398
+ const [chainErrors, setChainErrors] = React25.useState(
2399
+ {}
2400
+ );
2401
+ const [errorMessage, setErrorMessage] = React25.useState("");
2402
+ const [formValues, setFormValues] = React25.useState({
2403
+ address: "",
2404
+ account_name: "",
2405
+ api_key: "",
2406
+ secret_key: "",
2407
+ password: ""
2408
+ });
2409
+ const [formErrors, setFormErrors] = React25.useState({});
2410
+ React25.useEffect(() => {
2411
+ const fetchUserUsedChains = async () => {
2412
+ if (linkToken && formValues.address && formValues.address.trim()) {
2413
+ try {
2414
+ const res = await getUserUsedChains(
2415
+ linkToken,
2416
+ formValues.address.trim()
2417
+ );
2418
+ if (res && Array.isArray(res)) {
2419
+ setUserUsedChains(res);
2420
+ setSelectedChains(new Set(res.map((chain) => chain.id)));
2421
+ }
2422
+ } catch (error) {
2423
+ console.error("Failed to fetch user chains:", error);
2424
+ setUserUsedChains([]);
2425
+ setSelectedChains(/* @__PURE__ */ new Set());
2426
+ }
2427
+ } else {
2428
+ setUserUsedChains([]);
2429
+ setSelectedChains(/* @__PURE__ */ new Set());
2430
+ }
2431
+ };
2432
+ fetchUserUsedChains();
2433
+ }, [linkToken, formValues.address]);
2434
+ const toggleChainSelection = (chainId) => {
2435
+ const newSelected = new Set(selectedChains);
2436
+ if (newSelected.has(chainId)) {
2437
+ newSelected.delete(chainId);
2438
+ } else {
2439
+ newSelected.add(chainId);
2440
+ }
2441
+ setSelectedChains(newSelected);
2442
+ if (chainErrors[chainId]) {
2443
+ const newErrors = { ...chainErrors };
2444
+ delete newErrors[chainId];
2445
+ setChainErrors(newErrors);
2446
+ }
2447
+ };
2448
+ const validateForm = () => {
2449
+ const errors = {};
2450
+ if (metadata.address && !formValues.address.trim()) {
2451
+ errors.address = "Address is required";
2452
+ }
2453
+ if (metadata.account_name && !formValues.account_name.trim()) {
2454
+ errors.account_name = "Account name is required";
2455
+ }
2456
+ if (metadata.api_key && !formValues.api_key.trim()) {
2457
+ errors.api_key = "API key is required";
2458
+ }
2459
+ if (metadata.secret_key && !formValues.secret_key.trim()) {
2460
+ errors.secret_key = "Secret key is required";
2461
+ }
2462
+ if (metadata.password && !formValues.password.trim()) {
2463
+ errors.password = "Password is required";
2464
+ }
2465
+ setFormErrors(errors);
2466
+ return Object.keys(errors).length === 0;
2467
+ };
2468
+ const handleSubmit = async () => {
2469
+ if (!validateForm()) return;
2470
+ try {
2471
+ setIsLoading(true);
2472
+ setChainErrors({});
2473
+ setErrorMessage("");
2474
+ const primaryField = formValues.address || formValues.account_name || formValues.api_key || "";
2475
+ const chainsToProcess = userUsedChains.length > 0 && formValues.address && selectedChains.size > 0 ? userUsedChains.filter((chain) => selectedChains.has(chain.id)) : [];
2476
+ const integrationsToAdd = [];
2477
+ const errors = {};
2478
+ if (chainsToProcess.length > 0) {
2479
+ const credentialTestsData = chainsToProcess.map((chain) => {
2480
+ const walletId = generateUUID();
2481
+ const displaySuffix = primaryField.length >= 8 ? `${primaryField.slice(0, 4)}...${primaryField.slice(-4)}` : primaryField;
2482
+ const alias = `${metadata.id} - ${chain.name} (${displaySuffix})`;
2483
+ const credential = {
2484
+ source: metadata.id,
2485
+ credential: {
2486
+ apiKey: formValues.api_key?.trim() || "0",
2487
+ secret: formValues.secret_key?.trim() || "0",
2488
+ accountName: formValues.account_name?.trim() || "0",
2489
+ address: formValues.address?.trim() || "0",
2490
+ password: formValues.password?.trim() || "0",
2491
+ userId: user?.user?.uid || "0",
2492
+ projectId: metadata?.projectId || "0",
2493
+ privateKey: "0",
2494
+ alias,
2495
+ walletId,
2496
+ exchange: metadata.id
2497
+ }
2498
+ };
2499
+ return { chain, walletId, alias, credential };
2500
+ });
2501
+ const results = await Promise.allSettled(
2502
+ credentialTestsData.map(
2503
+ (testData) => testCredentials(linkToken, { ...testData.credential })
2504
+ )
2505
+ );
2506
+ results.forEach((result, index) => {
2507
+ const { chain, walletId, alias } = credentialTestsData[index];
2508
+ if (result.status === "fulfilled" && result.value?.value?.status) {
2509
+ const data = {
2510
+ alias,
2511
+ exchange: metadata.id.toLowerCase(),
2512
+ id: metadata.id,
2513
+ public_name: metadata.public_name,
2514
+ sync_time: (/* @__PURE__ */ new Date()).getTime(),
2515
+ fetchAll: true,
2516
+ logo: metadata.logo || null,
2517
+ startTime: null,
2518
+ endTime: null,
2519
+ uid: user?.user?.uid || "",
2520
+ walletId,
2521
+ clientMetadata: {
2522
+ clientId,
2523
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2524
+ },
2525
+ metadata: {
2526
+ environment: "sandbox"
2527
+ },
2528
+ addedOn: (/* @__PURE__ */ new Date()).getTime(),
2529
+ default_chain: chain.name,
2530
+ default_chain_logo: chain.logo || null,
2531
+ type: metadata.type,
2532
+ isNftSupported: metadata.isEvmWallet || metadata.nftSupport || false,
2533
+ chainId: chain.chainId || chain.id,
2534
+ address: formValues.address
2535
+ };
2536
+ if (formValues.account_name)
2537
+ data.accountName = formValues.account_name;
2538
+ if (formValues.api_key) data.apiKey = formValues.api_key;
2539
+ if (formValues.secret_key) data.secret = formValues.secret_key;
2540
+ if (formValues.password) data.password = formValues.password;
2541
+ integrationsToAdd.push(data);
2542
+ } else {
2543
+ if (result.status === "rejected") {
2544
+ const reason = result.reason;
2545
+ errors[chain.id] = reason?.response?.data?.message || "Failed to process chain";
2546
+ } else if (result.status === "fulfilled") {
2547
+ errors[chain.id] = result.value?.value?.message || "Failed to verify chain";
2548
+ }
2549
+ }
2550
+ });
2551
+ setChainErrors(errors);
2552
+ if (Object.keys(errors).length > 0) {
2553
+ setErrorMessage(
2554
+ `Cannot add integrations. ${Object.keys(errors).length} chain${Object.keys(errors).length > 1 ? "s" : ""} failed verification.`
2555
+ );
2556
+ return;
2557
+ }
2558
+ } else {
2559
+ const displaySuffix = primaryField.length >= 8 ? `${primaryField.slice(0, 4)}...${primaryField.slice(-4)}` : primaryField;
2560
+ const alias = `${metadata.id} (${displaySuffix})`;
2561
+ const walletId = generateUUID();
2562
+ const credential = {
2563
+ source: metadata.id,
2564
+ credential: {
2565
+ apiKey: formValues.api_key?.trim() || "0",
2566
+ secret: formValues.secret_key?.trim() || "0",
2567
+ accountName: formValues.account_name?.trim() || "0",
2568
+ address: formValues.address?.trim() || "0",
2569
+ password: formValues.password?.trim() || "0",
2570
+ userId: user?.user?.uid || "0",
2571
+ projectId: metadata?.projectId || "0",
2572
+ privateKey: "0",
2573
+ alias,
2574
+ walletId,
2575
+ exchange: metadata.id
2576
+ }
2577
+ };
2578
+ const testResult = await testCredentials(linkToken, { ...credential });
2579
+ if (!testResult?.value?.status) {
2580
+ setErrorMessage(
2581
+ testResult?.value?.message || "Credentials are invalid"
2582
+ );
2583
+ return;
2584
+ }
2585
+ const data = {
2586
+ alias,
2587
+ exchange: metadata.id.toLowerCase(),
2588
+ id: metadata.id,
2589
+ public_name: metadata.public_name,
2590
+ sync_time: (/* @__PURE__ */ new Date()).getTime(),
2591
+ fetchAll: true,
2592
+ logo: metadata.logo || null,
2593
+ startTime: null,
2594
+ endTime: null,
2595
+ uid: user?.user?.uid || "",
2596
+ walletId,
2597
+ clientMetadata: {
2598
+ clientId,
2599
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2600
+ },
2601
+ metadata: {
2602
+ environment: "sandbox"
2603
+ },
2604
+ addedOn: (/* @__PURE__ */ new Date()).getTime(),
2605
+ default_chain: metadata.id,
2606
+ default_chain_logo: null,
2607
+ type: metadata.type,
2608
+ isNftSupported: metadata.isEvmWallet || metadata.nftSupport || false
2609
+ };
2610
+ if (metadata.community_id) {
2611
+ data.chainId = Number(metadata.community_id);
2612
+ }
2613
+ if (formValues.address) data.address = formValues.address;
2614
+ if (formValues.account_name) data.accountName = formValues.account_name;
2615
+ if (formValues.api_key) data.apiKey = formValues.api_key;
2616
+ if (formValues.secret_key) data.secret = formValues.secret_key;
2617
+ if (formValues.password) data.password = formValues.password;
2618
+ integrationsToAdd.push(data);
2619
+ }
2620
+ if (integrationsToAdd.length > 0) {
2621
+ onAddHandle(integrationsToAdd);
2622
+ setFormErrors({});
2623
+ setFormValues({
2624
+ address: "",
2625
+ account_name: "",
2626
+ api_key: "",
2627
+ secret_key: "",
2628
+ password: ""
2629
+ });
2630
+ setErrorMessage("");
2631
+ } else {
2632
+ setErrorMessage("No integrations could be added. Please try again.");
2633
+ }
2634
+ } catch (error) {
2635
+ const err = error;
2636
+ console.error(error);
2637
+ setErrorMessage(
2638
+ err?.response?.data?.message || "Failed to add integration"
2639
+ );
2640
+ } finally {
2641
+ setIsLoading(false);
2642
+ }
2643
+ };
2644
+ const hasNoFields = !metadata.password && !metadata.secret_key && !metadata.api_key && !metadata.address && !metadata.account_name;
2645
+ const shouldShowFormFields = metadata.password || metadata.secret_key || metadata.api_key || metadata.address || metadata.account_name;
2646
+ const renderLogo = () => metadata.logo ? /* @__PURE__ */ React25.createElement(
2647
+ Image2,
2648
+ {
2649
+ source: { uri: metadata.logo },
2650
+ style: styles10.logo,
2651
+ resizeMode: "contain"
2652
+ }
2653
+ ) : /* @__PURE__ */ React25.createElement(
2654
+ View10,
2655
+ {
2656
+ style: [
2657
+ styles10.logoPlaceholder,
2658
+ { backgroundColor: theme.colors.surface }
2659
+ ]
2660
+ },
2661
+ /* @__PURE__ */ React25.createElement(Text10, { style: { color: theme.colors.text } }, metadata.name?.charAt(0) || "?")
2662
+ );
2663
+ const renderInput = (key, props) => /* @__PURE__ */ React25.createElement(
2664
+ Input,
2665
+ {
2666
+ placeholder: props.placeholder,
2667
+ value: formValues[key],
2668
+ onChangeText: (text) => setFormValues((prev) => ({ ...prev, [key]: text })),
2669
+ error: formErrors[key],
2670
+ autoCapitalize: props.autoCapitalize,
2671
+ autoCorrect: props.autoCorrect,
2672
+ secureTextEntry: props.secureTextEntry
2673
+ }
2674
+ );
2675
+ const renderErrorAlert = () => errorMessage ? /* @__PURE__ */ React25.createElement(Alert, { variant: "destructive" }, /* @__PURE__ */ React25.createElement(AlertDescription, null, errorMessage)) : null;
2676
+ const renderChainSelection = () => userUsedChains.length > 0 && formValues.address && /* @__PURE__ */ React25.createElement(View10, { style: styles10.chainSelection }, /* @__PURE__ */ React25.createElement(Text10, { style: [styles10.chainTitle, { color: theme.colors.text }] }, "Select Chains to Add:"), /* @__PURE__ */ React25.createElement(ScrollView3, { contentContainerStyle: styles10.scrollViewContent }, /* @__PURE__ */ React25.createElement(View10, { style: styles10.chainChips }, userUsedChains.map((chain) => {
2677
+ const isSelected = selectedChains.has(chain.id);
2678
+ const hasError = chainErrors[chain.id];
2679
+ return /* @__PURE__ */ React25.createElement(
2680
+ TouchableOpacity4,
2681
+ {
2682
+ onPress: () => toggleChainSelection(chain.id),
2683
+ style: styles10.chainButton,
2684
+ key: chain.id
2685
+ },
2686
+ /* @__PURE__ */ React25.createElement(
2687
+ View10,
2688
+ {
2689
+ style: [
2690
+ styles10.chainChip,
2691
+ {
2692
+ backgroundColor: hasError ? theme.colors.errorLight : isSelected ? theme.colors.primary + "20" : theme.colors.surface,
2693
+ borderColor: hasError ? theme.colors.error : isSelected ? theme.colors.primary : theme.colors.border
2694
+ }
2695
+ ]
2696
+ },
2697
+ /* @__PURE__ */ React25.createElement(
2698
+ Text10,
2699
+ {
2700
+ style: [
2701
+ styles10.chainName,
2702
+ {
2703
+ color: hasError ? theme.colors.error : isSelected ? theme.colors.primary : theme.colors.text
2704
+ }
2705
+ ]
2706
+ },
2707
+ chain.name
2708
+ ),
2709
+ isSelected ? /* @__PURE__ */ React25.createElement(
2710
+ CloseIcon,
2711
+ {
2712
+ size: 12,
2713
+ color: hasError ? theme.colors.error : theme.colors.primary
2714
+ }
2715
+ ) : /* @__PURE__ */ React25.createElement(PlusIcon, { size: 12, color: theme.colors.textSecondary })
2716
+ )
2717
+ );
2718
+ }))), Object.keys(chainErrors).length > 0 && /* @__PURE__ */ React25.createElement(View10, { style: styles10.chainErrorsContainer }, /* @__PURE__ */ React25.createElement(
2719
+ Text10,
2720
+ {
2721
+ style: [styles10.chainErrorsTitle, { color: theme.colors.error }]
2722
+ },
2723
+ "Errors:"
2724
+ ), Object.entries(chainErrors).map(([chainId, error]) => {
2725
+ const chain = userUsedChains.find((c) => c.id === chainId);
2726
+ return /* @__PURE__ */ React25.createElement(
2727
+ Text10,
2728
+ {
2729
+ key: chainId,
2730
+ style: [styles10.chainErrorItem, { color: theme.colors.error }]
2731
+ },
2732
+ "\u2022 ",
2733
+ chain?.name,
2734
+ ": ",
2735
+ error
2736
+ );
2737
+ })));
2738
+ const renderFormBlock = () => /* @__PURE__ */ React25.createElement(React25.Fragment, null, /* @__PURE__ */ React25.createElement(View10, { style: styles10.header }, renderLogo(), /* @__PURE__ */ React25.createElement(Text10, { style: [styles10.name, { color: theme.colors.text }] }, metadata.name)), renderErrorAlert(), shouldShowFormFields && /* @__PURE__ */ React25.createElement(React25.Fragment, null, metadata.address && /* @__PURE__ */ React25.createElement(React25.Fragment, null, renderInput("address", {
2739
+ placeholder: "Enter address",
2740
+ autoCapitalize: "none",
2741
+ autoCorrect: false
2742
+ }), renderChainSelection()), metadata.account_name && renderInput("account_name", { placeholder: "Enter account name" }), metadata.api_key && renderInput("api_key", {
2743
+ placeholder: "Enter API key",
2744
+ autoCapitalize: "none",
2745
+ autoCorrect: false
2746
+ }), metadata.secret_key && renderInput("secret_key", {
2747
+ placeholder: "Enter API secret",
2748
+ autoCapitalize: "none",
2749
+ autoCorrect: false,
2750
+ secureTextEntry: true
2751
+ }), metadata.password && renderInput("password", {
2752
+ placeholder: "Enter Password",
2753
+ secureTextEntry: true
2754
+ })), hasNoFields && !metadata?.isWalletConnectSupported && /* @__PURE__ */ React25.createElement(Alert, { variant: "default", style: { marginTop: 12 } }, /* @__PURE__ */ React25.createElement(AlertDescription, null, "This integration is not supported here yet \u2014 try using it through our Kryptos Platform.")));
2755
+ const addIntegrationLabel = formValues.address && userUsedChains.length > 0 && selectedChains.size > 0 ? `Add ${selectedChains.size} Chain${selectedChains.size !== 1 ? "s" : ""}` : "Add Integration";
2756
+ return /* @__PURE__ */ React25.createElement(React25.Fragment, null, !metadata?.isWalletConnectSupported ? /* @__PURE__ */ React25.createElement(Modal, { isOpen: open, onClose: handleClose, size: "full" }, /* @__PURE__ */ React25.createElement(ModalHeader, { onClose: handleClose }, /* @__PURE__ */ React25.createElement(View10, { style: styles10.headerContent }, /* @__PURE__ */ React25.createElement(
2757
+ TouchableOpacity4,
2758
+ {
2759
+ onPress: () => setAddIntegrationMode(null),
2760
+ style: styles10.backButton
2761
+ },
2762
+ /* @__PURE__ */ React25.createElement(ArrowLeftIcon, { size: 20, color: theme.colors.text })
2763
+ ), /* @__PURE__ */ React25.createElement(Text10, { style: [styles10.headerTitle, { color: theme.colors.text }] }, "Integration"))), /* @__PURE__ */ React25.createElement(ModalBody, { scrollable: false, style: styles10.contentContainer }, renderFormBlock()), !hasNoFields && /* @__PURE__ */ React25.createElement(ModalFooter, null, /* @__PURE__ */ React25.createElement(
2764
+ Button,
2765
+ {
2766
+ variant: "outline",
2767
+ size: "lg",
2768
+ onPress: handleSubmit,
2769
+ loading: isLoading,
2770
+ disabled: isLoading || !!formValues.address && userUsedChains.length > 0 && selectedChains.size === 0,
2771
+ style: styles10.button
2772
+ },
2773
+ addIntegrationLabel
2774
+ ))) : /* @__PURE__ */ React25.createElement(
2775
+ WalletConnectComponent,
2776
+ {
2777
+ integration: metadata,
2778
+ onClose: handleClose,
2779
+ onAddHandle,
2780
+ modalOpen: open,
2781
+ setAddIntegrationMode,
2782
+ handleClose
2783
+ }
2784
+ ));
2785
+ };
2786
+ var styles10 = StyleSheet10.create({
2787
+ scrollViewContent: {
2788
+ flexGrow: 1,
2789
+ paddingBottom: 100
2790
+ },
2791
+ headerContent: {
2792
+ flexDirection: "row",
2793
+ alignItems: "center"
2794
+ },
2795
+ backButton: {
2796
+ padding: 4,
2797
+ // theme.spacing.xs
2798
+ marginRight: 8
2799
+ // theme.spacing.sm
2800
+ },
2801
+ headerTitle: {
2802
+ fontSize: 16,
2803
+ // theme.fontSize.lg
2804
+ fontWeight: "600"
2805
+ },
2806
+ container: {
2807
+ flex: 1
2808
+ },
2809
+ contentContainer: {
2810
+ padding: 20,
2811
+ // theme.spacing.xl
2812
+ paddingBottom: 40,
2813
+ width: "100%",
2814
+ overflow: "hidden",
2815
+ alignSelf: "center",
2816
+ flexDirection: "column",
2817
+ flex: 1
2818
+ },
2819
+ header: {
2820
+ flexDirection: "row",
2821
+ justifyContent: "center",
2822
+ alignItems: "center",
2823
+ marginBottom: 16
2824
+ // theme.spacing.lg
2825
+ },
2826
+ logo: {
2827
+ width: 32,
2828
+ height: 32,
2829
+ borderRadius: 8
2830
+ // theme.borderRadius.sm
2831
+ },
2832
+ logoPlaceholder: {
2833
+ width: 32,
2834
+ height: 32,
2835
+ borderRadius: 8,
2836
+ // theme.borderRadius.sm
2837
+ alignItems: "center",
2838
+ justifyContent: "center"
2839
+ },
2840
+ name: {
2841
+ fontSize: 16,
2842
+ // theme.fontSize.lg
2843
+ fontWeight: "600",
2844
+ marginLeft: 12,
2845
+ // theme.spacing.md
2846
+ textTransform: "capitalize"
2847
+ },
2848
+ chainSelection: {
2849
+ marginBottom: 16
2850
+ // theme.spacing.lg
2851
+ },
2852
+ chainTitle: {
2853
+ fontSize: 14,
2854
+ // theme.fontSize.md
2855
+ fontWeight: "500",
2856
+ marginBottom: 12
2857
+ // theme.spacing.md - consistent label spacing
2858
+ },
2859
+ chainChips: {
2860
+ flexDirection: "row",
2861
+ flexWrap: "wrap",
2862
+ gap: 6
2863
+ // theme.spacing.sm
2864
+ },
2865
+ chainChip: {
2866
+ flexDirection: "row",
2867
+ alignItems: "center",
2868
+ paddingHorizontal: 8,
2869
+ // theme.spacing.sm
2870
+ paddingVertical: 5,
2871
+ // theme.spacing.xs
2872
+ borderRadius: 12,
2873
+ // theme.borderRadius.md
2874
+ borderWidth: 1
2875
+ },
2876
+ chainName: {
2877
+ fontSize: 12,
2878
+ fontWeight: "500",
2879
+ marginRight: 6
2880
+ // theme.spacing.xs
2881
+ },
2882
+ chainButton: {
2883
+ padding: 2
2884
+ // theme.spacing.xs
2885
+ },
2886
+ chainErrorsContainer: {
2887
+ marginTop: 12
2888
+ // theme.spacing.md - consistent spacing
2889
+ },
2890
+ chainErrorsTitle: {
2891
+ fontSize: 13,
2892
+ fontWeight: "500",
2893
+ marginBottom: 4
2894
+ // theme.spacing.xs
2895
+ },
2896
+ chainErrorItem: {
2897
+ fontSize: 12,
2898
+ // theme.fontSize.sm
2899
+ marginBottom: 4
2900
+ // theme.spacing.xs
2901
+ },
2902
+ button: {
2903
+ width: "100%",
2904
+ marginTop: 16
2905
+ // theme.spacing.lg - consistent button spacing
2906
+ }
2907
+ });
2908
+
2909
+ // src/components/SkeletonItem.tsx
2910
+ import React26, { useEffect, useRef } from "react";
2911
+ import { Animated as Animated2, View as View11, StyleSheet as StyleSheet11 } from "react-native";
2912
+ var SkeletonItem = () => {
2913
+ const opacity = useRef(new Animated2.Value(0.3)).current;
2914
+ useEffect(() => {
2915
+ Animated2.loop(
2916
+ Animated2.sequence([
2917
+ Animated2.timing(opacity, {
2918
+ toValue: 1,
2919
+ duration: 600,
2920
+ useNativeDriver: true
2921
+ }),
2922
+ Animated2.timing(opacity, {
2923
+ toValue: 0.3,
2924
+ duration: 600,
2925
+ useNativeDriver: true
2926
+ })
2927
+ ])
2928
+ ).start();
2929
+ }, []);
2930
+ return /* @__PURE__ */ React26.createElement(Animated2.View, { style: [styles11.row, { opacity }] }, /* @__PURE__ */ React26.createElement(View11, { style: styles11.iconCircle }), /* @__PURE__ */ React26.createElement(View11, { style: styles11.textBlock }, /* @__PURE__ */ React26.createElement(View11, { style: styles11.lineLong }), /* @__PURE__ */ React26.createElement(View11, { style: styles11.lineShort })));
2931
+ };
2932
+ var styles11 = StyleSheet11.create({
2933
+ row: {
2934
+ flexDirection: "row",
2935
+ alignItems: "center",
2936
+ paddingVertical: 16
2937
+ },
2938
+ iconCircle: {
2939
+ width: 45,
2940
+ height: 45,
2941
+ borderRadius: 22.5,
2942
+ backgroundColor: "#E5E5E5"
2943
+ },
2944
+ textBlock: {
2945
+ marginLeft: 12,
2946
+ flex: 1
2947
+ },
2948
+ lineShort: {
2949
+ width: "50%",
2950
+ height: 14,
2951
+ borderRadius: 6,
2952
+ backgroundColor: "#E5E5E5"
2953
+ },
2954
+ lineLong: {
2955
+ marginBottom: 6,
2956
+ width: "100%",
2957
+ height: 14,
2958
+ borderRadius: 6,
2959
+ backgroundColor: "#E5E5E5"
2960
+ }
2961
+ });
2962
+ var SkeletonItem_default = SkeletonItem;
2963
+
2964
+ // src/molecules/Integration.tsx
2965
+ var Integration = ({
2966
+ open,
2967
+ onSuccess,
2968
+ onClose
2969
+ }) => {
2970
+ const { appName, linkToken } = useKryptosConnect();
2971
+ const theme = useTheme();
2972
+ const [addIntegrationMode, setAddIntegrationMode] = React27.useState(null);
2973
+ const [query, setQuery] = React27.useState("");
2974
+ const [supportedProviders, setSupportedProviders] = React27.useState([]);
2975
+ const [addedIntegrations, setAddedIntegrations] = React27.useState([]);
2976
+ const [existingIntegrations, setExistingIntegrations] = React27.useState([]);
2977
+ const [isLoading, setIsLoading] = React27.useState(false);
2978
+ const [errorMessage, setErrorMessage] = React27.useState("");
2979
+ const handleClose = () => {
2980
+ onClose();
2981
+ };
2982
+ const fetchExistingIntegrations = async () => {
2983
+ try {
2984
+ const res = await getUserIntegrations(linkToken);
2985
+ setExistingIntegrations(res?.integrations || []);
2986
+ } catch (error) {
2987
+ const err = error;
2988
+ setErrorMessage(
2989
+ err?.response?.data?.message || "Failed to fetch existing integrations"
2990
+ );
2991
+ console.error(error);
2992
+ }
2993
+ };
2994
+ const fetchSupportedProviders = async () => {
2995
+ try {
2996
+ setIsLoading(true);
2997
+ const res = await getSupportedProviders(linkToken);
2998
+ setSupportedProviders(res?.providers || []);
2999
+ } catch (error) {
3000
+ const err = error;
3001
+ setErrorMessage(
3002
+ err?.response?.data?.message || "Failed to fetch supported providers"
3003
+ );
3004
+ console.error(error);
3005
+ } finally {
3006
+ setIsLoading(false);
3007
+ }
3008
+ };
3009
+ React27.useEffect(() => {
3010
+ if (linkToken) {
3011
+ fetchSupportedProviders();
3012
+ fetchExistingIntegrations();
3013
+ }
3014
+ }, [linkToken]);
3015
+ const isIntegrationAdded = React27.useCallback(
3016
+ (publicName) => {
3017
+ const integrations = [...addedIntegrations, ...existingIntegrations];
3018
+ return integrations.some(
3019
+ (integration) => integration.public_name === publicName
3020
+ );
3021
+ },
3022
+ [addedIntegrations, existingIntegrations]
3023
+ );
3024
+ const getIntegrationCount = React27.useCallback(
3025
+ (publicName) => {
3026
+ const integrations = [...addedIntegrations, ...existingIntegrations];
3027
+ return integrations.filter(
3028
+ (integration) => integration.public_name === publicName
3029
+ ).length;
3030
+ },
3031
+ [addedIntegrations, existingIntegrations]
3032
+ );
3033
+ const filteredResults = React27.useMemo(() => {
3034
+ if (query) {
3035
+ const lowerQuery = query.toLowerCase();
3036
+ return supportedProviders.filter(
3037
+ (provider) => provider.name?.toLowerCase().includes(lowerQuery) || provider.public_name?.toLowerCase().includes(lowerQuery) || provider.id?.toLowerCase().includes(lowerQuery)
3038
+ );
3039
+ }
3040
+ return [...supportedProviders].sort((a, b) => {
3041
+ const countA = getIntegrationCount(a.public_name);
3042
+ const countB = getIntegrationCount(b.public_name);
3043
+ if (countB !== countA) {
3044
+ return countB - countA;
3045
+ }
3046
+ return a.name.localeCompare(b.name);
3047
+ });
3048
+ }, [query, supportedProviders, getIntegrationCount]);
3049
+ const handleAddIntegration = async () => {
3050
+ try {
3051
+ setIsLoading(true);
3052
+ const integrations = [...addedIntegrations, ...existingIntegrations];
3053
+ if (integrations.length === 0) {
3054
+ setErrorMessage("Please add at least one integration");
3055
+ } else if (addedIntegrations.length === 0) {
3056
+ onSuccess();
3057
+ } else {
3058
+ await addUserIntegration(linkToken, addedIntegrations);
3059
+ onSuccess();
3060
+ }
3061
+ } catch (error) {
3062
+ const err = error;
3063
+ console.error(error);
3064
+ setErrorMessage(
3065
+ err?.response?.data?.message || "Failed to add integrations"
3066
+ );
3067
+ } finally {
3068
+ setIsLoading(false);
3069
+ }
3070
+ };
3071
+ const renderProviderItem = ({ item }) => /* @__PURE__ */ React27.createElement(
3072
+ TouchableOpacity5,
3073
+ {
3074
+ style: [
3075
+ styles12.providerItem,
3076
+ {
3077
+ backgroundColor: theme.colors.surface,
3078
+ borderColor: theme.colors.border
3079
+ }
3080
+ ],
3081
+ onPress: () => setAddIntegrationMode(item),
3082
+ activeOpacity: 0.7
3083
+ },
3084
+ /* @__PURE__ */ React27.createElement(View12, { style: styles12.providerInfo }, item?.logo ? /* @__PURE__ */ React27.createElement(
3085
+ Image3,
3086
+ {
3087
+ source: { uri: item?.logo },
3088
+ style: styles12.providerLogo,
3089
+ resizeMode: "contain"
3090
+ }
3091
+ ) : /* @__PURE__ */ React27.createElement(
3092
+ View12,
3093
+ {
3094
+ style: [
3095
+ styles12.providerLogoPlaceholder,
3096
+ { backgroundColor: theme.colors.surfaceSecondary }
3097
+ ]
3098
+ },
3099
+ /* @__PURE__ */ React27.createElement(Text11, { style: { color: theme.colors.text } }, item?.name?.charAt(0) || "?")
3100
+ ), /* @__PURE__ */ React27.createElement(Text11, { style: [styles12.providerName, { color: theme.colors.text }] }, item?.name + "\u200B")),
3101
+ isIntegrationAdded(item?.public_name) && /* @__PURE__ */ React27.createElement(View12, { style: styles12.providerStatus }, /* @__PURE__ */ React27.createElement(CheckCircleIcon, { size: 18, color: theme.colors.success }), /* @__PURE__ */ React27.createElement(
3102
+ Text11,
3103
+ {
3104
+ style: [
3105
+ styles12.providerCount,
3106
+ { color: theme.colors.textSecondary }
3107
+ ]
3108
+ },
3109
+ getIntegrationCount(item?.public_name)
3110
+ ))
3111
+ );
3112
+ const renderSkeletonItem = () => /* @__PURE__ */ React27.createElement(SkeletonItem_default, null);
3113
+ return /* @__PURE__ */ React27.createElement(React27.Fragment, null, !addIntegrationMode ? /* @__PURE__ */ React27.createElement(Modal, { isOpen: open, onClose: handleClose, size: "full" }, /* @__PURE__ */ React27.createElement(ModalHeader, { onClose: handleClose }, /* @__PURE__ */ React27.createElement(View12, { style: styles12.headerContent }, addIntegrationMode && /* @__PURE__ */ React27.createElement(
3114
+ TouchableOpacity5,
3115
+ {
3116
+ onPress: () => setAddIntegrationMode(null),
3117
+ style: styles12.backButton
3118
+ },
3119
+ /* @__PURE__ */ React27.createElement(ArrowLeftIcon, { size: 20, color: theme.colors.text })
3120
+ ), /* @__PURE__ */ React27.createElement(Text11, { style: [styles12.headerTitle, { color: theme.colors.text }] }, "Integration"))), /* @__PURE__ */ React27.createElement(ModalBody, { scrollable: false, style: styles12.noPadding }, /* @__PURE__ */ React27.createElement(View12, { style: styles12.container }, /* @__PURE__ */ React27.createElement(View12, { style: styles12.headerSection }, /* @__PURE__ */ React27.createElement(ConnectLogo, null), /* @__PURE__ */ React27.createElement(Text11, { style: [styles12.title, { color: theme.colors.text }] }, "Connect ", appName, " to your Kryptos account")), /* @__PURE__ */ React27.createElement(
3121
+ FlatList,
3122
+ {
3123
+ data: isLoading ? Array.from({ length: 8 }, (_, i) => ({
3124
+ id: `skeleton-${i}`,
3125
+ name: "",
3126
+ public_name: "",
3127
+ type: ""
3128
+ })) : filteredResults,
3129
+ keyExtractor: (item, index) => isLoading ? item.id : `provider-${item.id}-${index}`,
3130
+ renderItem: isLoading ? renderSkeletonItem : renderProviderItem,
3131
+ style: styles12.list,
3132
+ contentContainerStyle: [
3133
+ styles12.listContent,
3134
+ { paddingHorizontal: theme.spacing.xl }
3135
+ ],
3136
+ showsVerticalScrollIndicator: false,
3137
+ ListHeaderComponent: /* @__PURE__ */ React27.createElement(
3138
+ View12,
3139
+ {
3140
+ style: {
3141
+ paddingVertical: theme.spacing.sm + 2,
3142
+ backgroundColor: theme.colors.background,
3143
+ zIndex: 10
3144
+ }
3145
+ },
3146
+ /* @__PURE__ */ React27.createElement(
3147
+ Input,
3148
+ {
3149
+ value: query,
3150
+ onChangeText: setQuery,
3151
+ placeholder: "Search Integrations...",
3152
+ containerStyle: styles12.searchInput
3153
+ }
3154
+ )
3155
+ ),
3156
+ stickyHeaderIndices: [0],
3157
+ ListEmptyComponent: /* @__PURE__ */ React27.createElement(View12, { style: styles12.emptyContainer }, !isLoading && /* @__PURE__ */ React27.createElement(
3158
+ Text11,
3159
+ {
3160
+ style: [
3161
+ styles12.emptyText,
3162
+ { color: theme.colors.textSecondary }
3163
+ ]
3164
+ },
3165
+ query ? "No search results found" : "No supported integrations found"
3166
+ ))
3167
+ }
3168
+ ), errorMessage && /* @__PURE__ */ React27.createElement(View12, { style: styles12.errorContainer }, /* @__PURE__ */ React27.createElement(Alert, { variant: "destructive", style: styles12.errorAlert }, /* @__PURE__ */ React27.createElement(AlertDescription, null, errorMessage))))), /* @__PURE__ */ React27.createElement(ModalFooter, null, /* @__PURE__ */ React27.createElement(
3169
+ Button,
3170
+ {
3171
+ variant: "outline",
3172
+ size: "lg",
3173
+ onPress: handleAddIntegration,
3174
+ loading: isLoading,
3175
+ disabled: isLoading,
3176
+ style: styles12.continueButton
3177
+ },
3178
+ "Continue"
3179
+ ))) : /* @__PURE__ */ React27.createElement(
3180
+ IntegrationForm,
3181
+ {
3182
+ metadata: addIntegrationMode,
3183
+ onAddHandle: (integrations) => {
3184
+ setAddedIntegrations((prev) => [...prev, ...integrations]);
3185
+ setAddIntegrationMode(null);
3186
+ },
3187
+ open: !!addIntegrationMode,
3188
+ setAddIntegrationMode,
3189
+ handleClose
3190
+ }
3191
+ ));
3192
+ };
3193
+ var styles12 = StyleSheet12.create({
3194
+ headerContent: {
3195
+ flexDirection: "row",
3196
+ alignItems: "center"
3197
+ },
3198
+ backButton: {
3199
+ padding: 4,
3200
+ // theme.spacing.xs
3201
+ marginRight: 8
3202
+ // theme.spacing.sm
3203
+ },
3204
+ headerTitle: {
3205
+ fontSize: 16,
3206
+ // theme.fontSize.lg
3207
+ fontWeight: "600"
3208
+ },
3209
+ noPadding: {
3210
+ padding: 0
3211
+ },
3212
+ container: {
3213
+ flex: 1
3214
+ },
3215
+ headerSection: {
3216
+ paddingHorizontal: 20,
3217
+ // theme.spacing.xl
3218
+ paddingTop: 10
3219
+ // theme.spacing.xl
3220
+ },
3221
+ title: {
3222
+ fontSize: 16,
3223
+ // theme.fontSize.lg
3224
+ fontWeight: "600",
3225
+ textAlign: "center",
3226
+ marginBottom: 10
3227
+ // theme.spacing.lg
3228
+ },
3229
+ searchInput: {},
3230
+ list: {
3231
+ flex: 1,
3232
+ minHeight: 200
3233
+ },
3234
+ listContent: {
3235
+ paddingBottom: 12
3236
+ // theme.spacing.md - consistent padding
3237
+ },
3238
+ providerItem: {
3239
+ flexDirection: "row",
3240
+ alignItems: "center",
3241
+ justifyContent: "space-between",
3242
+ padding: 12,
3243
+ // theme.spacing.md
3244
+ borderRadius: 12,
3245
+ // theme.borderRadius.md
3246
+ borderWidth: 1,
3247
+ marginBottom: 12
3248
+ // theme.spacing.md - consistent list item spacing
3249
+ },
3250
+ providerInfo: {
3251
+ flexDirection: "row",
3252
+ alignItems: "center",
3253
+ flex: 1
3254
+ },
3255
+ providerLogo: {
3256
+ width: 32,
3257
+ height: 32,
3258
+ borderRadius: 8
3259
+ // theme.borderRadius.sm
3260
+ },
3261
+ providerLogoPlaceholder: {
3262
+ width: 32,
3263
+ height: 32,
3264
+ borderRadius: 8,
3265
+ // theme.borderRadius.sm
3266
+ alignItems: "center",
3267
+ justifyContent: "center"
3268
+ },
3269
+ providerName: {
3270
+ fontSize: 14,
3271
+ // theme.fontSize.md
3272
+ fontWeight: "500",
3273
+ marginLeft: 12,
3274
+ // theme.spacing.md
3275
+ textTransform: "capitalize"
3276
+ },
3277
+ providerStatus: {
3278
+ flexDirection: "row",
3279
+ alignItems: "center"
3280
+ },
3281
+ providerCount: {
3282
+ fontSize: 14,
3283
+ // theme.fontSize.md
3284
+ marginLeft: 4
3285
+ // theme.spacing.xs
3286
+ },
3287
+ emptyContainer: {
3288
+ flex: 1,
3289
+ alignItems: "center",
3290
+ justifyContent: "center",
3291
+ paddingVertical: 40
3292
+ // theme.spacing.xxxl + sm
3293
+ },
3294
+ emptyText: {
3295
+ fontSize: 14
3296
+ // theme.fontSize.md
3297
+ },
3298
+ continueButton: {
3299
+ width: "100%"
3300
+ },
3301
+ errorContainer: {
3302
+ paddingHorizontal: 20
3303
+ // theme.spacing.xl
3304
+ },
3305
+ errorAlert: {
3306
+ marginTop: 16
3307
+ // theme.spacing.lg - consistent alert spacing
3308
+ }
3309
+ });
3310
+
3311
+ // src/molecules/OTPVerify.tsx
3312
+ import React28 from "react";
3313
+ import { StyleSheet as StyleSheet13, Text as Text12, TouchableOpacity as TouchableOpacity6, View as View13 } from "react-native";
3314
+ var OTPVerify = ({
3315
+ open,
3316
+ onSuccess,
3317
+ onClose
3318
+ }) => {
3319
+ const theme = useTheme();
3320
+ const [otp, setOtp] = React28.useState("");
3321
+ const { linkToken, clientId, email, setUser } = useKryptosConnect();
3322
+ const [isLoading, setIsLoading] = React28.useState(false);
3323
+ const [isResending, setIsResending] = React28.useState(false);
3324
+ const [resendCooldown, setResendCooldown] = React28.useState(0);
3325
+ const [errorMessage, setErrorMessage] = React28.useState("");
3326
+ const [successMessage, setSuccessMessage] = React28.useState("");
3327
+ const handleSubmit = async () => {
3328
+ if (otp.length !== 6) return;
3329
+ setIsLoading(true);
3330
+ setErrorMessage("");
3331
+ try {
3332
+ const res = await loginWithOtp(linkToken, email, otp, clientId);
3333
+ setUser(res);
3334
+ onSuccess();
3335
+ } catch (error) {
3336
+ const err = error;
3337
+ setErrorMessage(
3338
+ err?.response?.data?.message || "Failed to login with OTP"
3339
+ );
3340
+ console.error(error);
3341
+ } finally {
3342
+ setIsLoading(false);
3343
+ }
3344
+ };
3345
+ const handleOtpComplete = (value) => {
3346
+ setOtp(value);
3347
+ };
3348
+ const handleResendOtp = async () => {
3349
+ if (resendCooldown > 0 || isResending) return;
3350
+ try {
3351
+ setIsResending(true);
3352
+ setErrorMessage("");
3353
+ setSuccessMessage("");
3354
+ await sendEmailOtp(linkToken, email, clientId);
3355
+ setSuccessMessage("OTP sent successfully!");
3356
+ setResendCooldown(60);
3357
+ setOtp("");
3358
+ setTimeout(() => {
3359
+ setSuccessMessage("");
3360
+ }, 3e3);
3361
+ } catch (error) {
3362
+ const err = error;
3363
+ setErrorMessage(
3364
+ err?.response?.data?.message || "Failed to resend OTP. Please try again."
3365
+ );
3366
+ console.error(error);
3367
+ } finally {
3368
+ setIsResending(false);
3369
+ }
3370
+ };
3371
+ const handleClose = () => {
3372
+ onClose();
3373
+ setErrorMessage("");
3374
+ setSuccessMessage("");
3375
+ setOtp("");
3376
+ };
3377
+ React28.useEffect(() => {
3378
+ if (resendCooldown > 0) {
3379
+ const timer = setTimeout(() => {
3380
+ setResendCooldown(resendCooldown - 1);
3381
+ }, 1e3);
3382
+ return () => clearTimeout(timer);
3383
+ }
3384
+ return void 0;
3385
+ }, [resendCooldown]);
3386
+ return /* @__PURE__ */ React28.createElement(Modal, { isOpen: open, onClose: handleClose, size: "lg" }, /* @__PURE__ */ React28.createElement(ModalHeader, { onClose: handleClose }, /* @__PURE__ */ React28.createElement(View13, { style: styles13.headerContent }, /* @__PURE__ */ React28.createElement(Text12, { style: [styles13.headerTitle, { color: theme.colors.text }] }, "OTP Verification"))), /* @__PURE__ */ React28.createElement(ModalBody, null, /* @__PURE__ */ React28.createElement(View13, { style: styles13.container }, /* @__PURE__ */ React28.createElement(ConnectLogo, null), /* @__PURE__ */ React28.createElement(View13, { style: styles13.emailInfo }, /* @__PURE__ */ React28.createElement(
3387
+ Text12,
3388
+ {
3389
+ style: [styles13.infoText, { color: theme.colors.textSecondary }]
3390
+ },
3391
+ "We have sent a verification code to"
3392
+ ), /* @__PURE__ */ React28.createElement(Text12, { style: [styles13.emailText, { color: theme.colors.text }] }, email)), /* @__PURE__ */ React28.createElement(OTP, { onComplete: handleOtpComplete, length: 6, setErrorMessage }), errorMessage ? /* @__PURE__ */ React28.createElement(Alert, { variant: "destructive" }, /* @__PURE__ */ React28.createElement(AlertDescription, null, errorMessage)) : null, successMessage ? /* @__PURE__ */ React28.createElement(Alert, { variant: "default" }, /* @__PURE__ */ React28.createElement(AlertDescription, null, successMessage)) : null, /* @__PURE__ */ React28.createElement(
3393
+ Button,
3394
+ {
3395
+ variant: "outline",
3396
+ size: "lg",
3397
+ onPress: handleSubmit,
3398
+ loading: isLoading,
3399
+ disabled: otp.length !== 6 || isLoading,
3400
+ style: styles13.button
3401
+ },
3402
+ "Continue"
3403
+ ), /* @__PURE__ */ React28.createElement(
3404
+ TouchableOpacity6,
3405
+ {
3406
+ onPress: handleResendOtp,
3407
+ disabled: resendCooldown > 0 || isResending,
3408
+ style: styles13.resendContainer
3409
+ },
3410
+ isResending ? /* @__PURE__ */ React28.createElement(View13, { style: styles13.resendLoading }, /* @__PURE__ */ React28.createElement(LoaderIcon, { size: 16, color: theme.colors.primary }), /* @__PURE__ */ React28.createElement(
3411
+ Text12,
3412
+ {
3413
+ style: [styles13.resendText, { color: theme.colors.primary }]
3414
+ },
3415
+ " ",
3416
+ "Sending..."
3417
+ )) : resendCooldown > 0 ? /* @__PURE__ */ React28.createElement(
3418
+ Text12,
3419
+ {
3420
+ style: [
3421
+ styles13.resendText,
3422
+ { color: theme.colors.textSecondary }
3423
+ ]
3424
+ },
3425
+ "Resend OTP in ",
3426
+ resendCooldown,
3427
+ "s"
3428
+ ) : /* @__PURE__ */ React28.createElement(
3429
+ Text12,
3430
+ {
3431
+ style: [styles13.resendText, { color: theme.colors.primary }]
3432
+ },
3433
+ "Resend OTP"
3434
+ )
3435
+ ))));
3436
+ };
3437
+ var styles13 = StyleSheet13.create({
3438
+ headerContent: {
3439
+ flexDirection: "row",
3440
+ alignItems: "center"
3441
+ },
3442
+ backButton: {
3443
+ padding: 4,
3444
+ // theme.spacing.xs
3445
+ marginRight: 8
3446
+ // theme.spacing.sm
3447
+ },
3448
+ headerTitle: {
3449
+ fontSize: 16,
3450
+ // theme.fontSize.lg
3451
+ fontWeight: "600"
3452
+ },
3453
+ container: {
3454
+ flex: 1,
3455
+ alignItems: "center"
3456
+ },
3457
+ emailInfo: {
3458
+ alignItems: "center",
3459
+ marginBottom: 24
3460
+ // theme.spacing.xxl
3461
+ },
3462
+ infoText: {
3463
+ fontSize: 14,
3464
+ // theme.fontSize.md
3465
+ marginBottom: 4
3466
+ // theme.spacing.xs
3467
+ },
3468
+ emailText: {
3469
+ fontSize: 16,
3470
+ // theme.fontSize.lg
3471
+ fontWeight: "600"
3472
+ },
3473
+ button: {
3474
+ width: "100%",
3475
+ marginTop: 16
3476
+ // theme.spacing.lg
3477
+ },
3478
+ resendContainer: {
3479
+ marginTop: 16,
3480
+ // theme.spacing.lg - consistent spacing
3481
+ padding: 8
3482
+ // theme.spacing.sm
3483
+ },
3484
+ resendLoading: {
3485
+ flexDirection: "row",
3486
+ alignItems: "center"
3487
+ },
3488
+ resendText: {
3489
+ fontSize: 14,
3490
+ // theme.fontSize.md
3491
+ fontWeight: "500"
3492
+ }
3493
+ });
3494
+
3495
+ // src/molecules/Permissions.tsx
3496
+ import React29 from "react";
3497
+ import { View as View14, Text as Text13, StyleSheet as StyleSheet14 } from "react-native";
3498
+ var Permissions = ({
3499
+ open,
3500
+ onClose,
3501
+ onSuccess
3502
+ }) => {
3503
+ const { appName, linkToken, setUserConsent } = useKryptosConnect();
3504
+ const theme = useTheme();
3505
+ const [isLoading, setIsLoading] = React29.useState(false);
3506
+ const [errorMessage, setErrorMessage] = React29.useState("");
3507
+ const handleConsentClick = async () => {
3508
+ try {
3509
+ setIsLoading(true);
3510
+ setErrorMessage("");
3511
+ const res = await giveUserConsent(linkToken);
3512
+ setUserConsent(res);
3513
+ onSuccess();
3514
+ } catch (error) {
3515
+ const err = error;
3516
+ setErrorMessage(
3517
+ err?.response?.data?.message || "Failed to give consent. Please try again."
3518
+ );
3519
+ console.error(error);
3520
+ } finally {
3521
+ setIsLoading(false);
3522
+ }
3523
+ };
3524
+ const permissionItems = [
3525
+ "View your crypto holdings, wallet balances, and portfolio data",
3526
+ "Access your transaction history and trading activity",
3527
+ "Keep this data updated and accessible when you're offline"
3528
+ ];
3529
+ return /* @__PURE__ */ React29.createElement(Modal, { isOpen: open, onClose, size: "xl" }, /* @__PURE__ */ React29.createElement(ModalHeader, { onClose }, "Permissions"), /* @__PURE__ */ React29.createElement(ModalBody, null, /* @__PURE__ */ React29.createElement(View14, { style: styles14.container }, /* @__PURE__ */ React29.createElement(ConnectLogo, null), errorMessage ? /* @__PURE__ */ React29.createElement(Alert, { variant: "destructive" }, /* @__PURE__ */ React29.createElement(AlertDescription, null, errorMessage)) : null, /* @__PURE__ */ React29.createElement(View14, { style: styles14.permissionsList }, /* @__PURE__ */ React29.createElement(
3530
+ Text13,
3531
+ {
3532
+ style: [styles14.subtitle, { color: theme.colors.textSecondary }]
3533
+ },
3534
+ "Allow ",
3535
+ appName,
3536
+ " to:"
3537
+ ), permissionItems.map((item, index) => /* @__PURE__ */ React29.createElement(View14, { key: `permission-${index}`, style: styles14.permissionItem }, /* @__PURE__ */ React29.createElement(Text13, { style: [styles14.bullet, { color: theme.colors.primary }] }, index + 1, "."), /* @__PURE__ */ React29.createElement(
3538
+ Text13,
3539
+ {
3540
+ style: [styles14.permissionText, { color: theme.colors.text }]
3541
+ },
3542
+ item
3543
+ )))), /* @__PURE__ */ React29.createElement(
3544
+ View14,
3545
+ {
3546
+ style: [
3547
+ styles14.infoBox,
3548
+ {
3549
+ backgroundColor: theme.colors.surface,
3550
+ borderColor: theme.colors.border
3551
+ }
3552
+ ]
3553
+ },
3554
+ /* @__PURE__ */ React29.createElement(
3555
+ Text13,
3556
+ {
3557
+ style: [styles14.infoText, { color: theme.colors.textSecondary }]
3558
+ },
3559
+ "By selecting",
3560
+ " ",
3561
+ /* @__PURE__ */ React29.createElement(Text13, { style: { fontWeight: "600", color: theme.colors.text } }, "'Allow'"),
3562
+ ", you agree to share this information and keep it updated."
3563
+ )
3564
+ ))), /* @__PURE__ */ React29.createElement(ModalFooter, null, /* @__PURE__ */ React29.createElement(
3565
+ Button,
3566
+ {
3567
+ variant: "outline",
3568
+ size: "lg",
3569
+ onPress: handleConsentClick,
3570
+ loading: isLoading,
3571
+ disabled: isLoading,
3572
+ style: styles14.button
3573
+ },
3574
+ "Allow"
3575
+ )));
3576
+ };
3577
+ var styles14 = StyleSheet14.create({
3578
+ container: {
3579
+ flex: 1
3580
+ },
3581
+ permissionsList: {
3582
+ marginBottom: 24
3583
+ // theme.spacing.xxl
3584
+ },
3585
+ subtitle: {
3586
+ fontSize: 14,
3587
+ // theme.fontSize.md
3588
+ fontWeight: "500",
3589
+ marginBottom: 16
3590
+ // theme.spacing.lg
3591
+ },
3592
+ permissionItem: {
3593
+ flexDirection: "row",
3594
+ marginBottom: 12,
3595
+ // theme.spacing.md
3596
+ paddingLeft: 4
3597
+ // theme.spacing.xs
3598
+ },
3599
+ bullet: {
3600
+ fontSize: 14,
3601
+ // theme.fontSize.md
3602
+ fontWeight: "600",
3603
+ marginRight: 8,
3604
+ // theme.spacing.sm
3605
+ width: 20
3606
+ // theme.spacing.xl
3607
+ },
3608
+ permissionText: {
3609
+ fontSize: 14,
3610
+ // theme.fontSize.md
3611
+ flex: 1,
3612
+ lineHeight: 20
3613
+ // theme.spacing.xl
3614
+ },
3615
+ infoBox: {
3616
+ padding: 16,
3617
+ // theme.spacing.lg
3618
+ borderRadius: 12,
3619
+ // theme.borderRadius.md
3620
+ borderWidth: 1,
3621
+ marginBottom: 24
3622
+ // theme.spacing.xxl
3623
+ },
3624
+ infoText: {
3625
+ fontSize: 13,
3626
+ lineHeight: 18,
3627
+ textAlign: "center"
3628
+ },
3629
+ button: {
3630
+ width: "100%"
3631
+ }
3632
+ });
3633
+
3634
+ // src/molecules/StatusModal.tsx
3635
+ import React30 from "react";
3636
+ import { View as View15, Text as Text14, StyleSheet as StyleSheet15 } from "react-native";
3637
+ var StatusModal = ({
3638
+ open,
3639
+ onClose,
3640
+ status,
3641
+ onSuccess,
3642
+ onError
3643
+ }) => {
3644
+ const theme = useTheme();
3645
+ const handleClose = () => {
3646
+ if (status === "success") {
3647
+ onSuccess();
3648
+ } else {
3649
+ onError();
3650
+ }
3651
+ onClose();
3652
+ };
3653
+ return /* @__PURE__ */ React30.createElement(Modal, { isOpen: open, onClose: handleClose, size: "xs" }, /* @__PURE__ */ React30.createElement(ModalBody, null, /* @__PURE__ */ React30.createElement(View15, { style: styles15.container }, /* @__PURE__ */ React30.createElement(View15, { style: styles15.iconContainer }, status === "success" ? /* @__PURE__ */ React30.createElement(SuccessIcon, { size: 80 }) : /* @__PURE__ */ React30.createElement(ErrorIcon, { size: 80 })), /* @__PURE__ */ React30.createElement(Text14, { style: [styles15.message, { color: theme.colors.text }] }, status === "success" ? "Connection successful" : "Connection failed"), /* @__PURE__ */ React30.createElement(
3654
+ Button,
3655
+ {
3656
+ variant: "outline",
3657
+ size: "lg",
3658
+ onPress: status === "success" ? onSuccess : onError,
3659
+ style: styles15.button
3660
+ },
3661
+ status === "success" ? "Continue" : "Try again later"
3662
+ ))));
3663
+ };
3664
+ var styles15 = StyleSheet15.create({
3665
+ container: {
3666
+ flex: 1,
3667
+ alignItems: "center",
3668
+ paddingVertical: 24
3669
+ // theme.spacing.xxl
3670
+ },
3671
+ iconContainer: {
3672
+ marginBottom: 24
3673
+ // theme.spacing.xxl
3674
+ },
3675
+ message: {
3676
+ fontSize: 18,
3677
+ // theme.fontSize.xl
3678
+ fontWeight: "600",
3679
+ marginBottom: 32,
3680
+ // theme.spacing.xxxl
3681
+ textAlign: "center"
3682
+ },
3683
+ button: {
3684
+ width: "100%"
3685
+ }
3686
+ });
3687
+
3688
+ // src/KryptosConnectButton.tsx
3689
+ var KryptosConnectButton = ({
3690
+ children,
3691
+ onSuccess,
3692
+ onError,
3693
+ generateLinkToken,
3694
+ style,
3695
+ textStyle
3696
+ }) => {
3697
+ const { theme: themeName } = useKryptosConnect();
3698
+ const [open, setOpen] = React31.useState(false);
3699
+ const theme = useTheme();
3700
+ return /* @__PURE__ */ React31.createElement(React31.Fragment, null, children ? /* @__PURE__ */ React31.createElement(TouchableOpacity7, { onPress: () => setOpen(true), style }, children) : /* @__PURE__ */ React31.createElement(
3701
+ TouchableOpacity7,
3702
+ {
3703
+ onPress: () => setOpen(true),
3704
+ style: [
3705
+ styles16.defaultButton,
3706
+ themeName === "light" ? {
3707
+ backgroundColor: theme.colors.white,
3708
+ borderRadius: theme.borderRadius.md,
3709
+ borderWidth: 1,
3710
+ borderColor: theme.colors.primary
3711
+ } : {
3712
+ backgroundColor: theme.colors.black,
3713
+ borderRadius: theme.borderRadius.md
3714
+ },
3715
+ style
3716
+ ],
3717
+ activeOpacity: 0.8
3718
+ },
3719
+ /* @__PURE__ */ React31.createElement(
3720
+ Text15,
3721
+ {
3722
+ style: [
3723
+ styles16.buttonText,
3724
+ {
3725
+ color: themeName === "light" ? theme.colors.primary : theme.colors.white,
3726
+ fontSize: theme.fontSize.lg
3727
+ },
3728
+ textStyle
3729
+ ]
3730
+ },
3731
+ "Connect with",
3732
+ " "
3733
+ ),
3734
+ /* @__PURE__ */ React31.createElement(LogoIcon, { size: 24 })
3735
+ ), /* @__PURE__ */ React31.createElement(
3736
+ KryptosConnectModal,
3737
+ {
3738
+ open,
3739
+ setOpen,
3740
+ onSuccess,
3741
+ onError,
3742
+ generateLinkToken
3743
+ }
3744
+ ));
3745
+ };
3746
+ var KryptosConnectModal = ({
3747
+ open,
3748
+ setOpen,
3749
+ onSuccess,
3750
+ onError,
3751
+ generateLinkToken
3752
+ }) => {
3753
+ const {
3754
+ setIsInitialized,
3755
+ userConsent,
3756
+ setUserConsent,
3757
+ setUser,
3758
+ setEmail,
3759
+ setLinkToken
3760
+ } = useKryptosConnect();
3761
+ const [step, setStep] = React31.useState("INIT" /* INIT */);
3762
+ const handleClose = () => {
3763
+ setOpen(false);
3764
+ setIsInitialized(false);
3765
+ setStep("INIT" /* INIT */);
3766
+ setUserConsent(null);
3767
+ setUser(null);
3768
+ setEmail("");
3769
+ setLinkToken("");
3770
+ };
3771
+ const handleSuccess = () => {
3772
+ onSuccess?.(userConsent);
3773
+ handleClose();
3774
+ };
3775
+ const handleError = () => {
3776
+ onError?.(userConsent);
3777
+ handleClose();
3778
+ };
3779
+ const handleAbort = () => {
3780
+ onError?.(new Error("User closed the modal"));
3781
+ handleClose();
3782
+ };
3783
+ if (!open) return null;
3784
+ return /* @__PURE__ */ React31.createElement(View16, { style: styles16.container }, step === "INIT" /* INIT */ && /* @__PURE__ */ React31.createElement(
3785
+ Init,
3786
+ {
3787
+ open,
3788
+ generateLinkToken,
3789
+ onSuccess: () => {
3790
+ setStep("AUTH" /* AUTH */);
3791
+ },
3792
+ onClose: handleAbort
3793
+ }
3794
+ ), step === "AUTH" /* AUTH */ && /* @__PURE__ */ React31.createElement(
3795
+ Auth,
3796
+ {
3797
+ open,
3798
+ onEmailSuccess: () => setStep("OTP" /* OTP */),
3799
+ onGuestSuccess: () => setStep("INTEGRATION" /* INTEGRATION */),
3800
+ onClose: handleAbort
3801
+ }
3802
+ ), step === "OTP" /* OTP */ && /* @__PURE__ */ React31.createElement(
3803
+ OTPVerify,
3804
+ {
3805
+ open,
3806
+ onSuccess: () => setStep("INTEGRATION" /* INTEGRATION */),
3807
+ onClose: handleAbort
3808
+ }
3809
+ ), step === "INTEGRATION" /* INTEGRATION */ && /* @__PURE__ */ React31.createElement(
3810
+ Integration,
3811
+ {
3812
+ open,
3813
+ onSuccess: () => setStep("PERMISSIONS" /* PERMISSIONS */),
3814
+ onClose: handleAbort
3815
+ }
3816
+ ), step === "PERMISSIONS" /* PERMISSIONS */ && /* @__PURE__ */ React31.createElement(
3817
+ Permissions,
3818
+ {
3819
+ open,
3820
+ onClose: handleAbort,
3821
+ onSuccess: () => setStep("STATUS" /* STATUS */)
3822
+ }
3823
+ ), step === "STATUS" /* STATUS */ && /* @__PURE__ */ React31.createElement(
3824
+ StatusModal,
3825
+ {
3826
+ open,
3827
+ onClose: handleAbort,
3828
+ onSuccess: handleSuccess,
3829
+ onError: handleError,
3830
+ status: userConsent?.public_token ? "success" : "error"
3831
+ }
3832
+ ));
3833
+ };
3834
+ var styles16 = StyleSheet16.create({
3835
+ defaultButton: {
3836
+ flexDirection: "row",
3837
+ alignItems: "center",
3838
+ justifyContent: "center",
3839
+ paddingVertical: 14,
3840
+ paddingHorizontal: 20,
3841
+ minHeight: 48,
3842
+ gap: 8
3843
+ },
3844
+ buttonText: {
3845
+ fontWeight: "600"
3846
+ },
3847
+ container: {
3848
+ position: "absolute",
3849
+ top: 0,
3850
+ left: 0,
3851
+ right: 0,
3852
+ bottom: 0
3853
+ }
3854
+ });
3855
+ export {
3856
+ KryptosConnectButton,
3857
+ KryptosConnectModal,
3858
+ KryptosConnectProvider,
3859
+ useKryptosConnect
3860
+ };