@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/LICENSE +22 -0
- package/README.md +268 -0
- package/dist/index.d.mts +72 -0
- package/dist/index.d.ts +72 -0
- package/dist/index.js +3834 -0
- package/dist/index.mjs +3860 -0
- package/package.json +115 -0
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
|
+
};
|