@buoy-gg/notifications 7.0.41

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