@naculus/connect-native 0.8.1 → 0.10.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.
@@ -0,0 +1,996 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/ui/index.ts
21
+ var ui_exports = {};
22
+ __export(ui_exports, {
23
+ AuthorizationConsentNative: () => AuthorizationConsentNative,
24
+ AuthorizationListNative: () => AuthorizationListNative,
25
+ NativePaymentThemeProvider: () => NativePaymentThemeProvider,
26
+ PaymentReceiptNative: () => PaymentReceiptNative,
27
+ darkPaymentTheme: () => darkPaymentTheme,
28
+ lightPaymentTheme: () => lightPaymentTheme,
29
+ useNativePaymentTheme: () => useNativePaymentTheme
30
+ });
31
+ module.exports = __toCommonJS(ui_exports);
32
+
33
+ // src/ui/AuthorizationConsentNative.tsx
34
+ var import_connect_appkit_core = require("@naculus/connect-appkit-core");
35
+ var import_react_native3 = require("react-native");
36
+
37
+ // src/ui/parts.tsx
38
+ var import_react = require("react");
39
+ var import_react_native = require("react-native");
40
+ var import_jsx_runtime = require("react/jsx-runtime");
41
+ function toneColors(theme, tone) {
42
+ if (tone === "success")
43
+ return {
44
+ backgroundColor: theme.successBackground,
45
+ borderColor: theme.successBorder,
46
+ color: theme.successForeground
47
+ };
48
+ if (tone === "danger")
49
+ return {
50
+ backgroundColor: theme.dangerBackground,
51
+ borderColor: theme.dangerBorder,
52
+ color: theme.dangerForeground
53
+ };
54
+ if (tone === "warning")
55
+ return {
56
+ backgroundColor: theme.warningBackground,
57
+ borderColor: theme.warningBorder,
58
+ color: theme.warningForeground
59
+ };
60
+ return {
61
+ backgroundColor: theme.muted,
62
+ borderColor: theme.border,
63
+ color: theme.foreground
64
+ };
65
+ }
66
+ function Glyph({
67
+ children,
68
+ color
69
+ }) {
70
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
71
+ import_react_native.Text,
72
+ {
73
+ accessible: false,
74
+ importantForAccessibility: "no",
75
+ accessibilityElementsHidden: true,
76
+ style: [styles.glyph, { color }],
77
+ children
78
+ }
79
+ );
80
+ }
81
+ function ActionButton({
82
+ label,
83
+ onPress,
84
+ theme,
85
+ variant = "primary",
86
+ disabled = false,
87
+ busy = false
88
+ }) {
89
+ const primary = variant === "primary";
90
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
91
+ import_react_native.Pressable,
92
+ {
93
+ accessibilityRole: "button",
94
+ accessibilityLabel: label,
95
+ accessibilityState: { disabled, busy },
96
+ disabled,
97
+ onPress,
98
+ style: ({ pressed }) => [
99
+ styles.button,
100
+ {
101
+ backgroundColor: primary ? theme.primary : theme.background,
102
+ borderColor: primary ? theme.primary : theme.border,
103
+ opacity: disabled ? 0.55 : pressed ? 0.8 : 1
104
+ }
105
+ ],
106
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
107
+ import_react_native.Text,
108
+ {
109
+ style: [
110
+ styles.buttonLabel,
111
+ { color: primary ? theme.primaryForeground : theme.foreground }
112
+ ],
113
+ children: label
114
+ }
115
+ )
116
+ }
117
+ );
118
+ }
119
+ function CopyableValue({
120
+ value,
121
+ display,
122
+ onCopy,
123
+ theme,
124
+ color = theme.foreground
125
+ }) {
126
+ const [copied, setCopied] = (0, import_react.useState)(false);
127
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: styles.copyRow, children: [
128
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
129
+ import_react_native.Text,
130
+ {
131
+ accessibilityLabel: value,
132
+ numberOfLines: 1,
133
+ ellipsizeMode: "middle",
134
+ style: [styles.mono, { color }],
135
+ children: display ?? value
136
+ }
137
+ ),
138
+ onCopy && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
139
+ import_react_native.Pressable,
140
+ {
141
+ accessibilityRole: "button",
142
+ accessibilityLabel: copied ? `Copied ${value}` : `Copy ${value}`,
143
+ hitSlop: 6,
144
+ onPress: () => {
145
+ onCopy(value);
146
+ setCopied(true);
147
+ },
148
+ style: ({ pressed }) => [
149
+ styles.iconButton,
150
+ { borderColor: theme.border, opacity: pressed ? 0.7 : 1 }
151
+ ],
152
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Glyph, { color: theme.foreground, children: copied ? "\u2713" : "\u29C9" })
153
+ }
154
+ )
155
+ ] });
156
+ }
157
+ function Callout({
158
+ tone,
159
+ glyph,
160
+ theme,
161
+ role,
162
+ children
163
+ }) {
164
+ const colors = toneColors(theme, tone);
165
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
166
+ import_react_native.View,
167
+ {
168
+ accessibilityRole: role,
169
+ style: [
170
+ styles.callout,
171
+ {
172
+ backgroundColor: colors.backgroundColor,
173
+ borderColor: colors.borderColor
174
+ }
175
+ ],
176
+ children: [
177
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Glyph, { color: colors.color, children: glyph }),
178
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.Text, { style: [styles.calloutText, { color: colors.color }], children })
179
+ ]
180
+ }
181
+ );
182
+ }
183
+ var styles = import_react_native.StyleSheet.create({
184
+ glyph: { fontSize: 16, lineHeight: 20, fontWeight: "700" },
185
+ button: {
186
+ flex: 1,
187
+ minHeight: 48,
188
+ minWidth: 44,
189
+ borderRadius: 10,
190
+ borderWidth: 1,
191
+ alignItems: "center",
192
+ justifyContent: "center",
193
+ paddingHorizontal: 16
194
+ },
195
+ buttonLabel: { fontSize: 16, fontWeight: "600" },
196
+ copyRow: { flexDirection: "row", alignItems: "center", gap: 6 },
197
+ mono: { flexShrink: 1, fontFamily: "monospace", fontSize: 13 },
198
+ iconButton: {
199
+ width: 32,
200
+ height: 32,
201
+ borderRadius: 8,
202
+ borderWidth: 1,
203
+ alignItems: "center",
204
+ justifyContent: "center"
205
+ },
206
+ callout: {
207
+ flexDirection: "row",
208
+ gap: 8,
209
+ borderWidth: 1,
210
+ borderRadius: 10,
211
+ padding: 12
212
+ },
213
+ calloutText: { flex: 1, fontSize: 14, lineHeight: 20 },
214
+ label: { fontSize: 12, fontWeight: "500" },
215
+ heading: { fontSize: 18, fontWeight: "600" },
216
+ chip: {
217
+ borderWidth: 1,
218
+ borderRadius: 999,
219
+ paddingHorizontal: 10,
220
+ paddingVertical: 4
221
+ },
222
+ chipText: { fontSize: 12 }
223
+ });
224
+
225
+ // src/ui/theme.tsx
226
+ var import_react2 = require("react");
227
+ var import_react_native2 = require("react-native");
228
+ var import_jsx_runtime2 = require("react/jsx-runtime");
229
+ var lightPaymentTheme = {
230
+ scheme: "light",
231
+ background: "#ffffff",
232
+ // --background 0 0% 100%
233
+ foreground: "#020817",
234
+ // --foreground 222.2 84% 4.9%
235
+ card: "#ffffff",
236
+ // --card
237
+ border: "#e2e8f0",
238
+ // --border 214.3 31.8% 91.4%
239
+ muted: "#f1f5f9",
240
+ // --muted 210 40% 96.1%
241
+ mutedForeground: "#475569",
242
+ // --muted-foreground hue, one step darker
243
+ primary: "#0f172a",
244
+ // --primary 222.2 47.4% 11.2%
245
+ primaryForeground: "#f8fafc",
246
+ // --primary-foreground 210 40% 98%
247
+ successBackground: "#ecfdf5",
248
+ successBorder: "#047857",
249
+ successForeground: "#064e3b",
250
+ dangerBackground: "#fef2f2",
251
+ dangerBorder: "#ef4444",
252
+ // --destructive 0 84.2% 60.2%
253
+ dangerForeground: "#991b1b",
254
+ warningBackground: "#fffbeb",
255
+ warningBorder: "#b45309",
256
+ warningForeground: "#451a03"
257
+ };
258
+ var darkPaymentTheme = {
259
+ scheme: "dark",
260
+ background: "#020817",
261
+ // .dark --background
262
+ foreground: "#f8fafc",
263
+ // .dark --foreground
264
+ card: "#020817",
265
+ // .dark --card
266
+ border: "#1e293b",
267
+ // .dark --border 217.2 32.6% 17.5%
268
+ muted: "#1e293b",
269
+ // .dark --muted
270
+ mutedForeground: "#94a3b8",
271
+ // .dark --muted-foreground 215 20.2% 65.1%
272
+ primary: "#f8fafc",
273
+ // .dark --primary
274
+ primaryForeground: "#0f172a",
275
+ // .dark --primary-foreground
276
+ successBackground: "#022c22",
277
+ successBorder: "#10b981",
278
+ successForeground: "#d1fae5",
279
+ dangerBackground: "#450a0a",
280
+ dangerBorder: "#f87171",
281
+ dangerForeground: "#fecaca",
282
+ warningBackground: "#451a03",
283
+ warningBorder: "#f59e0b",
284
+ warningForeground: "#fef3c7"
285
+ };
286
+ var ThemeContext = (0, import_react2.createContext)(void 0);
287
+ function NativePaymentThemeProvider({
288
+ theme,
289
+ children
290
+ }) {
291
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ThemeContext.Provider, { value: theme, children });
292
+ }
293
+ function useNativePaymentTheme(override) {
294
+ const provided = (0, import_react2.useContext)(ThemeContext);
295
+ const scheme = (0, import_react_native2.useColorScheme)();
296
+ return override ?? provided ?? (scheme === "dark" ? darkPaymentTheme : lightPaymentTheme);
297
+ }
298
+
299
+ // src/ui/AuthorizationConsentNative.tsx
300
+ var import_jsx_runtime3 = require("react/jsx-runtime");
301
+ function AuthorizationConsentNative({
302
+ authorization,
303
+ assets = {},
304
+ trustedAssets,
305
+ locale,
306
+ now,
307
+ requester,
308
+ preview,
309
+ onApprove,
310
+ onDecline,
311
+ busy = false,
312
+ onCopy,
313
+ theme: themeProp
314
+ }) {
315
+ const theme = useNativePaymentTheme(themeProp);
316
+ const description = "expiry" in authorization ? authorization : (0, import_connect_appkit_core.describeAuthorization)(authorization, {
317
+ assets,
318
+ trustedAssets,
319
+ locale,
320
+ now
321
+ });
322
+ const muted = { color: theme.mutedForeground };
323
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: [styles2.root, { backgroundColor: theme.background }], children: [
324
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.ScrollView, { contentContainerStyle: styles2.content, children: [
325
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: styles2.requester, children: [
326
+ requester.icon ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
327
+ import_react_native3.Image,
328
+ {
329
+ accessibilityIgnoresInvertColors: true,
330
+ source: { uri: requester.icon },
331
+ style: styles2.avatar
332
+ }
333
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
334
+ import_react_native3.View,
335
+ {
336
+ importantForAccessibility: "no",
337
+ style: [styles2.avatar, { backgroundColor: theme.muted }],
338
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles2.initial, { color: theme.foreground }], children: requester.name.slice(0, 1).toUpperCase() })
339
+ }
340
+ ),
341
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: styles2.flexShrink, children: [
342
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
343
+ import_react_native3.Text,
344
+ {
345
+ accessibilityRole: "header",
346
+ style: [styles.heading, { color: theme.foreground }],
347
+ children: [
348
+ "Allow ",
349
+ requester.name,
350
+ " to pay?"
351
+ ]
352
+ }
353
+ ),
354
+ requester.origin && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles2.small, muted], children: requester.origin })
355
+ ] })
356
+ ] }),
357
+ description.warnings.map((warning) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
358
+ Callout,
359
+ {
360
+ tone: "warning",
361
+ glyph: "\u26A0",
362
+ role: "alert",
363
+ theme,
364
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: styles2.strong, children: warning })
365
+ },
366
+ warning
367
+ )),
368
+ preview && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
369
+ Callout,
370
+ {
371
+ tone: preview.allowed ? "success" : "danger",
372
+ glyph: preview.allowed ? "\u2713" : "\u2715",
373
+ theme,
374
+ children: [
375
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.Text, { style: styles2.strong, children: [
376
+ preview.allowed ? "Allowed" : "Blocked",
377
+ "."
378
+ ] }),
379
+ " ",
380
+ preview.sentence
381
+ ]
382
+ }
383
+ ),
384
+ description.grants.map((grant) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
385
+ import_react_native3.View,
386
+ {
387
+ style: [
388
+ styles2.card,
389
+ { backgroundColor: theme.card, borderColor: theme.border }
390
+ ],
391
+ children: [
392
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles2.assetLabel, { color: theme.foreground }], children: grant.assetLabel }),
393
+ grant.assetTrust === "unverified" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Callout, { tone: "warning", glyph: "\u26A0", role: "alert", theme, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: styles2.strong, children: "Unverified token \u2014 not on the trusted list" }) }),
394
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: styles2.limits, children: [
395
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: styles2.limit, children: [
396
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles.label, muted], children: "Per payment" }),
397
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles2.amount, { color: theme.foreground }], children: grant.perPayment.formatted })
398
+ ] }),
399
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: styles2.limit, children: [
400
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles.label, muted], children: "Total limit" }),
401
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles2.amount, { color: theme.foreground }], children: grant.total.formatted })
402
+ ] })
403
+ ] }),
404
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles2.term, { color: theme.foreground }], children: "Recipients" }),
405
+ grant.recipients.length ? grant.recipients.map((recipient) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
406
+ CopyableValue,
407
+ {
408
+ value: recipient.full,
409
+ display: recipient.shortened,
410
+ onCopy,
411
+ theme
412
+ },
413
+ recipient.full
414
+ )) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles2.strong, { color: theme.dangerForeground }], children: "No recipient restriction is displayed" }),
415
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles2.term, { color: theme.foreground }], children: "Payment count" }),
416
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles2.small, muted], children: grant.count === null ? "No count limit" : `Up to ${grant.count}` }),
417
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles2.term, { color: theme.foreground }], children: "Payment methods" }),
418
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: styles2.chips, children: grant.rails.map((rail) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
419
+ import_react_native3.View,
420
+ {
421
+ style: [
422
+ styles.chip,
423
+ { backgroundColor: theme.muted, borderColor: theme.border }
424
+ ],
425
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles.chipText, { color: theme.foreground }], children: rail })
426
+ },
427
+ rail
428
+ )) })
429
+ ]
430
+ },
431
+ `${grant.asset}:${grant.recipients.map((r) => r.full).join(",")}:${grant.perPayment.formatted}:${grant.total.formatted}`
432
+ )),
433
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: [styles2.expiry, { backgroundColor: theme.muted }], children: [
434
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.Text, { style: [styles2.strong, { color: theme.foreground }], children: [
435
+ "Expires ",
436
+ description.expiry.relative
437
+ ] }),
438
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles2.small, muted], children: description.expiry.absolute })
439
+ ] })
440
+ ] }),
441
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
442
+ import_react_native3.View,
443
+ {
444
+ testID: "payment-actions",
445
+ style: [
446
+ styles2.footer,
447
+ { borderTopColor: theme.border, backgroundColor: theme.background }
448
+ ],
449
+ children: [
450
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
451
+ ActionButton,
452
+ {
453
+ label: "Decline",
454
+ variant: "secondary",
455
+ onPress: onDecline,
456
+ theme
457
+ }
458
+ ),
459
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
460
+ ActionButton,
461
+ {
462
+ label: busy ? "Approving\u2026" : "Approve",
463
+ onPress: onApprove,
464
+ disabled: busy,
465
+ busy,
466
+ theme
467
+ }
468
+ )
469
+ ]
470
+ }
471
+ )
472
+ ] });
473
+ }
474
+ var styles2 = import_react_native3.StyleSheet.create({
475
+ root: { flex: 1 },
476
+ content: { padding: 16, gap: 12 },
477
+ requester: { flexDirection: "row", alignItems: "center", gap: 12 },
478
+ avatar: {
479
+ width: 44,
480
+ height: 44,
481
+ borderRadius: 12,
482
+ alignItems: "center",
483
+ justifyContent: "center"
484
+ },
485
+ initial: { fontSize: 18, fontWeight: "600" },
486
+ flexShrink: { flexShrink: 1 },
487
+ small: { fontSize: 12 },
488
+ strong: { fontWeight: "700" },
489
+ card: { borderWidth: 1, borderRadius: 12, padding: 16, gap: 6 },
490
+ assetLabel: { fontSize: 14, fontWeight: "600" },
491
+ limits: { flexDirection: "row", gap: 12, marginVertical: 6 },
492
+ limit: { flex: 1 },
493
+ amount: {
494
+ fontSize: 22,
495
+ fontWeight: "600",
496
+ fontVariant: ["tabular-nums"]
497
+ },
498
+ term: { fontSize: 14, fontWeight: "500", marginTop: 8 },
499
+ chips: { flexDirection: "row", flexWrap: "wrap", gap: 6 },
500
+ expiry: { borderRadius: 10, padding: 12, gap: 2 },
501
+ footer: {
502
+ flexDirection: "row",
503
+ gap: 12,
504
+ paddingHorizontal: 16,
505
+ paddingVertical: 12,
506
+ borderTopWidth: 1
507
+ }
508
+ });
509
+
510
+ // src/ui/AuthorizationListNative.tsx
511
+ var import_connect_appkit_core2 = require("@naculus/connect-appkit-core");
512
+ var import_react3 = require("react");
513
+ var import_react_native4 = require("react-native");
514
+ var import_jsx_runtime4 = require("react/jsx-runtime");
515
+ var ENFORCER_LABELS = {
516
+ "evm-session": "EVM session key",
517
+ "solana-session": "Solana session key",
518
+ "mpp-voucher": "MPP channel key"
519
+ };
520
+ var STATUS_GLYPHS = {
521
+ active: "\u25CF",
522
+ pending: "\u25D0",
523
+ revoked: "\u2715",
524
+ expired: "\u25CB"
525
+ };
526
+ function StatusBadge({
527
+ status,
528
+ theme
529
+ }) {
530
+ const colors = status === "active" ? {
531
+ backgroundColor: theme.primary,
532
+ borderColor: theme.primary,
533
+ color: theme.primaryForeground
534
+ } : status === "pending" ? {
535
+ backgroundColor: theme.background,
536
+ borderColor: theme.foreground,
537
+ color: theme.foreground
538
+ } : status === "revoked" ? toneColors(theme, "danger") : {
539
+ backgroundColor: theme.muted,
540
+ borderColor: theme.border,
541
+ color: theme.mutedForeground
542
+ };
543
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
544
+ import_react_native4.View,
545
+ {
546
+ accessibilityLabel: `Status: ${status}`,
547
+ style: [
548
+ styles.chip,
549
+ styles3.badge,
550
+ {
551
+ backgroundColor: colors.backgroundColor,
552
+ borderColor: colors.borderColor
553
+ }
554
+ ],
555
+ children: [
556
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Glyph, { color: colors.color, children: STATUS_GLYPHS[status] }),
557
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: [styles3.badgeText, { color: colors.color }], children: status })
558
+ ]
559
+ }
560
+ );
561
+ }
562
+ function AuthorizationListNative({
563
+ entries: input,
564
+ assets = {},
565
+ trustedAssets,
566
+ onRevoke,
567
+ revokingId,
568
+ locale,
569
+ onCopy,
570
+ theme: themeProp
571
+ }) {
572
+ const theme = useNativePaymentTheme(themeProp);
573
+ const entries = Array.isArray(input) ? input : input.entries;
574
+ const [confirming, setConfirming] = (0, import_react3.useState)(null);
575
+ const [onChain, setOnChain] = (0, import_react3.useState)(/* @__PURE__ */ new Set());
576
+ const muted = { color: theme.mutedForeground };
577
+ const revoke = (entry) => void Promise.resolve(onRevoke(entry)).then((result) => {
578
+ setConfirming(null);
579
+ if (result.onChainRevocationRequired)
580
+ setOnChain((current) => new Set(current).add(entry.keyId));
581
+ });
582
+ const renderItem = ({ item: entry }) => {
583
+ const view = (0, import_connect_appkit_core2.describeAuthorization)(entry, {
584
+ assets,
585
+ trustedAssets,
586
+ locale
587
+ });
588
+ const busy = revokingId === entry.keyId;
589
+ const live = entry.status === "active" || entry.status === "pending";
590
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
591
+ import_react_native4.View,
592
+ {
593
+ style: [
594
+ styles3.card,
595
+ { backgroundColor: theme.card, borderColor: theme.border }
596
+ ],
597
+ children: [
598
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { style: styles3.cardHeader, children: [
599
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { style: styles3.flexShrink, children: [
600
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: [styles3.title, { color: theme.foreground }], children: view.label ?? view.grants.map((g) => g.assetLabel).join(", ") }),
601
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: [styles3.small, muted], children: ENFORCER_LABELS[entry.enforcer] ?? entry.enforcer.replaceAll("-", " ") })
602
+ ] }),
603
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StatusBadge, { status: entry.status, theme })
604
+ ] }),
605
+ view.grants.map((grant) => {
606
+ const spent = entry.spent?.[grant.asset];
607
+ const total = entry.grants.find(
608
+ (g) => g.asset === grant.asset
609
+ )?.maxTotal;
610
+ const percent = spent !== void 0 && total && total > 0n ? Math.min(Number(spent * 100n / total), 100) : 0;
611
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
612
+ import_react_native4.View,
613
+ {
614
+ style: [styles3.grant, { backgroundColor: theme.muted }],
615
+ children: [
616
+ view.label && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: [styles3.strong, { color: theme.foreground }], children: grant.assetLabel }),
617
+ grant.assetTrust === "unverified" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Callout, { tone: "warning", glyph: "\u26A0", role: "alert", theme, children: "Unverified token \u2014 not on the trusted list" }),
618
+ grant.recipients.map((recipient) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
619
+ CopyableValue,
620
+ {
621
+ value: recipient.full,
622
+ display: recipient.shortened,
623
+ onCopy,
624
+ theme
625
+ },
626
+ recipient.full
627
+ )),
628
+ spent !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { children: [
629
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { style: styles3.spentRow, children: [
630
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.Text, { style: [styles3.small, { color: theme.foreground }], children: [
631
+ "Spent",
632
+ " ",
633
+ (0, import_connect_appkit_core2.formatAuthorizationAmount)(
634
+ spent,
635
+ grant.assetTrust === "unverified" ? void 0 : assets[grant.asset]
636
+ ).formatted
637
+ ] }),
638
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.Text, { style: [styles3.small, { color: theme.foreground }], children: [
639
+ "Total ",
640
+ grant.total.formatted
641
+ ] })
642
+ ] }),
643
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
644
+ import_react_native4.View,
645
+ {
646
+ accessibilityRole: "progressbar",
647
+ accessibilityLabel: `${grant.assetLabel} spent`,
648
+ accessibilityValue: { min: 0, max: 100, now: percent },
649
+ style: [
650
+ styles3.track,
651
+ { backgroundColor: theme.background }
652
+ ],
653
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
654
+ import_react_native4.View,
655
+ {
656
+ style: [
657
+ styles3.fill,
658
+ {
659
+ width: `${percent}%`,
660
+ backgroundColor: theme.primary
661
+ }
662
+ ]
663
+ }
664
+ )
665
+ }
666
+ )
667
+ ] })
668
+ ]
669
+ },
670
+ grant.asset
671
+ );
672
+ }),
673
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.Text, { style: [styles3.small, muted], children: [
674
+ entry.status === "expired" ? "Expired" : entry.status === "revoked" ? "Revoked" : `Expires ${view.expiry.relative}`,
675
+ " ",
676
+ "\xB7 ",
677
+ view.expiry.absolute
678
+ ] }),
679
+ view.warnings.map((warning) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
680
+ Callout,
681
+ {
682
+ tone: "warning",
683
+ glyph: "\u26A0",
684
+ role: "alert",
685
+ theme,
686
+ children: warning
687
+ },
688
+ warning
689
+ )),
690
+ onChain.has(entry.keyId) && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Callout, { tone: "warning", glyph: "\u26D3", theme, children: "Local access is revoked. Complete the on-chain revocation to stop this authorization on chain." }),
691
+ live && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.View, { style: [styles3.actions, { borderTopColor: theme.border }], children: confirming === entry.keyId ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
692
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
693
+ ActionButton,
694
+ {
695
+ label: "Cancel",
696
+ variant: "secondary",
697
+ onPress: () => setConfirming(null),
698
+ theme
699
+ }
700
+ ),
701
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
702
+ ActionButton,
703
+ {
704
+ label: busy ? "Revoking\u2026" : "Confirm revoke",
705
+ disabled: busy,
706
+ busy,
707
+ onPress: () => revoke(entry),
708
+ theme
709
+ }
710
+ )
711
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
712
+ ActionButton,
713
+ {
714
+ label: "Revoke",
715
+ variant: "secondary",
716
+ onPress: () => setConfirming(entry.keyId),
717
+ theme
718
+ }
719
+ ) })
720
+ ]
721
+ }
722
+ );
723
+ };
724
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
725
+ import_react_native4.FlatList,
726
+ {
727
+ data: entries,
728
+ keyExtractor: (entry) => entry.keyId,
729
+ renderItem,
730
+ extraData: [confirming, onChain, revokingId, theme],
731
+ contentContainerStyle: styles3.list,
732
+ style: { backgroundColor: theme.background },
733
+ ListHeaderComponent: entries.length ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
734
+ import_react_native4.Text,
735
+ {
736
+ accessibilityRole: "header",
737
+ style: [styles.heading, { color: theme.foreground }],
738
+ children: "Payment authorizations"
739
+ }
740
+ ) : void 0,
741
+ ListEmptyComponent: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { style: [styles3.empty, { borderColor: theme.border }], children: [
742
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
743
+ import_react_native4.Text,
744
+ {
745
+ accessibilityRole: "header",
746
+ style: [styles3.title, { color: theme.foreground }],
747
+ children: "No payment authorizations"
748
+ }
749
+ ),
750
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: [styles3.body, muted], children: "Approvals you create will appear here." })
751
+ ] })
752
+ }
753
+ );
754
+ }
755
+ var styles3 = import_react_native4.StyleSheet.create({
756
+ list: { padding: 16, gap: 12 },
757
+ card: { borderWidth: 1, borderRadius: 12, padding: 16, gap: 10 },
758
+ cardHeader: {
759
+ flexDirection: "row",
760
+ justifyContent: "space-between",
761
+ alignItems: "flex-start",
762
+ gap: 8
763
+ },
764
+ flexShrink: { flexShrink: 1 },
765
+ title: { fontSize: 16, fontWeight: "600" },
766
+ small: { fontSize: 12 },
767
+ body: { fontSize: 14, textAlign: "center" },
768
+ strong: { fontSize: 14, fontWeight: "500" },
769
+ badge: { flexDirection: "row", alignItems: "center", gap: 4 },
770
+ badgeText: { fontSize: 12, fontWeight: "600", textTransform: "capitalize" },
771
+ grant: { borderRadius: 10, padding: 12, gap: 6 },
772
+ spentRow: {
773
+ flexDirection: "row",
774
+ justifyContent: "space-between",
775
+ gap: 8
776
+ },
777
+ track: { height: 8, borderRadius: 4, overflow: "hidden", marginTop: 4 },
778
+ fill: { height: "100%" },
779
+ actions: {
780
+ flexDirection: "row",
781
+ justifyContent: "flex-end",
782
+ gap: 8,
783
+ borderTopWidth: 1,
784
+ paddingTop: 12
785
+ },
786
+ empty: {
787
+ borderWidth: 1,
788
+ borderStyle: "dashed",
789
+ borderRadius: 12,
790
+ padding: 32,
791
+ alignItems: "center",
792
+ gap: 4
793
+ }
794
+ });
795
+
796
+ // src/ui/PaymentReceiptNative.tsx
797
+ var import_connect_appkit_core3 = require("@naculus/connect-appkit-core");
798
+ var import_react_native5 = require("react-native");
799
+ var import_jsx_runtime5 = require("react/jsx-runtime");
800
+ var VERIFICATION = {
801
+ unverified: {
802
+ title: "Unverified",
803
+ detail: "Reported by the server, not yet checked.",
804
+ glyph: "?",
805
+ tone: "neutral"
806
+ },
807
+ pending: {
808
+ title: "Checking",
809
+ detail: "Checking on chain.",
810
+ glyph: "\u2026",
811
+ tone: "neutral"
812
+ },
813
+ verified: {
814
+ title: "Verified",
815
+ detail: "Verified on chain.",
816
+ glyph: "\u2713",
817
+ tone: "success"
818
+ },
819
+ mismatch: {
820
+ title: "Mismatch",
821
+ detail: "The on-chain settlement does not match this payment.",
822
+ glyph: "!",
823
+ tone: "danger"
824
+ },
825
+ failed: {
826
+ title: "Verification failed",
827
+ detail: "The settlement could not be verified.",
828
+ glyph: "!",
829
+ tone: "danger"
830
+ },
831
+ unavailable: {
832
+ title: "Verification unavailable",
833
+ detail: "On-chain checking is unavailable. Retry later.",
834
+ glyph: "?",
835
+ tone: "neutral"
836
+ }
837
+ };
838
+ function PaymentReceiptNative({
839
+ record,
840
+ assets = {},
841
+ onOpenExplorer,
842
+ onCopy,
843
+ theme: themeProp
844
+ }) {
845
+ const theme = useNativePaymentTheme(themeProp);
846
+ const status = record.verification.status;
847
+ const state = VERIFICATION[status];
848
+ const colors = toneColors(theme, state.tone);
849
+ const muted = { color: theme.mutedForeground };
850
+ const amount = record.amount === null ? "Amount not reported" : /^\d+$/.test(record.amount) ? (0, import_connect_appkit_core3.formatAuthorizationAmount)(
851
+ BigInt(record.amount),
852
+ record.asset ? assets[record.asset] : void 0
853
+ ).formatted : `${record.amount} base units`;
854
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
855
+ import_react_native5.View,
856
+ {
857
+ testID: `verification-${status}`,
858
+ style: [
859
+ styles4.card,
860
+ { backgroundColor: theme.card, borderColor: theme.border }
861
+ ],
862
+ children: [
863
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { style: styles4.header, children: [
864
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.eyebrow, muted], children: "PAYMENT RECEIPT" }),
865
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
866
+ import_react_native5.Text,
867
+ {
868
+ accessibilityRole: "header",
869
+ style: [styles4.amount, { color: theme.foreground }],
870
+ children: amount
871
+ }
872
+ ),
873
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.small, muted], children: record.asset ? assets[record.asset]?.symbol ?? record.asset : "Asset not reported" })
874
+ ] }),
875
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
876
+ import_react_native5.View,
877
+ {
878
+ testID: "verification-panel",
879
+ accessibilityLiveRegion: "polite",
880
+ accessible: true,
881
+ accessibilityLabel: `${state.title}. ${state.detail}${record.verification.reason ? ` Reason: ${record.verification.reason}` : ""}`,
882
+ style: [
883
+ styles4.panel,
884
+ {
885
+ backgroundColor: colors.backgroundColor,
886
+ borderColor: colors.borderColor
887
+ }
888
+ ],
889
+ children: [
890
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Glyph, { color: colors.color, children: state.glyph }),
891
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { style: styles4.flexShrink, children: [
892
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.badge, { color: colors.color }], children: state.title }),
893
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.body, { color: colors.color }], children: state.detail }),
894
+ record.verification.reason && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
895
+ import_react_native5.Text,
896
+ {
897
+ numberOfLines: 2,
898
+ style: [styles4.body, styles4.strong, { color: colors.color }],
899
+ children: [
900
+ "Reason: ",
901
+ record.verification.reason
902
+ ]
903
+ }
904
+ )
905
+ ] })
906
+ ]
907
+ }
908
+ ),
909
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { style: styles4.field, children: [
910
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.small, muted], children: "Recipient" }),
911
+ record.payTo ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CopyableValue, { value: record.payTo, onCopy, theme }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.body, { color: theme.foreground }], children: "Not reported" })
912
+ ] }),
913
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { style: styles4.row, children: [
914
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { style: styles4.cell, children: [
915
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.small, muted], children: "Protocol" }),
916
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.body, { color: theme.foreground }], children: record.protocol === "mpp" ? "MPP" : record.protocol })
917
+ ] }),
918
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { style: styles4.cell, children: [
919
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.small, muted], children: "Time" }),
920
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.body, { color: theme.foreground }], children: "Not recorded" })
921
+ ] })
922
+ ] }),
923
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { style: styles4.field, children: [
924
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.small, muted], children: "Settlement reference" }),
925
+ record.reference ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
926
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
927
+ CopyableValue,
928
+ {
929
+ value: record.reference,
930
+ onCopy,
931
+ theme
932
+ }
933
+ ),
934
+ onOpenExplorer && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
935
+ import_react_native5.Pressable,
936
+ {
937
+ accessibilityRole: "link",
938
+ accessibilityLabel: "View on explorer",
939
+ onPress: () => onOpenExplorer(record),
940
+ style: styles4.link,
941
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.linkText, { color: theme.foreground }], children: "View on explorer \u2197" })
942
+ }
943
+ )
944
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.body, { color: theme.foreground }], children: "Not reported" })
945
+ ] })
946
+ ]
947
+ }
948
+ );
949
+ }
950
+ var styles4 = import_react_native5.StyleSheet.create({
951
+ card: { borderWidth: 1, borderRadius: 16, padding: 20, gap: 16 },
952
+ header: { alignItems: "center", gap: 4 },
953
+ eyebrow: { fontSize: 12, fontWeight: "500", letterSpacing: 0.6 },
954
+ amount: {
955
+ fontSize: 32,
956
+ fontWeight: "600",
957
+ fontVariant: ["tabular-nums"],
958
+ textAlign: "center"
959
+ },
960
+ small: { fontSize: 12 },
961
+ body: { fontSize: 14, lineHeight: 20 },
962
+ strong: { fontWeight: "600" },
963
+ // Fixed height: badge, explanation and a two-line reason fit, so the card
964
+ // does not jump as verification moves from pending to its final state.
965
+ panel: {
966
+ height: 128,
967
+ flexDirection: "row",
968
+ gap: 8,
969
+ borderWidth: 1,
970
+ borderRadius: 10,
971
+ padding: 12,
972
+ overflow: "hidden"
973
+ },
974
+ flexShrink: { flexShrink: 1 },
975
+ badge: { fontSize: 13, fontWeight: "700", marginBottom: 2 },
976
+ field: { gap: 4 },
977
+ row: { flexDirection: "row", gap: 12 },
978
+ cell: { flex: 1, gap: 4 },
979
+ link: { minHeight: 44, minWidth: 44, justifyContent: "center" },
980
+ linkText: {
981
+ fontSize: 13,
982
+ fontWeight: "500",
983
+ textDecorationLine: "underline"
984
+ }
985
+ });
986
+ // Annotate the CommonJS export names for ESM import in node:
987
+ 0 && (module.exports = {
988
+ AuthorizationConsentNative,
989
+ AuthorizationListNative,
990
+ NativePaymentThemeProvider,
991
+ PaymentReceiptNative,
992
+ darkPaymentTheme,
993
+ lightPaymentTheme,
994
+ useNativePaymentTheme
995
+ });
996
+ //# sourceMappingURL=index.cjs.map