@naculus/connect-native 0.8.1 → 0.9.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,980 @@
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
+ locale,
305
+ now,
306
+ requester,
307
+ preview,
308
+ onApprove,
309
+ onDecline,
310
+ busy = false,
311
+ onCopy,
312
+ theme: themeProp
313
+ }) {
314
+ const theme = useNativePaymentTheme(themeProp);
315
+ const description = "expiry" in authorization ? authorization : (0, import_connect_appkit_core.describeAuthorization)(authorization, { assets, locale, now });
316
+ const muted = { color: theme.mutedForeground };
317
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: [styles2.root, { backgroundColor: theme.background }], children: [
318
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.ScrollView, { contentContainerStyle: styles2.content, children: [
319
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: styles2.requester, children: [
320
+ requester.icon ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
321
+ import_react_native3.Image,
322
+ {
323
+ accessibilityIgnoresInvertColors: true,
324
+ source: { uri: requester.icon },
325
+ style: styles2.avatar
326
+ }
327
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
328
+ import_react_native3.View,
329
+ {
330
+ importantForAccessibility: "no",
331
+ style: [styles2.avatar, { backgroundColor: theme.muted }],
332
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles2.initial, { color: theme.foreground }], children: requester.name.slice(0, 1).toUpperCase() })
333
+ }
334
+ ),
335
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: styles2.flexShrink, children: [
336
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
337
+ import_react_native3.Text,
338
+ {
339
+ accessibilityRole: "header",
340
+ style: [styles.heading, { color: theme.foreground }],
341
+ children: [
342
+ "Allow ",
343
+ requester.name,
344
+ " to pay?"
345
+ ]
346
+ }
347
+ ),
348
+ requester.origin && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles2.small, muted], children: requester.origin })
349
+ ] })
350
+ ] }),
351
+ description.warnings.map((warning) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
352
+ Callout,
353
+ {
354
+ tone: "warning",
355
+ glyph: "\u26A0",
356
+ role: "alert",
357
+ theme,
358
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: styles2.strong, children: warning })
359
+ },
360
+ warning
361
+ )),
362
+ preview && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
363
+ Callout,
364
+ {
365
+ tone: preview.allowed ? "success" : "danger",
366
+ glyph: preview.allowed ? "\u2713" : "\u2715",
367
+ theme,
368
+ children: [
369
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.Text, { style: styles2.strong, children: [
370
+ preview.allowed ? "Allowed" : "Blocked",
371
+ "."
372
+ ] }),
373
+ " ",
374
+ preview.sentence
375
+ ]
376
+ }
377
+ ),
378
+ description.grants.map((grant) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
379
+ import_react_native3.View,
380
+ {
381
+ style: [
382
+ styles2.card,
383
+ { backgroundColor: theme.card, borderColor: theme.border }
384
+ ],
385
+ children: [
386
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles2.assetLabel, { color: theme.foreground }], children: grant.assetLabel }),
387
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: styles2.limits, children: [
388
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: styles2.limit, children: [
389
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles.label, muted], children: "Per payment" }),
390
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles2.amount, { color: theme.foreground }], children: grant.perPayment.formatted })
391
+ ] }),
392
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: styles2.limit, children: [
393
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles.label, muted], children: "Total limit" }),
394
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles2.amount, { color: theme.foreground }], children: grant.total.formatted })
395
+ ] })
396
+ ] }),
397
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles2.term, { color: theme.foreground }], children: "Recipients" }),
398
+ grant.recipients.length ? grant.recipients.map((recipient) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
399
+ CopyableValue,
400
+ {
401
+ value: recipient.full,
402
+ display: recipient.shortened,
403
+ onCopy,
404
+ theme
405
+ },
406
+ recipient.full
407
+ )) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles2.strong, { color: theme.dangerForeground }], children: "No recipient restriction is displayed" }),
408
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles2.term, { color: theme.foreground }], children: "Payment count" }),
409
+ /* @__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}` }),
410
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles2.term, { color: theme.foreground }], children: "Payment methods" }),
411
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: styles2.chips, children: grant.rails.map((rail) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
412
+ import_react_native3.View,
413
+ {
414
+ style: [
415
+ styles.chip,
416
+ { backgroundColor: theme.muted, borderColor: theme.border }
417
+ ],
418
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles.chipText, { color: theme.foreground }], children: rail })
419
+ },
420
+ rail
421
+ )) })
422
+ ]
423
+ },
424
+ `${grant.asset}:${grant.recipients.map((r) => r.full).join(",")}:${grant.perPayment.formatted}:${grant.total.formatted}`
425
+ )),
426
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.View, { style: [styles2.expiry, { backgroundColor: theme.muted }], children: [
427
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native3.Text, { style: [styles2.strong, { color: theme.foreground }], children: [
428
+ "Expires ",
429
+ description.expiry.relative
430
+ ] }),
431
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.Text, { style: [styles2.small, muted], children: description.expiry.absolute })
432
+ ] })
433
+ ] }),
434
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
435
+ import_react_native3.View,
436
+ {
437
+ testID: "payment-actions",
438
+ style: [
439
+ styles2.footer,
440
+ { borderTopColor: theme.border, backgroundColor: theme.background }
441
+ ],
442
+ children: [
443
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
444
+ ActionButton,
445
+ {
446
+ label: "Decline",
447
+ variant: "secondary",
448
+ onPress: onDecline,
449
+ theme
450
+ }
451
+ ),
452
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
453
+ ActionButton,
454
+ {
455
+ label: busy ? "Approving\u2026" : "Approve",
456
+ onPress: onApprove,
457
+ disabled: busy,
458
+ busy,
459
+ theme
460
+ }
461
+ )
462
+ ]
463
+ }
464
+ )
465
+ ] });
466
+ }
467
+ var styles2 = import_react_native3.StyleSheet.create({
468
+ root: { flex: 1 },
469
+ content: { padding: 16, gap: 12 },
470
+ requester: { flexDirection: "row", alignItems: "center", gap: 12 },
471
+ avatar: {
472
+ width: 44,
473
+ height: 44,
474
+ borderRadius: 12,
475
+ alignItems: "center",
476
+ justifyContent: "center"
477
+ },
478
+ initial: { fontSize: 18, fontWeight: "600" },
479
+ flexShrink: { flexShrink: 1 },
480
+ small: { fontSize: 12 },
481
+ strong: { fontWeight: "700" },
482
+ card: { borderWidth: 1, borderRadius: 12, padding: 16, gap: 6 },
483
+ assetLabel: { fontSize: 14, fontWeight: "600" },
484
+ limits: { flexDirection: "row", gap: 12, marginVertical: 6 },
485
+ limit: { flex: 1 },
486
+ amount: {
487
+ fontSize: 22,
488
+ fontWeight: "600",
489
+ fontVariant: ["tabular-nums"]
490
+ },
491
+ term: { fontSize: 14, fontWeight: "500", marginTop: 8 },
492
+ chips: { flexDirection: "row", flexWrap: "wrap", gap: 6 },
493
+ expiry: { borderRadius: 10, padding: 12, gap: 2 },
494
+ footer: {
495
+ flexDirection: "row",
496
+ gap: 12,
497
+ paddingHorizontal: 16,
498
+ paddingVertical: 12,
499
+ borderTopWidth: 1
500
+ }
501
+ });
502
+
503
+ // src/ui/AuthorizationListNative.tsx
504
+ var import_connect_appkit_core2 = require("@naculus/connect-appkit-core");
505
+ var import_react3 = require("react");
506
+ var import_react_native4 = require("react-native");
507
+ var import_jsx_runtime4 = require("react/jsx-runtime");
508
+ var ENFORCER_LABELS = {
509
+ "evm-session": "EVM session key",
510
+ "solana-session": "Solana session key",
511
+ "mpp-voucher": "MPP channel key"
512
+ };
513
+ var STATUS_GLYPHS = {
514
+ active: "\u25CF",
515
+ pending: "\u25D0",
516
+ revoked: "\u2715",
517
+ expired: "\u25CB"
518
+ };
519
+ function StatusBadge({
520
+ status,
521
+ theme
522
+ }) {
523
+ const colors = status === "active" ? {
524
+ backgroundColor: theme.primary,
525
+ borderColor: theme.primary,
526
+ color: theme.primaryForeground
527
+ } : status === "pending" ? {
528
+ backgroundColor: theme.background,
529
+ borderColor: theme.foreground,
530
+ color: theme.foreground
531
+ } : status === "revoked" ? toneColors(theme, "danger") : {
532
+ backgroundColor: theme.muted,
533
+ borderColor: theme.border,
534
+ color: theme.mutedForeground
535
+ };
536
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
537
+ import_react_native4.View,
538
+ {
539
+ accessibilityLabel: `Status: ${status}`,
540
+ style: [
541
+ styles.chip,
542
+ styles3.badge,
543
+ {
544
+ backgroundColor: colors.backgroundColor,
545
+ borderColor: colors.borderColor
546
+ }
547
+ ],
548
+ children: [
549
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Glyph, { color: colors.color, children: STATUS_GLYPHS[status] }),
550
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: [styles3.badgeText, { color: colors.color }], children: status })
551
+ ]
552
+ }
553
+ );
554
+ }
555
+ function AuthorizationListNative({
556
+ entries: input,
557
+ assets = {},
558
+ onRevoke,
559
+ revokingId,
560
+ locale,
561
+ onCopy,
562
+ theme: themeProp
563
+ }) {
564
+ const theme = useNativePaymentTheme(themeProp);
565
+ const entries = Array.isArray(input) ? input : input.entries;
566
+ const [confirming, setConfirming] = (0, import_react3.useState)(null);
567
+ const [onChain, setOnChain] = (0, import_react3.useState)(/* @__PURE__ */ new Set());
568
+ const muted = { color: theme.mutedForeground };
569
+ const revoke = (entry) => void Promise.resolve(onRevoke(entry)).then((result) => {
570
+ setConfirming(null);
571
+ if (result.onChainRevocationRequired)
572
+ setOnChain((current) => new Set(current).add(entry.keyId));
573
+ });
574
+ const renderItem = ({ item: entry }) => {
575
+ const view = (0, import_connect_appkit_core2.describeAuthorization)(entry, { assets, locale });
576
+ const busy = revokingId === entry.keyId;
577
+ const live = entry.status === "active" || entry.status === "pending";
578
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
579
+ import_react_native4.View,
580
+ {
581
+ style: [
582
+ styles3.card,
583
+ { backgroundColor: theme.card, borderColor: theme.border }
584
+ ],
585
+ children: [
586
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { style: styles3.cardHeader, children: [
587
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { style: styles3.flexShrink, children: [
588
+ /* @__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(", ") }),
589
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: [styles3.small, muted], children: ENFORCER_LABELS[entry.enforcer] ?? entry.enforcer.replaceAll("-", " ") })
590
+ ] }),
591
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StatusBadge, { status: entry.status, theme })
592
+ ] }),
593
+ view.grants.map((grant) => {
594
+ const spent = entry.spent?.[grant.asset];
595
+ const total = entry.grants.find(
596
+ (g) => g.asset === grant.asset
597
+ )?.maxTotal;
598
+ const percent = spent !== void 0 && total && total > 0n ? Math.min(Number(spent * 100n / total), 100) : 0;
599
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
600
+ import_react_native4.View,
601
+ {
602
+ style: [styles3.grant, { backgroundColor: theme.muted }],
603
+ children: [
604
+ view.label && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: [styles3.strong, { color: theme.foreground }], children: grant.assetLabel }),
605
+ grant.recipients.map((recipient) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
606
+ CopyableValue,
607
+ {
608
+ value: recipient.full,
609
+ display: recipient.shortened,
610
+ onCopy,
611
+ theme
612
+ },
613
+ recipient.full
614
+ )),
615
+ spent !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { children: [
616
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { style: styles3.spentRow, children: [
617
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.Text, { style: [styles3.small, { color: theme.foreground }], children: [
618
+ "Spent",
619
+ " ",
620
+ (0, import_connect_appkit_core2.formatAuthorizationAmount)(spent, assets[grant.asset]).formatted
621
+ ] }),
622
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.Text, { style: [styles3.small, { color: theme.foreground }], children: [
623
+ "Total ",
624
+ grant.total.formatted
625
+ ] })
626
+ ] }),
627
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
628
+ import_react_native4.View,
629
+ {
630
+ accessibilityRole: "progressbar",
631
+ accessibilityLabel: `${grant.assetLabel} spent`,
632
+ accessibilityValue: { min: 0, max: 100, now: percent },
633
+ style: [
634
+ styles3.track,
635
+ { backgroundColor: theme.background }
636
+ ],
637
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
638
+ import_react_native4.View,
639
+ {
640
+ style: [
641
+ styles3.fill,
642
+ {
643
+ width: `${percent}%`,
644
+ backgroundColor: theme.primary
645
+ }
646
+ ]
647
+ }
648
+ )
649
+ }
650
+ )
651
+ ] })
652
+ ]
653
+ },
654
+ grant.asset
655
+ );
656
+ }),
657
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.Text, { style: [styles3.small, muted], children: [
658
+ entry.status === "expired" ? "Expired" : entry.status === "revoked" ? "Revoked" : `Expires ${view.expiry.relative}`,
659
+ " ",
660
+ "\xB7 ",
661
+ view.expiry.absolute
662
+ ] }),
663
+ view.warnings.map((warning) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
664
+ Callout,
665
+ {
666
+ tone: "warning",
667
+ glyph: "\u26A0",
668
+ role: "alert",
669
+ theme,
670
+ children: warning
671
+ },
672
+ warning
673
+ )),
674
+ 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." }),
675
+ 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: [
676
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
677
+ ActionButton,
678
+ {
679
+ label: "Cancel",
680
+ variant: "secondary",
681
+ onPress: () => setConfirming(null),
682
+ theme
683
+ }
684
+ ),
685
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
686
+ ActionButton,
687
+ {
688
+ label: busy ? "Revoking\u2026" : "Confirm revoke",
689
+ disabled: busy,
690
+ busy,
691
+ onPress: () => revoke(entry),
692
+ theme
693
+ }
694
+ )
695
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
696
+ ActionButton,
697
+ {
698
+ label: "Revoke",
699
+ variant: "secondary",
700
+ onPress: () => setConfirming(entry.keyId),
701
+ theme
702
+ }
703
+ ) })
704
+ ]
705
+ }
706
+ );
707
+ };
708
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
709
+ import_react_native4.FlatList,
710
+ {
711
+ data: entries,
712
+ keyExtractor: (entry) => entry.keyId,
713
+ renderItem,
714
+ extraData: [confirming, onChain, revokingId, theme],
715
+ contentContainerStyle: styles3.list,
716
+ style: { backgroundColor: theme.background },
717
+ ListHeaderComponent: entries.length ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
718
+ import_react_native4.Text,
719
+ {
720
+ accessibilityRole: "header",
721
+ style: [styles.heading, { color: theme.foreground }],
722
+ children: "Payment authorizations"
723
+ }
724
+ ) : void 0,
725
+ ListEmptyComponent: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native4.View, { style: [styles3.empty, { borderColor: theme.border }], children: [
726
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
727
+ import_react_native4.Text,
728
+ {
729
+ accessibilityRole: "header",
730
+ style: [styles3.title, { color: theme.foreground }],
731
+ children: "No payment authorizations"
732
+ }
733
+ ),
734
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native4.Text, { style: [styles3.body, muted], children: "Approvals you create will appear here." })
735
+ ] })
736
+ }
737
+ );
738
+ }
739
+ var styles3 = import_react_native4.StyleSheet.create({
740
+ list: { padding: 16, gap: 12 },
741
+ card: { borderWidth: 1, borderRadius: 12, padding: 16, gap: 10 },
742
+ cardHeader: {
743
+ flexDirection: "row",
744
+ justifyContent: "space-between",
745
+ alignItems: "flex-start",
746
+ gap: 8
747
+ },
748
+ flexShrink: { flexShrink: 1 },
749
+ title: { fontSize: 16, fontWeight: "600" },
750
+ small: { fontSize: 12 },
751
+ body: { fontSize: 14, textAlign: "center" },
752
+ strong: { fontSize: 14, fontWeight: "500" },
753
+ badge: { flexDirection: "row", alignItems: "center", gap: 4 },
754
+ badgeText: { fontSize: 12, fontWeight: "600", textTransform: "capitalize" },
755
+ grant: { borderRadius: 10, padding: 12, gap: 6 },
756
+ spentRow: {
757
+ flexDirection: "row",
758
+ justifyContent: "space-between",
759
+ gap: 8
760
+ },
761
+ track: { height: 8, borderRadius: 4, overflow: "hidden", marginTop: 4 },
762
+ fill: { height: "100%" },
763
+ actions: {
764
+ flexDirection: "row",
765
+ justifyContent: "flex-end",
766
+ gap: 8,
767
+ borderTopWidth: 1,
768
+ paddingTop: 12
769
+ },
770
+ empty: {
771
+ borderWidth: 1,
772
+ borderStyle: "dashed",
773
+ borderRadius: 12,
774
+ padding: 32,
775
+ alignItems: "center",
776
+ gap: 4
777
+ }
778
+ });
779
+
780
+ // src/ui/PaymentReceiptNative.tsx
781
+ var import_connect_appkit_core3 = require("@naculus/connect-appkit-core");
782
+ var import_react_native5 = require("react-native");
783
+ var import_jsx_runtime5 = require("react/jsx-runtime");
784
+ var VERIFICATION = {
785
+ unverified: {
786
+ title: "Unverified",
787
+ detail: "Reported by the server, not yet checked.",
788
+ glyph: "?",
789
+ tone: "neutral"
790
+ },
791
+ pending: {
792
+ title: "Checking",
793
+ detail: "Checking on chain.",
794
+ glyph: "\u2026",
795
+ tone: "neutral"
796
+ },
797
+ verified: {
798
+ title: "Verified",
799
+ detail: "Verified on chain.",
800
+ glyph: "\u2713",
801
+ tone: "success"
802
+ },
803
+ mismatch: {
804
+ title: "Mismatch",
805
+ detail: "The on-chain settlement does not match this payment.",
806
+ glyph: "!",
807
+ tone: "danger"
808
+ },
809
+ failed: {
810
+ title: "Verification failed",
811
+ detail: "The settlement could not be verified.",
812
+ glyph: "!",
813
+ tone: "danger"
814
+ },
815
+ unavailable: {
816
+ title: "Verification unavailable",
817
+ detail: "On-chain checking is unavailable. Retry later.",
818
+ glyph: "?",
819
+ tone: "neutral"
820
+ }
821
+ };
822
+ function PaymentReceiptNative({
823
+ record,
824
+ assets = {},
825
+ onOpenExplorer,
826
+ onCopy,
827
+ theme: themeProp
828
+ }) {
829
+ const theme = useNativePaymentTheme(themeProp);
830
+ const status = record.verification.status;
831
+ const state = VERIFICATION[status];
832
+ const colors = toneColors(theme, state.tone);
833
+ const muted = { color: theme.mutedForeground };
834
+ const amount = record.amount === null ? "Amount not reported" : /^\d+$/.test(record.amount) ? (0, import_connect_appkit_core3.formatAuthorizationAmount)(
835
+ BigInt(record.amount),
836
+ record.asset ? assets[record.asset] : void 0
837
+ ).formatted : `${record.amount} base units`;
838
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
839
+ import_react_native5.View,
840
+ {
841
+ testID: `verification-${status}`,
842
+ style: [
843
+ styles4.card,
844
+ { backgroundColor: theme.card, borderColor: theme.border }
845
+ ],
846
+ children: [
847
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { style: styles4.header, children: [
848
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.eyebrow, muted], children: "PAYMENT RECEIPT" }),
849
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
850
+ import_react_native5.Text,
851
+ {
852
+ accessibilityRole: "header",
853
+ style: [styles4.amount, { color: theme.foreground }],
854
+ children: amount
855
+ }
856
+ ),
857
+ /* @__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" })
858
+ ] }),
859
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
860
+ import_react_native5.View,
861
+ {
862
+ testID: "verification-panel",
863
+ accessibilityLiveRegion: "polite",
864
+ accessible: true,
865
+ accessibilityLabel: `${state.title}. ${state.detail}${record.verification.reason ? ` Reason: ${record.verification.reason}` : ""}`,
866
+ style: [
867
+ styles4.panel,
868
+ {
869
+ backgroundColor: colors.backgroundColor,
870
+ borderColor: colors.borderColor
871
+ }
872
+ ],
873
+ children: [
874
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Glyph, { color: colors.color, children: state.glyph }),
875
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { style: styles4.flexShrink, children: [
876
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.badge, { color: colors.color }], children: state.title }),
877
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.body, { color: colors.color }], children: state.detail }),
878
+ record.verification.reason && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
879
+ import_react_native5.Text,
880
+ {
881
+ numberOfLines: 2,
882
+ style: [styles4.body, styles4.strong, { color: colors.color }],
883
+ children: [
884
+ "Reason: ",
885
+ record.verification.reason
886
+ ]
887
+ }
888
+ )
889
+ ] })
890
+ ]
891
+ }
892
+ ),
893
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { style: styles4.field, children: [
894
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.small, muted], children: "Recipient" }),
895
+ 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" })
896
+ ] }),
897
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { style: styles4.row, children: [
898
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { style: styles4.cell, children: [
899
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.small, muted], children: "Protocol" }),
900
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.body, { color: theme.foreground }], children: record.protocol === "mpp" ? "MPP" : record.protocol })
901
+ ] }),
902
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { style: styles4.cell, children: [
903
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.small, muted], children: "Time" }),
904
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.body, { color: theme.foreground }], children: "Not recorded" })
905
+ ] })
906
+ ] }),
907
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { style: styles4.field, children: [
908
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.small, muted], children: "Settlement reference" }),
909
+ record.reference ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
910
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
911
+ CopyableValue,
912
+ {
913
+ value: record.reference,
914
+ onCopy,
915
+ theme
916
+ }
917
+ ),
918
+ onOpenExplorer && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
919
+ import_react_native5.Pressable,
920
+ {
921
+ accessibilityRole: "link",
922
+ accessibilityLabel: "View on explorer",
923
+ onPress: () => onOpenExplorer(record),
924
+ style: styles4.link,
925
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.linkText, { color: theme.foreground }], children: "View on explorer \u2197" })
926
+ }
927
+ )
928
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.Text, { style: [styles4.body, { color: theme.foreground }], children: "Not reported" })
929
+ ] })
930
+ ]
931
+ }
932
+ );
933
+ }
934
+ var styles4 = import_react_native5.StyleSheet.create({
935
+ card: { borderWidth: 1, borderRadius: 16, padding: 20, gap: 16 },
936
+ header: { alignItems: "center", gap: 4 },
937
+ eyebrow: { fontSize: 12, fontWeight: "500", letterSpacing: 0.6 },
938
+ amount: {
939
+ fontSize: 32,
940
+ fontWeight: "600",
941
+ fontVariant: ["tabular-nums"],
942
+ textAlign: "center"
943
+ },
944
+ small: { fontSize: 12 },
945
+ body: { fontSize: 14, lineHeight: 20 },
946
+ strong: { fontWeight: "600" },
947
+ // Fixed height: badge, explanation and a two-line reason fit, so the card
948
+ // does not jump as verification moves from pending to its final state.
949
+ panel: {
950
+ height: 128,
951
+ flexDirection: "row",
952
+ gap: 8,
953
+ borderWidth: 1,
954
+ borderRadius: 10,
955
+ padding: 12,
956
+ overflow: "hidden"
957
+ },
958
+ flexShrink: { flexShrink: 1 },
959
+ badge: { fontSize: 13, fontWeight: "700", marginBottom: 2 },
960
+ field: { gap: 4 },
961
+ row: { flexDirection: "row", gap: 12 },
962
+ cell: { flex: 1, gap: 4 },
963
+ link: { minHeight: 44, minWidth: 44, justifyContent: "center" },
964
+ linkText: {
965
+ fontSize: 13,
966
+ fontWeight: "500",
967
+ textDecorationLine: "underline"
968
+ }
969
+ });
970
+ // Annotate the CommonJS export names for ESM import in node:
971
+ 0 && (module.exports = {
972
+ AuthorizationConsentNative,
973
+ AuthorizationListNative,
974
+ NativePaymentThemeProvider,
975
+ PaymentReceiptNative,
976
+ darkPaymentTheme,
977
+ lightPaymentTheme,
978
+ useNativePaymentTheme
979
+ });
980
+ //# sourceMappingURL=index.cjs.map