@buoy-gg/notifications 7.0.41 → 7.0.42

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.
Files changed (71) hide show
  1. package/lib/commonjs/components/LocalNotificationTest.js +1 -185
  2. package/lib/commonjs/components/NotificationTestView.js +1 -329
  3. package/lib/commonjs/components/NotificationToolView.js +1 -673
  4. package/lib/commonjs/components/notificationStyles.js +1 -292
  5. package/lib/commonjs/expo.js +1 -247
  6. package/lib/commonjs/index.js +1 -148
  7. package/lib/commonjs/json.js +1 -58
  8. package/lib/commonjs/payload.js +1 -68
  9. package/lib/commonjs/preset.js +1 -56
  10. package/lib/commonjs/preview.js +1 -18
  11. package/lib/commonjs/recorder.js +1 -339
  12. package/lib/commonjs/runtime.js +1 -110
  13. package/lib/commonjs/storage.js +1 -61
  14. package/lib/commonjs/types.js +0 -1
  15. package/lib/module/components/LocalNotificationTest.js +1 -180
  16. package/lib/module/components/NotificationTestView.js +1 -324
  17. package/lib/module/components/NotificationToolView.js +1 -668
  18. package/lib/module/components/notificationStyles.js +1 -288
  19. package/lib/module/expo.js +1 -241
  20. package/lib/module/index.js +1 -13
  21. package/lib/module/json.js +1 -51
  22. package/lib/module/payload.js +1 -63
  23. package/lib/module/preset.js +1 -50
  24. package/lib/module/preview.js +1 -14
  25. package/lib/module/recorder.js +1 -332
  26. package/lib/module/runtime.js +1 -103
  27. package/lib/module/storage.js +1 -56
  28. package/lib/module/types.js +0 -1
  29. package/package.json +2 -2
  30. package/lib/commonjs/components/LocalNotificationTest.js.map +0 -1
  31. package/lib/commonjs/components/NotificationTestView.js.map +0 -1
  32. package/lib/commonjs/components/NotificationToolView.js.map +0 -1
  33. package/lib/commonjs/components/notificationStyles.js.map +0 -1
  34. package/lib/commonjs/expo.js.map +0 -1
  35. package/lib/commonjs/index.js.map +0 -1
  36. package/lib/commonjs/json.js.map +0 -1
  37. package/lib/commonjs/payload.js.map +0 -1
  38. package/lib/commonjs/preset.js.map +0 -1
  39. package/lib/commonjs/preview.js.map +0 -1
  40. package/lib/commonjs/recorder.js.map +0 -1
  41. package/lib/commonjs/runtime.js.map +0 -1
  42. package/lib/commonjs/storage.js.map +0 -1
  43. package/lib/commonjs/types.js.map +0 -1
  44. package/lib/module/components/LocalNotificationTest.js.map +0 -1
  45. package/lib/module/components/NotificationTestView.js.map +0 -1
  46. package/lib/module/components/NotificationToolView.js.map +0 -1
  47. package/lib/module/components/notificationStyles.js.map +0 -1
  48. package/lib/module/expo.js.map +0 -1
  49. package/lib/module/index.js.map +0 -1
  50. package/lib/module/json.js.map +0 -1
  51. package/lib/module/payload.js.map +0 -1
  52. package/lib/module/preset.js.map +0 -1
  53. package/lib/module/preview.js.map +0 -1
  54. package/lib/module/recorder.js.map +0 -1
  55. package/lib/module/runtime.js.map +0 -1
  56. package/lib/module/storage.js.map +0 -1
  57. package/lib/module/types.js.map +0 -1
  58. package/lib/typescript/components/LocalNotificationTest.d.ts.map +0 -1
  59. package/lib/typescript/components/NotificationTestView.d.ts.map +0 -1
  60. package/lib/typescript/components/NotificationToolView.d.ts.map +0 -1
  61. package/lib/typescript/components/notificationStyles.d.ts.map +0 -1
  62. package/lib/typescript/expo.d.ts.map +0 -1
  63. package/lib/typescript/index.d.ts.map +0 -1
  64. package/lib/typescript/json.d.ts.map +0 -1
  65. package/lib/typescript/payload.d.ts.map +0 -1
  66. package/lib/typescript/preset.d.ts.map +0 -1
  67. package/lib/typescript/preview.d.ts.map +0 -1
  68. package/lib/typescript/recorder.d.ts.map +0 -1
  69. package/lib/typescript/runtime.d.ts.map +0 -1
  70. package/lib/typescript/storage.d.ts.map +0 -1
  71. package/lib/typescript/types.d.ts.map +0 -1
@@ -1,668 +1 @@
1
- "use strict";
2
-
3
- import React, { useEffect, useMemo, useState } from "react";
4
- import { FlatList, Pressable, ScrollView, Text, TextInput, View } from "react-native";
5
- import { ChevronDown, ChevronLeft, ChevronRight, Copy, ModalHeader, NightBadge, NightCard, NightRows, NightSectionLabel, NightSegmented, NotificationsIcon, PowerToggleButton, Search, Settings, ToolEmptyState, Trash2, X, copyToClipboard, night, useSafeAreaInsets } from "@buoy-gg/shared-ui";
6
- import { DataViewer } from "@buoy-gg/shared-ui/dataViewer";
7
- import { LocalNotificationTest } from "./LocalNotificationTest";
8
- import { styles } from "./notificationStyles";
9
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
10
- const labels = {
11
- received: "App received",
12
- response: "Notification response",
13
- "startup-response": "Response recovered at startup",
14
- "presentation-decision": "Presentation decision",
15
- "presentation-error": "Presentation error",
16
- "background-started": "Background task started",
17
- "background-completed": "Background task completed",
18
- "background-error": "Background task failed",
19
- "app-outcome": "App outcome",
20
- "local-scheduled": "Local notification scheduled"
21
- };
22
- function eventTone(kind) {
23
- return kind.includes("error") ? "danger" : kind === "startup-response" ? "warning" : kind === "received" || kind === "app-outcome" ? "accent" : "neutral";
24
- }
25
- function Action({
26
- label,
27
- onPress,
28
- disabled,
29
- primary,
30
- icon
31
- }) {
32
- return /*#__PURE__*/_jsxs(Pressable, {
33
- accessibilityRole: "button",
34
- accessibilityLabel: label,
35
- accessibilityState: {
36
- disabled: !!disabled
37
- },
38
- disabled: disabled,
39
- onPress: onPress,
40
- style: ({
41
- pressed
42
- }) => [styles.button, primary && styles.primaryButton, pressed && styles.pressed, disabled && styles.disabled],
43
- children: [icon, /*#__PURE__*/_jsx(Text, {
44
- style: [styles.buttonText, primary && styles.primaryButtonText],
45
- children: label
46
- })]
47
- });
48
- }
49
- function Field({
50
- label,
51
- value
52
- }) {
53
- return /*#__PURE__*/_jsxs(View, {
54
- style: styles.field,
55
- children: [/*#__PURE__*/_jsx(Text, {
56
- style: [styles.caption, styles.fieldLabel],
57
- children: label
58
- }), /*#__PURE__*/_jsx(Text, {
59
- selectable: true,
60
- style: styles.value,
61
- children: value
62
- })]
63
- });
64
- }
65
- function JsonCard({
66
- title,
67
- value,
68
- initiallyOpen = true
69
- }) {
70
- const [open, setOpen] = useState(initiallyOpen);
71
- const serialized = JSON.stringify(value ?? null);
72
- // Device snapshots can recreate unchanged objects. Keep the data tree stable.
73
- const data = useMemo(() => JSON.parse(serialized), [serialized]);
74
- return /*#__PURE__*/_jsxs(NightCard, {
75
- glass: true,
76
- children: [/*#__PURE__*/_jsxs(Pressable, {
77
- accessibilityRole: "button",
78
- accessibilityLabel: title,
79
- accessibilityState: {
80
- expanded: open
81
- },
82
- onPress: () => setOpen(value => !value),
83
- style: styles.disclosure,
84
- children: [/*#__PURE__*/_jsx(Text, {
85
- style: styles.disclosureTitle,
86
- children: title
87
- }), open ? /*#__PURE__*/_jsx(ChevronDown, {
88
- size: 14,
89
- color: night.color.textSecondary
90
- }) : /*#__PURE__*/_jsx(ChevronRight, {
91
- size: 14,
92
- color: night.color.textSecondary
93
- })]
94
- }), open && /*#__PURE__*/_jsx(View, {
95
- style: styles.jsonBody,
96
- children: /*#__PURE__*/_jsx(DataViewer, {
97
- title: "",
98
- data: data,
99
- rawMode: true,
100
- initialExpanded: true,
101
- showTypeFilter: false
102
- })
103
- })]
104
- });
105
- }
106
- export function NotificationToolView({
107
- snapshot,
108
- onAction,
109
- sender,
110
- activeTab,
111
- onTabChange,
112
- renderShell
113
- }) {
114
- const [localTab, setLocalTab] = useState("send");
115
- const tab = activeTab ?? localTab;
116
- const [filter, setFilter] = useState("");
117
- const [selectedId, setSelectedId] = useState(null);
118
- const [busy, setBusy] = useState(false);
119
- const [message, setMessage] = useState(null);
120
- const [, setClock] = useState(0);
121
- const insets = useSafeAreaInsets({
122
- minBottom: 16
123
- });
124
- const expiresAt = snapshot?.session?.expiresAt;
125
- useEffect(() => {
126
- if (!expiresAt || expiresAt <= Date.now()) return;
127
- const timeout = setTimeout(() => setClock(value => value + 1), expiresAt - Date.now() + 1);
128
- return () => clearTimeout(timeout);
129
- }, [expiresAt]);
130
- const recording = !!expiresAt && expiresAt > Date.now();
131
- const events = useMemo(() => (snapshot?.events ?? []).filter(event => JSON.stringify(event).toLowerCase().includes(filter.trim().toLowerCase())).slice().reverse(), [snapshot?.events, filter]);
132
- const selected = snapshot?.events.find(event => event.id === selectedId);
133
- const count = snapshot?.events.length ?? 0;
134
- const connected = !!snapshot?.provider;
135
- function changeTab(next) {
136
- setLocalTab(next);
137
- onTabChange?.(next);
138
- setMessage(null);
139
- }
140
- async function act(action, params) {
141
- if (busy) return false;
142
- setBusy(true);
143
- setMessage(null);
144
- try {
145
- await onAction(action, params);
146
- return true;
147
- } catch (error) {
148
- setMessage({
149
- text: error instanceof Error ? error.message : String(error),
150
- error: true
151
- });
152
- return false;
153
- } finally {
154
- setBusy(false);
155
- }
156
- }
157
- const arm = () => {
158
- void act("setCaptureSession", {
159
- runId: `capture-${Date.now()}`,
160
- durationMs: 1800000
161
- });
162
- };
163
- const stop = () => {
164
- void act("setCaptureSession", {
165
- runId: null
166
- });
167
- };
168
- async function copy(value) {
169
- const copied = await copyToClipboard(value);
170
- setMessage({
171
- text: copied ? "Copied to clipboard." : "Clipboard is unavailable. Select the text to copy it.",
172
- error: !copied
173
- });
174
- }
175
- const tabs = [{
176
- key: "send",
177
- label: "Test"
178
- }, {
179
- key: "events",
180
- label: `Activity (${count})`
181
- }];
182
- const header = /*#__PURE__*/_jsxs(ModalHeader, {
183
- children: [/*#__PURE__*/_jsx(ModalHeader.Content, {
184
- title: "",
185
- noMargin: true,
186
- children: /*#__PURE__*/_jsx(NightSegmented, {
187
- tabs: tabs,
188
- activeKey: tab,
189
- onChange: changeTab,
190
- size: "sm",
191
- glass: true
192
- })
193
- }), /*#__PURE__*/_jsxs(ModalHeader.Actions, {
194
- children: [/*#__PURE__*/_jsx(Pressable, {
195
- accessibilityRole: "button",
196
- accessibilityLabel: "Notification settings",
197
- onPress: () => changeTab(tab === "setup" || tab === "tokens" ? "send" : "setup"),
198
- style: styles.iconButton,
199
- children: /*#__PURE__*/_jsx(Settings, {
200
- size: 16,
201
- color: tab === "setup" || tab === "tokens" ? night.color.accent : night.color.textSecondary
202
- })
203
- }), tab !== "send" && /*#__PURE__*/_jsx(PowerToggleButton, {
204
- isEnabled: recording,
205
- onToggle: recording ? stop : arm,
206
- disabled: !connected || busy,
207
- accessibilityLabel: recording ? "Stop notification capture" : "Start notification capture",
208
- testID: "notifications-capture"
209
- })]
210
- })]
211
- });
212
- const body = /*#__PURE__*/_jsxs(View, {
213
- style: styles.root,
214
- children: [connected && tab !== "send" && /*#__PURE__*/_jsxs(View, {
215
- style: styles.status,
216
- children: [/*#__PURE__*/_jsxs(View, {
217
- style: styles.statusLabel,
218
- children: [/*#__PURE__*/_jsx(View, {
219
- style: [styles.dot, recording && styles.liveDot]
220
- }), /*#__PURE__*/_jsx(Text, {
221
- style: styles.caption,
222
- children: recording ? "Recording" : "Capture stopped"
223
- })]
224
- }), tab !== "events" && /*#__PURE__*/_jsx(Text, {
225
- style: styles.caption,
226
- children: snapshot.durable ? "Persistent history" : "Memory only"
227
- }), tab !== "events" && /*#__PURE__*/_jsxs(Text, {
228
- style: styles.provider,
229
- children: [snapshot.provider, " \xB7 ", snapshot.platform]
230
- })]
231
- }), snapshot?.storageError && /*#__PURE__*/_jsxs(Text, {
232
- accessibilityRole: "alert",
233
- style: styles.errorNotice,
234
- children: ["Storage: ", snapshot.storageError]
235
- }), message && /*#__PURE__*/_jsx(Text, {
236
- accessibilityRole: message.error ? "alert" : "text",
237
- style: [styles.notice, message.error && styles.errorText],
238
- children: message.text
239
- }), !sender && /*#__PURE__*/_jsx(View, {
240
- style: tab === "send" ? styles.fill : {
241
- display: "none"
242
- },
243
- children: /*#__PURE__*/_jsx(LocalNotificationTest, {
244
- snapshot: snapshot,
245
- onAction: onAction,
246
- onViewActivity: () => changeTab("events")
247
- })
248
- }), tab === "send" ? sender ?? null : !connected ? /*#__PURE__*/_jsxs(ScrollView, {
249
- children: [/*#__PURE__*/_jsx(ToolEmptyState, {
250
- otto: "paused",
251
- title: "Connect notification capture",
252
- message: "Install @buoy-gg/notifications and call installExpoNotificationCapture in the app entry point. Use an app-specific development build for remote notifications."
253
- }), /*#__PURE__*/_jsx(Text, {
254
- style: styles.emptyFootnote,
255
- children: "The desktop sender works with any installed app. Inspection needs the capture integration."
256
- })]
257
- }) : /*#__PURE__*/_jsxs(_Fragment, {
258
- children: [tab === "events" && /*#__PURE__*/_jsxs(_Fragment, {
259
- children: [/*#__PURE__*/_jsxs(View, {
260
- style: styles.searchRow,
261
- children: [/*#__PURE__*/_jsxs(View, {
262
- style: styles.searchField,
263
- children: [/*#__PURE__*/_jsx(Search, {
264
- size: 14,
265
- color: night.color.textSecondary
266
- }), /*#__PURE__*/_jsx(TextInput, {
267
- accessibilityLabel: "Filter notification events",
268
- placeholder: "Search notifications\u2026",
269
- placeholderTextColor: night.color.placeholder,
270
- value: filter,
271
- onChangeText: value => {
272
- setFilter(value);
273
- setSelectedId(null);
274
- },
275
- autoCapitalize: "none",
276
- autoCorrect: false,
277
- style: styles.searchInput
278
- }), !!filter && /*#__PURE__*/_jsx(Pressable, {
279
- accessibilityRole: "button",
280
- accessibilityLabel: "Clear notification search",
281
- onPress: () => setFilter(""),
282
- style: styles.clearSearch,
283
- children: /*#__PURE__*/_jsx(X, {
284
- size: 12,
285
- color: night.color.textSecondary
286
- })
287
- })]
288
- }), /*#__PURE__*/_jsx(Pressable, {
289
- accessibilityRole: "button",
290
- accessibilityLabel: "Clear captured events",
291
- disabled: busy || !count,
292
- accessibilityState: {
293
- disabled: busy || !count
294
- },
295
- style: [styles.iconButton, (busy || !count) && styles.disabled],
296
- onPress: () => {
297
- void act("clearCapturedEvents").then(cleared => {
298
- if (cleared) setSelectedId(null);
299
- });
300
- },
301
- children: /*#__PURE__*/_jsx(Trash2, {
302
- size: 14,
303
- color: night.color.textSecondary
304
- })
305
- })]
306
- }), selected ? /*#__PURE__*/_jsxs(ScrollView, {
307
- style: styles.fill,
308
- contentContainerStyle: [styles.content, {
309
- paddingBottom: insets.bottom
310
- }],
311
- keyboardShouldPersistTaps: "handled",
312
- children: [/*#__PURE__*/_jsxs(View, {
313
- style: styles.actionRow,
314
- children: [/*#__PURE__*/_jsx(Action, {
315
- label: "Back to events",
316
- onPress: () => setSelectedId(null),
317
- icon: /*#__PURE__*/_jsx(ChevronLeft, {
318
- size: 14,
319
- color: night.color.accent
320
- })
321
- }), /*#__PURE__*/_jsx(Action, {
322
- label: "Copy event JSON",
323
- onPress: () => {
324
- void copy(selected);
325
- },
326
- icon: /*#__PURE__*/_jsx(Copy, {
327
- size: 14,
328
- color: night.color.accent
329
- })
330
- })]
331
- }), /*#__PURE__*/_jsxs(NightCard, {
332
- glass: true,
333
- padded: true,
334
- children: [/*#__PURE__*/_jsx(NightBadge, {
335
- label: labels[selected.kind] ?? selected.kind,
336
- tone: eventTone(selected.kind)
337
- }), /*#__PURE__*/_jsx(Text, {
338
- style: styles.detailTitle,
339
- children: selected.title ?? "Notification event"
340
- }), /*#__PURE__*/_jsx(Text, {
341
- style: styles.caption,
342
- children: new Date(selected.capturedAt).toLocaleString()
343
- })]
344
- }), /*#__PURE__*/_jsxs(View, {
345
- children: [/*#__PURE__*/_jsx(NightSectionLabel, {
346
- children: "Observation"
347
- }), /*#__PURE__*/_jsx(NightCard, {
348
- glass: true,
349
- children: /*#__PURE__*/_jsxs(NightRows, {
350
- children: [/*#__PURE__*/_jsx(Field, {
351
- label: "App state at callback",
352
- value: selected.observedAppState
353
- }), selected.testId && /*#__PURE__*/_jsx(Field, {
354
- label: "Test ID",
355
- value: selected.testId
356
- }), selected.notificationId && /*#__PURE__*/_jsx(Field, {
357
- label: "Notification ID",
358
- value: selected.notificationId
359
- }), selected.actionIdentifier && /*#__PURE__*/_jsx(Field, {
360
- label: "Action",
361
- value: selected.actionIdentifier
362
- })]
363
- })
364
- })]
365
- }), selected.kind === "startup-response" && /*#__PURE__*/_jsx(Text, {
366
- style: styles.footnote,
367
- children: "Recovered from the SDK cache. This alone does not establish why the app launched."
368
- }), selected.truncated && /*#__PURE__*/_jsx(Text, {
369
- style: styles.warning,
370
- children: "This payload was truncated when captured."
371
- }), /*#__PURE__*/_jsx(JsonCard, {
372
- title: "Payload",
373
- value: selected.raw
374
- }), /*#__PURE__*/_jsx(JsonCard, {
375
- title: "Full event",
376
- value: selected,
377
- initiallyOpen: false
378
- })]
379
- }) : /*#__PURE__*/_jsx(FlatList, {
380
- data: events,
381
- keyExtractor: event => event.id,
382
- style: styles.fill,
383
- contentContainerStyle: [styles.eventList, {
384
- paddingBottom: insets.bottom
385
- }],
386
- keyboardShouldPersistTaps: "handled",
387
- initialNumToRender: 15,
388
- ListHeaderComponent: filter ? /*#__PURE__*/_jsxs(Text, {
389
- style: styles.resultCount,
390
- children: [events.length, " of ", count, " events"]
391
- }) : null,
392
- ListFooterComponent: snapshot.evicted > 0 ? /*#__PURE__*/_jsxs(Text, {
393
- style: styles.footnote,
394
- children: [snapshot.evicted, " older records removed by the history limit."]
395
- }) : null,
396
- ListEmptyComponent: /*#__PURE__*/_jsx(ToolEmptyState, {
397
- otto: filter ? "sad" : recording ? "listening" : "paused",
398
- title: filter ? "No matching events" : recording ? "Waiting for notifications" : "Capture is stopped",
399
- message: filter ? "Try another title, test ID, or payload value." : recording ? "Send a notification from Buoy Desktop or your app. Background alerts can reach the system tray before an app callback runs." : "Start capture with the power button, then send a notification."
400
- }),
401
- renderItem: ({
402
- item: event
403
- }) => /*#__PURE__*/_jsx(EventRow, {
404
- event: event,
405
- onPress: () => setSelectedId(event.id)
406
- })
407
- })]
408
- }), tab === "tokens" && /*#__PURE__*/_jsxs(ScrollView, {
409
- style: styles.fill,
410
- contentContainerStyle: [styles.content, {
411
- paddingBottom: insets.bottom
412
- }],
413
- keyboardShouldPersistTaps: "handled",
414
- children: [/*#__PURE__*/_jsxs(View, {
415
- children: [/*#__PURE__*/_jsx(Action, {
416
- label: "Back to settings",
417
- onPress: () => changeTab("setup")
418
- }), /*#__PURE__*/_jsx(NightSectionLabel, {
419
- children: "Registration"
420
- }), /*#__PURE__*/_jsxs(NightCard, {
421
- glass: true,
422
- padded: true,
423
- children: [/*#__PURE__*/_jsx(Text, {
424
- style: styles.bodyText,
425
- children: "Register a token to test delivery through your push provider."
426
- }), /*#__PURE__*/_jsxs(View, {
427
- style: styles.actionRow,
428
- children: [/*#__PURE__*/_jsx(Action, {
429
- label: "Register device token",
430
- onPress: () => {
431
- void act("refreshToken", {
432
- type: "device"
433
- });
434
- },
435
- disabled: busy,
436
- primary: true
437
- }), /*#__PURE__*/_jsx(Action, {
438
- label: "Register Expo token",
439
- onPress: () => {
440
- void act("refreshToken", {
441
- type: "expo"
442
- });
443
- },
444
- disabled: busy || !snapshot.capabilities.includes("expo-token")
445
- })]
446
- })]
447
- }), /*#__PURE__*/_jsx(Text, {
448
- style: styles.footnote,
449
- children: "Registration runs only when you or your app request it. Simulator sends work without a token."
450
- })]
451
- }), snapshot.tokens.length === 0 ? /*#__PURE__*/_jsx(ToolEmptyState, {
452
- otto: "listening",
453
- title: "No tokens observed",
454
- message: "Register a device token above, or let your app register one."
455
- }) : snapshot.tokens.map((token, i) => /*#__PURE__*/_jsxs(View, {
456
- children: [/*#__PURE__*/_jsx(NightSectionLabel, {
457
- children: token.type.toUpperCase() + " token"
458
- }), /*#__PURE__*/_jsxs(NightCard, {
459
- glass: true,
460
- padded: true,
461
- children: [/*#__PURE__*/_jsxs(View, {
462
- style: styles.actionRow,
463
- children: [/*#__PURE__*/_jsx(NightBadge, {
464
- label: token.type,
465
- tone: "accent"
466
- }), token.environment && /*#__PURE__*/_jsx(Text, {
467
- style: styles.caption,
468
- children: token.environment
469
- })]
470
- }), /*#__PURE__*/_jsx(Text, {
471
- selectable: true,
472
- style: styles.token,
473
- children: token.value
474
- }), token.projectId && /*#__PURE__*/_jsxs(Text, {
475
- selectable: true,
476
- style: styles.caption,
477
- children: ["Project: ", token.projectId]
478
- }), /*#__PURE__*/_jsxs(Text, {
479
- style: styles.caption,
480
- children: ["Observed ", new Date(token.observedAt).toLocaleString()]
481
- }), /*#__PURE__*/_jsx(View, {
482
- style: styles.actionRow,
483
- children: /*#__PURE__*/_jsx(Action, {
484
- label: "Copy " + token.type.toUpperCase() + " token",
485
- onPress: () => {
486
- void copy(token.value);
487
- },
488
- icon: /*#__PURE__*/_jsx(Copy, {
489
- size: 14,
490
- color: night.color.accent
491
- })
492
- })
493
- })]
494
- })]
495
- }, token.type + "-" + i))]
496
- }), tab === "setup" && /*#__PURE__*/_jsxs(ScrollView, {
497
- style: styles.fill,
498
- contentContainerStyle: [styles.content, {
499
- paddingBottom: insets.bottom
500
- }],
501
- keyboardShouldPersistTaps: "handled",
502
- children: [/*#__PURE__*/_jsxs(View, {
503
- children: [/*#__PURE__*/_jsx(Action, {
504
- label: "Device tokens",
505
- onPress: () => changeTab("tokens")
506
- }), /*#__PURE__*/_jsx(NightSectionLabel, {
507
- children: "Capture session"
508
- }), /*#__PURE__*/_jsxs(NightCard, {
509
- glass: true,
510
- padded: true,
511
- children: [/*#__PURE__*/_jsxs(View, {
512
- style: styles.actionRow,
513
- children: [/*#__PURE__*/_jsx(NightBadge, {
514
- label: recording ? "Recording" : "Stopped",
515
- tone: recording ? "accent" : "neutral"
516
- }), /*#__PURE__*/_jsx(Text, {
517
- style: styles.caption,
518
- children: recording ? "Until " + new Date(expiresAt).toLocaleTimeString() : "Ready when you are"
519
- })]
520
- }), /*#__PURE__*/_jsxs(View, {
521
- style: styles.actionRow,
522
- children: [/*#__PURE__*/_jsx(Action, {
523
- label: "Arm capture for 30 minutes",
524
- onPress: arm,
525
- disabled: busy,
526
- primary: true
527
- }), /*#__PURE__*/_jsx(Action, {
528
- label: "Stop capture",
529
- onPress: stop,
530
- disabled: busy || !recording
531
- })]
532
- })]
533
- }), /*#__PURE__*/_jsx(Text, {
534
- style: styles.footnote,
535
- children: snapshot.durable ? "History is saved on the app and survives app and desktop restarts." : "History is held in memory. Keep the app running to retain it."
536
- })]
537
- }), /*#__PURE__*/_jsxs(View, {
538
- children: [/*#__PURE__*/_jsx(NightSectionLabel, {
539
- children: "Connected app"
540
- }), /*#__PURE__*/_jsx(NightCard, {
541
- glass: true,
542
- children: /*#__PURE__*/_jsxs(NightRows, {
543
- children: [/*#__PURE__*/_jsx(Field, {
544
- label: "App",
545
- value: snapshot.appId ?? "Not supplied"
546
- }), /*#__PURE__*/_jsx(Field, {
547
- label: "Provider",
548
- value: [snapshot.provider, snapshot.providerVersion].filter(Boolean).join(" ")
549
- }), /*#__PURE__*/_jsx(Field, {
550
- label: "Platform",
551
- value: snapshot.platform
552
- })]
553
- })
554
- })]
555
- }), /*#__PURE__*/_jsxs(View, {
556
- children: [/*#__PURE__*/_jsx(NightSectionLabel, {
557
- children: "Permissions"
558
- }), /*#__PURE__*/_jsx(View, {
559
- style: styles.sectionActions,
560
- children: /*#__PURE__*/_jsx(Action, {
561
- label: "Refresh permissions",
562
- disabled: busy,
563
- onPress: () => {
564
- void act("getPermissions");
565
- }
566
- })
567
- }), /*#__PURE__*/_jsx(JsonCard, {
568
- title: "Permission settings",
569
- value: snapshot.permissions
570
- })]
571
- }), /*#__PURE__*/_jsxs(View, {
572
- children: [/*#__PURE__*/_jsx(NightSectionLabel, {
573
- children: "System tray"
574
- }), /*#__PURE__*/_jsx(View, {
575
- style: styles.sectionActions,
576
- children: /*#__PURE__*/_jsx(Action, {
577
- label: "Inspect system tray",
578
- disabled: busy || !snapshot.capabilities.includes("presented"),
579
- onPress: () => {
580
- void act("listPresented");
581
- }
582
- })
583
- }), /*#__PURE__*/_jsx(JsonCard, {
584
- title: "Currently presented",
585
- value: snapshot.presented
586
- }), /*#__PURE__*/_jsx(Text, {
587
- style: styles.footnote,
588
- children: "Notifications currently in the system tray. Captured history is in Activity."
589
- })]
590
- }), /*#__PURE__*/_jsxs(View, {
591
- children: [/*#__PURE__*/_jsx(NightSectionLabel, {
592
- children: "Capabilities"
593
- }), /*#__PURE__*/_jsx(NightCard, {
594
- glass: true,
595
- padded: true,
596
- children: /*#__PURE__*/_jsx(View, {
597
- style: styles.actionRow,
598
- children: snapshot.capabilities.map(capability => /*#__PURE__*/_jsx(NightBadge, {
599
- label: capability
600
- }, capability))
601
- })
602
- })]
603
- })]
604
- })]
605
- })]
606
- });
607
- return /*#__PURE__*/_jsx(_Fragment, {
608
- children: renderShell ? renderShell({
609
- header,
610
- body
611
- }) : /*#__PURE__*/_jsxs(View, {
612
- style: styles.root,
613
- children: [/*#__PURE__*/_jsx(View, {
614
- style: styles.inlineHeader,
615
- children: header
616
- }), body]
617
- })
618
- });
619
- }
620
- function EventRow({
621
- event,
622
- onPress
623
- }) {
624
- const tone = eventTone(event.kind);
625
- const color = tone === "danger" ? night.color.danger : tone === "warning" ? night.color.warning : tone === "accent" ? night.color.accent : night.color.textSecondary;
626
- return /*#__PURE__*/_jsxs(Pressable, {
627
- accessibilityRole: "button",
628
- accessibilityLabel: (labels[event.kind] ?? event.kind) + ": " + (event.title ?? event.testId ?? event.notificationId ?? "notification"),
629
- onPress: onPress,
630
- style: ({
631
- pressed
632
- }) => [styles.event, pressed && styles.pressed],
633
- children: [/*#__PURE__*/_jsxs(View, {
634
- style: styles.eventTop,
635
- children: [/*#__PURE__*/_jsxs(View, {
636
- style: styles.eventKind,
637
- children: [/*#__PURE__*/_jsx(NotificationsIcon, {
638
- size: 13,
639
- color: color
640
- }), /*#__PURE__*/_jsx(Text, {
641
- numberOfLines: 1,
642
- style: [styles.eventLabel, {
643
- color
644
- }],
645
- children: labels[event.kind] ?? event.kind
646
- })]
647
- }), /*#__PURE__*/_jsx(Text, {
648
- style: styles.timestamp,
649
- children: new Date(event.capturedAt).toLocaleTimeString()
650
- })]
651
- }), /*#__PURE__*/_jsxs(View, {
652
- style: styles.eventSubject,
653
- children: [/*#__PURE__*/_jsx(Text, {
654
- numberOfLines: 1,
655
- style: styles.eventTitle,
656
- children: event.title ?? event.testId ?? event.notificationId ?? "Notification event"
657
- }), /*#__PURE__*/_jsx(ChevronRight, {
658
- size: 14,
659
- color: night.color.textTertiary
660
- })]
661
- }), /*#__PURE__*/_jsxs(Text, {
662
- numberOfLines: 1,
663
- style: styles.eventMeta,
664
- children: [event.observedAppState, event.truncated ? " · Truncated" : ""]
665
- })]
666
- });
667
- }
668
- //# sourceMappingURL=NotificationToolView.js.map
1
+ "use strict";import ke,{useEffect as le,useMemo as Y,useState as k}from"react";import{FlatList as se,Pressable as P,ScrollView as D,Text as l,TextInput as ae,View as r}from"react-native";import{ChevronDown as ce,ChevronLeft as de,ChevronRight as Z,Copy as ee,ModalHeader as V,NightBadge as z,NightCard as g,NightRows as te,NightSectionLabel as v,NightSegmented as ue,NotificationsIcon as pe,PowerToggleButton as ye,Search as he,Settings as be,ToolEmptyState as H,Trash2 as fe,X as me,copyToClipboard as ge,night as a,useSafeAreaInsets as ve}from"@buoy-gg/shared-ui";import{DataViewer as Se}from"@buoy-gg/shared-ui/dataViewer";import{LocalNotificationTest as we}from"./LocalNotificationTest";import{styles as t}from"./notificationStyles";import{jsx as e,jsxs as o,Fragment as J}from"react/jsx-runtime";const K={received:"App received",response:"Notification response","startup-response":"Response recovered at startup","presentation-decision":"Presentation decision","presentation-error":"Presentation error","background-started":"Background task started","background-completed":"Background task completed","background-error":"Background task failed","app-outcome":"App outcome","local-scheduled":"Local notification scheduled"};function ie(i){return i.includes("error")?"danger":i==="startup-response"?"warning":i==="received"||i==="app-outcome"?"accent":"neutral"}function h({label:i,onPress:b,disabled:d,primary:p,icon:S}){return o(P,{accessibilityRole:"button",accessibilityLabel:i,accessibilityState:{disabled:!!d},disabled:d,onPress:b,style:({pressed:w})=>[t.button,p&&t.primaryButton,w&&t.pressed,d&&t.disabled],children:[S,e(l,{style:[t.buttonText,p&&t.primaryButtonText],children:i})]})}function x({label:i,value:b}){return o(r,{style:t.field,children:[e(l,{style:[t.caption,t.fieldLabel],children:i}),e(l,{selectable:true,style:t.value,children:b})]})}function E({title:i,value:b,initiallyOpen:d=true}){const[p,S]=k(d);const w=JSON.stringify(b??null);const F=Y(()=>JSON.parse(w),[w]);return o(g,{glass:true,children:[o(P,{accessibilityRole:"button",accessibilityLabel:i,accessibilityState:{expanded:p},onPress:()=>S(O=>!O),style:t.disclosure,children:[e(l,{style:t.disclosureTitle,children:i}),p?e(ce,{size:14,color:a.color.textSecondary}):e(Z,{size:14,color:a.color.textSecondary})]}),p&&e(r,{style:t.jsonBody,children:e(Se,{title:"",data:F,rawMode:true,initialExpanded:true,showTypeFilter:false})})]})}export function NotificationToolView({snapshot:i,onAction:b,sender:d,activeTab:p,onTabChange:S,renderShell:w}){const[F,O]=k("send");const c=p??F;const[f,U]=k("");const[oe,I]=k(null);const[y,_]=k(false);const[N,B]=k(null);const[,ne]=k(0);const A=ve({minBottom:16});const C=i?.session?.expiresAt;le(()=>{if(!C||C<=Date.now())return;const n=setTimeout(()=>ne(m=>m+1),C-Date.now()+1);return()=>clearTimeout(n)},[C]);const u=!!C&&C>Date.now();const $=Y(()=>(i?.events??[]).filter(n=>JSON.stringify(n).toLowerCase().includes(f.trim().toLowerCase())).slice().reverse(),[i?.events,f]);const s=i?.events.find(n=>n.id===oe);const R=i?.events.length??0;const j=!!i?.provider;function L(n){O(n);S?.(n);B(null)}async function T(n,m){if(y)return false;_(true);B(null);try{await b(n,m);return true}catch(M){B({text:M instanceof Error?M.message:String(M),error:true});return false}finally{_(false)}}const q=()=>{void T("setCaptureSession",{runId:`capture-${Date.now()}`,durationMs:18e5})};const W=()=>{void T("setCaptureSession",{runId:null})};async function X(n){const m=await ge(n);B({text:m?"Copied to clipboard.":"Clipboard is unavailable. Select the text to copy it.",error:!m})}const re=[{key:"send",label:"Test"},{key:"events",label:`Activity (${R})`}];const G=o(V,{children:[e(V.Content,{title:"",noMargin:true,children:e(ue,{tabs:re,activeKey:c,onChange:L,size:"sm",glass:true})}),o(V.Actions,{children:[e(P,{accessibilityRole:"button",accessibilityLabel:"Notification settings",onPress:()=>L(c==="setup"||c==="tokens"?"send":"setup"),style:t.iconButton,children:e(be,{size:16,color:c==="setup"||c==="tokens"?a.color.accent:a.color.textSecondary})}),c!=="send"&&e(ye,{isEnabled:u,onToggle:u?W:q,disabled:!j||y,accessibilityLabel:u?"Stop notification capture":"Start notification capture",testID:"notifications-capture"})]})]});const Q=o(r,{style:t.root,children:[j&&c!=="send"&&o(r,{style:t.status,children:[o(r,{style:t.statusLabel,children:[e(r,{style:[t.dot,u&&t.liveDot]}),e(l,{style:t.caption,children:u?"Recording":"Capture stopped"})]}),c!=="events"&&e(l,{style:t.caption,children:i.durable?"Persistent history":"Memory only"}),c!=="events"&&o(l,{style:t.provider,children:[i.provider," \xB7 ",i.platform]})]}),i?.storageError&&o(l,{accessibilityRole:"alert",style:t.errorNotice,children:["Storage: ",i.storageError]}),N&&e(l,{accessibilityRole:N.error?"alert":"text",style:[t.notice,N.error&&t.errorText],children:N.text}),!d&&e(r,{style:c==="send"?t.fill:{display:"none"},children:e(we,{snapshot:i,onAction:b,onViewActivity:()=>L("events")})}),c==="send"?d??null:!j?o(D,{children:[e(H,{otto:"paused",title:"Connect notification capture",message:"Install @buoy-gg/notifications and call installExpoNotificationCapture in the app entry point. Use an app-specific development build for remote notifications."}),e(l,{style:t.emptyFootnote,children:"The desktop sender works with any installed app. Inspection needs the capture integration."})]}):o(J,{children:[c==="events"&&o(J,{children:[o(r,{style:t.searchRow,children:[o(r,{style:t.searchField,children:[e(he,{size:14,color:a.color.textSecondary}),e(ae,{accessibilityLabel:"Filter notification events",placeholder:"Search notifications\u2026",placeholderTextColor:a.color.placeholder,value:f,onChangeText:n=>{U(n);I(null)},autoCapitalize:"none",autoCorrect:false,style:t.searchInput}),!!f&&e(P,{accessibilityRole:"button",accessibilityLabel:"Clear notification search",onPress:()=>U(""),style:t.clearSearch,children:e(me,{size:12,color:a.color.textSecondary})})]}),e(P,{accessibilityRole:"button",accessibilityLabel:"Clear captured events",disabled:y||!R,accessibilityState:{disabled:y||!R},style:[t.iconButton,(y||!R)&&t.disabled],onPress:()=>{void T("clearCapturedEvents").then(n=>{if(n)I(null)})},children:e(fe,{size:14,color:a.color.textSecondary})})]}),s?o(D,{style:t.fill,contentContainerStyle:[t.content,{paddingBottom:A.bottom}],keyboardShouldPersistTaps:"handled",children:[o(r,{style:t.actionRow,children:[e(h,{label:"Back to events",onPress:()=>I(null),icon:e(de,{size:14,color:a.color.accent})}),e(h,{label:"Copy event JSON",onPress:()=>{void X(s)},icon:e(ee,{size:14,color:a.color.accent})})]}),o(g,{glass:true,padded:true,children:[e(z,{label:K[s.kind]??s.kind,tone:ie(s.kind)}),e(l,{style:t.detailTitle,children:s.title??"Notification event"}),e(l,{style:t.caption,children:new Date(s.capturedAt).toLocaleString()})]}),o(r,{children:[e(v,{children:"Observation"}),e(g,{glass:true,children:o(te,{children:[e(x,{label:"App state at callback",value:s.observedAppState}),s.testId&&e(x,{label:"Test ID",value:s.testId}),s.notificationId&&e(x,{label:"Notification ID",value:s.notificationId}),s.actionIdentifier&&e(x,{label:"Action",value:s.actionIdentifier})]})})]}),s.kind==="startup-response"&&e(l,{style:t.footnote,children:"Recovered from the SDK cache. This alone does not establish why the app launched."}),s.truncated&&e(l,{style:t.warning,children:"This payload was truncated when captured."}),e(E,{title:"Payload",value:s.raw}),e(E,{title:"Full event",value:s,initiallyOpen:false})]}):e(se,{data:$,keyExtractor:n=>n.id,style:t.fill,contentContainerStyle:[t.eventList,{paddingBottom:A.bottom}],keyboardShouldPersistTaps:"handled",initialNumToRender:15,ListHeaderComponent:f?o(l,{style:t.resultCount,children:[$.length," of ",R," events"]}):null,ListFooterComponent:i.evicted>0?o(l,{style:t.footnote,children:[i.evicted," older records removed by the history limit."]}):null,ListEmptyComponent:e(H,{otto:f?"sad":u?"listening":"paused",title:f?"No matching events":u?"Waiting for notifications":"Capture is stopped",message:f?"Try another title, test ID, or payload value.":u?"Send a notification from Buoy Desktop or your app. Background alerts can reach the system tray before an app callback runs.":"Start capture with the power button, then send a notification."}),renderItem:({item:n})=>e(Ce,{event:n,onPress:()=>I(n.id)})})]}),c==="tokens"&&o(D,{style:t.fill,contentContainerStyle:[t.content,{paddingBottom:A.bottom}],keyboardShouldPersistTaps:"handled",children:[o(r,{children:[e(h,{label:"Back to settings",onPress:()=>L("setup")}),e(v,{children:"Registration"}),o(g,{glass:true,padded:true,children:[e(l,{style:t.bodyText,children:"Register a token to test delivery through your push provider."}),o(r,{style:t.actionRow,children:[e(h,{label:"Register device token",onPress:()=>{void T("refreshToken",{type:"device"})},disabled:y,primary:true}),e(h,{label:"Register Expo token",onPress:()=>{void T("refreshToken",{type:"expo"})},disabled:y||!i.capabilities.includes("expo-token")})]})]}),e(l,{style:t.footnote,children:"Registration runs only when you or your app request it. Simulator sends work without a token."})]}),i.tokens.length===0?e(H,{otto:"listening",title:"No tokens observed",message:"Register a device token above, or let your app register one."}):i.tokens.map((n,m)=>o(r,{children:[e(v,{children:n.type.toUpperCase()+" token"}),o(g,{glass:true,padded:true,children:[o(r,{style:t.actionRow,children:[e(z,{label:n.type,tone:"accent"}),n.environment&&e(l,{style:t.caption,children:n.environment})]}),e(l,{selectable:true,style:t.token,children:n.value}),n.projectId&&o(l,{selectable:true,style:t.caption,children:["Project: ",n.projectId]}),o(l,{style:t.caption,children:["Observed ",new Date(n.observedAt).toLocaleString()]}),e(r,{style:t.actionRow,children:e(h,{label:"Copy "+n.type.toUpperCase()+" token",onPress:()=>{void X(n.value)},icon:e(ee,{size:14,color:a.color.accent})})})]})]},n.type+"-"+m))]}),c==="setup"&&o(D,{style:t.fill,contentContainerStyle:[t.content,{paddingBottom:A.bottom}],keyboardShouldPersistTaps:"handled",children:[o(r,{children:[e(h,{label:"Device tokens",onPress:()=>L("tokens")}),e(v,{children:"Capture session"}),o(g,{glass:true,padded:true,children:[o(r,{style:t.actionRow,children:[e(z,{label:u?"Recording":"Stopped",tone:u?"accent":"neutral"}),e(l,{style:t.caption,children:u?"Until "+new Date(C).toLocaleTimeString():"Ready when you are"})]}),o(r,{style:t.actionRow,children:[e(h,{label:"Arm capture for 30 minutes",onPress:q,disabled:y,primary:true}),e(h,{label:"Stop capture",onPress:W,disabled:y||!u})]})]}),e(l,{style:t.footnote,children:i.durable?"History is saved on the app and survives app and desktop restarts.":"History is held in memory. Keep the app running to retain it."})]}),o(r,{children:[e(v,{children:"Connected app"}),e(g,{glass:true,children:o(te,{children:[e(x,{label:"App",value:i.appId??"Not supplied"}),e(x,{label:"Provider",value:[i.provider,i.providerVersion].filter(Boolean).join(" ")}),e(x,{label:"Platform",value:i.platform})]})})]}),o(r,{children:[e(v,{children:"Permissions"}),e(r,{style:t.sectionActions,children:e(h,{label:"Refresh permissions",disabled:y,onPress:()=>{void T("getPermissions")}})}),e(E,{title:"Permission settings",value:i.permissions})]}),o(r,{children:[e(v,{children:"System tray"}),e(r,{style:t.sectionActions,children:e(h,{label:"Inspect system tray",disabled:y||!i.capabilities.includes("presented"),onPress:()=>{void T("listPresented")}})}),e(E,{title:"Currently presented",value:i.presented}),e(l,{style:t.footnote,children:"Notifications currently in the system tray. Captured history is in Activity."})]}),o(r,{children:[e(v,{children:"Capabilities"}),e(g,{glass:true,padded:true,children:e(r,{style:t.actionRow,children:i.capabilities.map(n=>e(z,{label:n},n))})})]})]})]})]});return e(J,{children:w?w({header:G,body:Q}):o(r,{style:t.root,children:[e(r,{style:t.inlineHeader,children:G}),Q]})})}function Ce({event:i,onPress:b}){const d=ie(i.kind);const p=d==="danger"?a.color.danger:d==="warning"?a.color.warning:d==="accent"?a.color.accent:a.color.textSecondary;return o(P,{accessibilityRole:"button",accessibilityLabel:(K[i.kind]??i.kind)+": "+(i.title??i.testId??i.notificationId??"notification"),onPress:b,style:({pressed:S})=>[t.event,S&&t.pressed],children:[o(r,{style:t.eventTop,children:[o(r,{style:t.eventKind,children:[e(pe,{size:13,color:p}),e(l,{numberOfLines:1,style:[t.eventLabel,{color:p}],children:K[i.kind]??i.kind})]}),e(l,{style:t.timestamp,children:new Date(i.capturedAt).toLocaleTimeString()})]}),o(r,{style:t.eventSubject,children:[e(l,{numberOfLines:1,style:t.eventTitle,children:i.title??i.testId??i.notificationId??"Notification event"}),e(Z,{size:14,color:a.color.textTertiary})]}),o(l,{numberOfLines:1,style:t.eventMeta,children:[i.observedAppState,i.truncated?" \xB7 Truncated":""]})]})}