@mushi-mushi/react-native 0.21.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, FC, ReactElement } from 'react';
3
- import { MushiRewardsConfig, MushiPageContext, MushiBreadcrumb, MushiReporterReport, MushiReporterComment, MushiHallOfFameEntry, MushiTierResult, MushiReputationResult, MushiAssistantReply } from '@mushi-mushi/core';
3
+ import { MushiAnalyticsConfig, MushiRewardsConfig, MushiPageContext, MushiBreadcrumb, MushiReporterReport, MushiReporterComment, MushiHallOfFameEntry, MushiPropertyValue, MushiTierResult, MushiReputationResult, MushiAssistantReply } from '@mushi-mushi/core';
4
4
  export { MushiHallOfFameEntry, MushiReporterComment, MushiReporterReport } from '@mushi-mushi/core';
5
5
 
6
6
  interface ConsoleEntry {
@@ -42,6 +42,74 @@ interface DeviceInfo {
42
42
  }
43
43
  declare function getDeviceInfo(): DeviceInfo;
44
44
 
45
+ /**
46
+ * FILE: packages/react-native/src/analytics/event-tracker.ts
47
+ * PURPOSE: Product-analytics batcher behind `useMushi().track()` on React Native.
48
+ *
49
+ * OVERVIEW:
50
+ * - The core tracker (`@mushi-mushi/core` event-tracker.ts) returns early
51
+ * without `window`, so RN gets this minimal port: in-memory batch, interval
52
+ * flush, explicit `flush()` for app-background, AsyncStorage spill for
53
+ * unsent batches (cap 200 / 24 h) and persisted consent.
54
+ * - Validation and PII rules are the shared ones from core
55
+ * (`isValidEventName`, `sanitizeEventProperties`, `propertiesWithinByteLimit`).
56
+ * - Transport is `MushiApiClient.postProductEvents()` — the same
57
+ * POST /v1/sdk/events the web SDK uses; `surface` is `'mobile'`.
58
+ *
59
+ * DEPENDENCIES:
60
+ * - @mushi-mushi/core — validation helpers + wire types.
61
+ * - @react-native-async-storage/async-storage — optional peer; without it the
62
+ * batch is memory-only and consent is per-launch.
63
+ *
64
+ * USAGE:
65
+ * - Created by MushiProvider; hosts call `useMushi().track(name, props)`.
66
+ * - No React import here so the module is unit-testable with fake timers.
67
+ *
68
+ * NOTES:
69
+ * - No DNT signal exists on native; `analytics.enabled:false` or
70
+ * `analytics.consent:'required'` + `setConsent('denied')` are the off switches.
71
+ * App Tracking Transparency prompts remain the host's responsibility.
72
+ */
73
+
74
+ /** The subset of the web `analytics` block that applies on native. */
75
+ type RNAnalyticsConfig = Pick<MushiAnalyticsConfig, 'enabled' | 'consent' | 'flushIntervalMs' | 'propertyAllowlist'>;
76
+
77
+ /**
78
+ * FILE: theme.ts
79
+ * PURPOSE: Host-adaptive theme for the RN report sheet (`widget.theme`).
80
+ *
81
+ * OVERVIEW:
82
+ * - Defaults are neutral: the shared washi/sumi surfaces with an INK accent,
83
+ * so the sheet reads as part of the host app rather than a neon Mushi
84
+ * product. The neon palette is only used by `<MushiBanner variant="neon">`.
85
+ * - Hosts override any token. When a host sets `accent` but not `accentFg`,
86
+ * the text colour on the accent is picked for contrast (black or white).
87
+ *
88
+ * Plan 018 (docs/execplans/reporter-loop-v2.md §1.4).
89
+ */
90
+ interface MushiRNTheme {
91
+ /** Sheet background. */
92
+ bg: string;
93
+ /** Primary text. */
94
+ fg: string;
95
+ /** Secondary text. */
96
+ muted: string;
97
+ /** Cards, inputs, chips. */
98
+ surface: string;
99
+ /** Borders and separators. */
100
+ border: string;
101
+ /** Selected chip, primary button, links. */
102
+ accent: string;
103
+ /** Text on `accent`. */
104
+ accentFg: string;
105
+ success: string;
106
+ error: string;
107
+ /** Host font family; undefined keeps the platform font. */
108
+ fontFamily?: string;
109
+ /** Corner radius for cards, inputs and buttons. */
110
+ radius: number;
111
+ }
112
+
45
113
  interface MushiRNConfig {
46
114
  projectId: string;
47
115
  apiKey: string;
@@ -83,6 +151,13 @@ interface MushiRNConfig {
83
151
  };
84
152
  /** Poll interval (ms) for My Reports while the inbox tab is open. 0 = off (default). */
85
153
  inboxPollIntervalMs?: number;
154
+ /**
155
+ * Theme tokens for the report sheet (bg, fg, muted, surface, border,
156
+ * accent, accentFg, success, error, fontFamily, radius). Unset tokens
157
+ * use neutral system-style defaults with an ink accent; when only
158
+ * `accent` is set, the text on it is chosen for contrast.
159
+ */
160
+ theme?: Partial<MushiRNTheme>;
86
161
  };
87
162
  assistant?: {
88
163
  enabled?: boolean;
@@ -115,6 +190,22 @@ interface MushiRNConfig {
115
190
  secureStorage?: boolean;
116
191
  };
117
192
  rewards?: MushiRewardsConfig;
193
+ /**
194
+ * Product analytics (`useMushi().track()`). Events batch to
195
+ * POST /v1/sdk/events every 5 s, on app background and at 20 queued events;
196
+ * unsent batches spill to AsyncStorage (optional peer) and replay on the
197
+ * next launch. `consent: 'required'` buffers until `setConsent('granted')`.
198
+ * There is no DNT signal on native — `enabled: false` is the off switch.
199
+ */
200
+ analytics?: RNAnalyticsConfig;
201
+ /**
202
+ * Session lifecycle tracking for the console's Activity and Users views:
203
+ * session start / end on foreground and background, a heartbeat every
204
+ * minute, and a page view per `setScreen()` change. Default true. Follows
205
+ * the `analytics` switch and consent: nothing is sent while analytics is
206
+ * disabled or consent is not granted.
207
+ */
208
+ trackSessions?: boolean;
118
209
  }
119
210
 
120
211
  interface MushiRNInstance {
@@ -178,8 +269,12 @@ interface MushiRNInstance {
178
269
  }): void;
179
270
  /** List this device's reports ordered by most recent. Returns [] on failure. */
180
271
  listMyReports(): Promise<MushiReporterReport[]>;
181
- /** List admin + reporter comments on a specific report. */
272
+ /** List admin + reporter comments on a specific report. Returns [] on failure. */
182
273
  listMyComments(reportId: string): Promise<MushiReporterComment[]>;
274
+ /** Like `listMyComments`, but resolves `null` when the thread could not be loaded, so a UI can offer Retry. */
275
+ loadMyThread(reportId: string): Promise<MushiReporterComment[] | null>;
276
+ /** Mark this device's unread notifications for a report as read. Resolves the number marked (0 on failure). */
277
+ markReportRead(reportId: string): Promise<number>;
183
278
  /** Post a reporter reply on a report thread. Returns the new comment or null on failure. */
184
279
  replyToReport(reportId: string, body: string): Promise<MushiReporterComment | null>;
185
280
  /** Record a reporter feedback signal (e.g. `confirms`, `not_fixed`) on a report. Returns the outcome or null. */
@@ -190,6 +285,15 @@ interface MushiRNInstance {
190
285
  getHallOfFame(limit?: number): Promise<MushiHallOfFameEntry[]>;
191
286
  /** Manually record a host-defined activity event. */
192
287
  recordActivity(action: string, metadata?: Record<string, unknown>): void;
288
+ /**
289
+ * Record a product-analytics event, e.g. `track('checkout_started', { plan: 'pro' })`.
290
+ * Names must match `^[a-z][a-z0-9_]{1,63}$`; properties are flat, PII-looking
291
+ * keys are dropped and string values are scrubbed. Batched to
292
+ * POST /v1/sdk/events with `$surface: 'mobile'`. Returns true when queued.
293
+ */
294
+ track(event: string, properties?: Record<string, MushiPropertyValue>): boolean;
295
+ /** Grant or deny analytics consent (persisted in AsyncStorage when available). */
296
+ setConsent(state: 'granted' | 'denied'): void;
193
297
  /** Returns the current user's tier. */
194
298
  getTier(): Promise<MushiTierResult | null>;
195
299
  /** Returns the current user's reputation + point totals. */
@@ -226,7 +330,9 @@ declare function useMushiWidget(): {
226
330
  * - Slide-up modal built entirely on RN built-ins (Modal, Animated, PanResponder)
227
331
  * - Conversational flow: category → description → submit → confirmation
228
332
  * - Drag-to-dismiss via PanResponder on the handle area
229
- * - Dark/light theme via useColorScheme
333
+ * - Host-adaptive theme (`widget.theme`, see ../theme.ts) over light/dark
334
+ * - "Your reports": statuses come from the shared core table
335
+ * (`@mushi-mushi/core/reporter-ui`), so web and RN say the same thing
230
336
  *
231
337
  * DEPENDENCIES:
232
338
  * - React Native built-in APIs only (no third-party libs)
@@ -240,10 +346,14 @@ declare function useMushiWidget(): {
240
346
  * - PanResponder threshold: 80 px downward drag dismisses
241
347
  * - Animated.spring for slide-up, Animated.timing for backdrop
242
348
  * - Success confirmation auto-closes after 1.4 s
349
+ * - Thread loads show a skeleton, then Retry on failure; a failed reply keeps
350
+ * its text and offers Retry. Opening a thread marks it read.
243
351
  *
244
352
  * NOTES:
245
353
  * - KeyboardAvoidingView wraps the sheet so the text input stays visible
246
354
  * - Categories match the web SDK: bug, slow, visual, confusing, other
355
+ * - accessibilityViewIsModal is iOS-only; RN's Modal is its own window on
356
+ * Android, which keeps TalkBack focus inside the sheet there.
247
357
  */
248
358
 
249
359
  interface MushiBottomSheetProps {
@@ -267,6 +377,8 @@ interface MushiBottomSheetProps {
267
377
  assistantSuggestions?: string[];
268
378
  /** Poll My Reports while inbox tab is open. 0 disables polling. */
269
379
  inboxPollIntervalMs?: number;
380
+ /** Theme tokens (`widget.theme`); unset tokens use the neutral defaults. */
381
+ theme?: Partial<MushiRNTheme>;
270
382
  }
271
383
  declare const MushiBottomSheet: FC<MushiBottomSheetProps>;
272
384
 
@@ -390,4 +502,4 @@ declare class AsyncStorageQueue {
390
502
  private getAsyncStorage;
391
503
  }
392
504
 
393
- export { AsyncStorageQueue, type ConsoleEntry, type DeviceInfo, MUSHI_BANNER_DEFAULT_HEIGHT, MushiBanner, type MushiBannerProps, MushiBottomSheet, type MushiBottomSheetProps, MushiFloatingButton, type MushiFloatingButtonProps, MushiProvider, type MushiRNConfig, type MushiRNInstance, MushiTrigger, type MushiTriggerProps, type NavigationEntry, type NetworkEntry, getDeviceInfo, setupConsoleCapture, setupNetworkCapture, useMushi, useMushiReport, useMushiWidget, useNavigationCapture };
505
+ export { AsyncStorageQueue, type ConsoleEntry, type DeviceInfo, MUSHI_BANNER_DEFAULT_HEIGHT, MushiBanner, type MushiBannerProps, MushiBottomSheet, type MushiBottomSheetProps, MushiFloatingButton, type MushiFloatingButtonProps, MushiProvider, type MushiRNConfig, type MushiRNInstance, MushiTrigger, type MushiTriggerProps, type NavigationEntry, type NetworkEntry, type RNAnalyticsConfig, getDeviceInfo, setupConsoleCapture, setupNetworkCapture, useMushi, useMushiReport, useMushiWidget, useNavigationCapture };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, FC, ReactElement } from 'react';
3
- import { MushiRewardsConfig, MushiPageContext, MushiBreadcrumb, MushiReporterReport, MushiReporterComment, MushiHallOfFameEntry, MushiTierResult, MushiReputationResult, MushiAssistantReply } from '@mushi-mushi/core';
3
+ import { MushiAnalyticsConfig, MushiRewardsConfig, MushiPageContext, MushiBreadcrumb, MushiReporterReport, MushiReporterComment, MushiHallOfFameEntry, MushiPropertyValue, MushiTierResult, MushiReputationResult, MushiAssistantReply } from '@mushi-mushi/core';
4
4
  export { MushiHallOfFameEntry, MushiReporterComment, MushiReporterReport } from '@mushi-mushi/core';
5
5
 
6
6
  interface ConsoleEntry {
@@ -42,6 +42,74 @@ interface DeviceInfo {
42
42
  }
43
43
  declare function getDeviceInfo(): DeviceInfo;
44
44
 
45
+ /**
46
+ * FILE: packages/react-native/src/analytics/event-tracker.ts
47
+ * PURPOSE: Product-analytics batcher behind `useMushi().track()` on React Native.
48
+ *
49
+ * OVERVIEW:
50
+ * - The core tracker (`@mushi-mushi/core` event-tracker.ts) returns early
51
+ * without `window`, so RN gets this minimal port: in-memory batch, interval
52
+ * flush, explicit `flush()` for app-background, AsyncStorage spill for
53
+ * unsent batches (cap 200 / 24 h) and persisted consent.
54
+ * - Validation and PII rules are the shared ones from core
55
+ * (`isValidEventName`, `sanitizeEventProperties`, `propertiesWithinByteLimit`).
56
+ * - Transport is `MushiApiClient.postProductEvents()` — the same
57
+ * POST /v1/sdk/events the web SDK uses; `surface` is `'mobile'`.
58
+ *
59
+ * DEPENDENCIES:
60
+ * - @mushi-mushi/core — validation helpers + wire types.
61
+ * - @react-native-async-storage/async-storage — optional peer; without it the
62
+ * batch is memory-only and consent is per-launch.
63
+ *
64
+ * USAGE:
65
+ * - Created by MushiProvider; hosts call `useMushi().track(name, props)`.
66
+ * - No React import here so the module is unit-testable with fake timers.
67
+ *
68
+ * NOTES:
69
+ * - No DNT signal exists on native; `analytics.enabled:false` or
70
+ * `analytics.consent:'required'` + `setConsent('denied')` are the off switches.
71
+ * App Tracking Transparency prompts remain the host's responsibility.
72
+ */
73
+
74
+ /** The subset of the web `analytics` block that applies on native. */
75
+ type RNAnalyticsConfig = Pick<MushiAnalyticsConfig, 'enabled' | 'consent' | 'flushIntervalMs' | 'propertyAllowlist'>;
76
+
77
+ /**
78
+ * FILE: theme.ts
79
+ * PURPOSE: Host-adaptive theme for the RN report sheet (`widget.theme`).
80
+ *
81
+ * OVERVIEW:
82
+ * - Defaults are neutral: the shared washi/sumi surfaces with an INK accent,
83
+ * so the sheet reads as part of the host app rather than a neon Mushi
84
+ * product. The neon palette is only used by `<MushiBanner variant="neon">`.
85
+ * - Hosts override any token. When a host sets `accent` but not `accentFg`,
86
+ * the text colour on the accent is picked for contrast (black or white).
87
+ *
88
+ * Plan 018 (docs/execplans/reporter-loop-v2.md §1.4).
89
+ */
90
+ interface MushiRNTheme {
91
+ /** Sheet background. */
92
+ bg: string;
93
+ /** Primary text. */
94
+ fg: string;
95
+ /** Secondary text. */
96
+ muted: string;
97
+ /** Cards, inputs, chips. */
98
+ surface: string;
99
+ /** Borders and separators. */
100
+ border: string;
101
+ /** Selected chip, primary button, links. */
102
+ accent: string;
103
+ /** Text on `accent`. */
104
+ accentFg: string;
105
+ success: string;
106
+ error: string;
107
+ /** Host font family; undefined keeps the platform font. */
108
+ fontFamily?: string;
109
+ /** Corner radius for cards, inputs and buttons. */
110
+ radius: number;
111
+ }
112
+
45
113
  interface MushiRNConfig {
46
114
  projectId: string;
47
115
  apiKey: string;
@@ -83,6 +151,13 @@ interface MushiRNConfig {
83
151
  };
84
152
  /** Poll interval (ms) for My Reports while the inbox tab is open. 0 = off (default). */
85
153
  inboxPollIntervalMs?: number;
154
+ /**
155
+ * Theme tokens for the report sheet (bg, fg, muted, surface, border,
156
+ * accent, accentFg, success, error, fontFamily, radius). Unset tokens
157
+ * use neutral system-style defaults with an ink accent; when only
158
+ * `accent` is set, the text on it is chosen for contrast.
159
+ */
160
+ theme?: Partial<MushiRNTheme>;
86
161
  };
87
162
  assistant?: {
88
163
  enabled?: boolean;
@@ -115,6 +190,22 @@ interface MushiRNConfig {
115
190
  secureStorage?: boolean;
116
191
  };
117
192
  rewards?: MushiRewardsConfig;
193
+ /**
194
+ * Product analytics (`useMushi().track()`). Events batch to
195
+ * POST /v1/sdk/events every 5 s, on app background and at 20 queued events;
196
+ * unsent batches spill to AsyncStorage (optional peer) and replay on the
197
+ * next launch. `consent: 'required'` buffers until `setConsent('granted')`.
198
+ * There is no DNT signal on native — `enabled: false` is the off switch.
199
+ */
200
+ analytics?: RNAnalyticsConfig;
201
+ /**
202
+ * Session lifecycle tracking for the console's Activity and Users views:
203
+ * session start / end on foreground and background, a heartbeat every
204
+ * minute, and a page view per `setScreen()` change. Default true. Follows
205
+ * the `analytics` switch and consent: nothing is sent while analytics is
206
+ * disabled or consent is not granted.
207
+ */
208
+ trackSessions?: boolean;
118
209
  }
119
210
 
120
211
  interface MushiRNInstance {
@@ -178,8 +269,12 @@ interface MushiRNInstance {
178
269
  }): void;
179
270
  /** List this device's reports ordered by most recent. Returns [] on failure. */
180
271
  listMyReports(): Promise<MushiReporterReport[]>;
181
- /** List admin + reporter comments on a specific report. */
272
+ /** List admin + reporter comments on a specific report. Returns [] on failure. */
182
273
  listMyComments(reportId: string): Promise<MushiReporterComment[]>;
274
+ /** Like `listMyComments`, but resolves `null` when the thread could not be loaded, so a UI can offer Retry. */
275
+ loadMyThread(reportId: string): Promise<MushiReporterComment[] | null>;
276
+ /** Mark this device's unread notifications for a report as read. Resolves the number marked (0 on failure). */
277
+ markReportRead(reportId: string): Promise<number>;
183
278
  /** Post a reporter reply on a report thread. Returns the new comment or null on failure. */
184
279
  replyToReport(reportId: string, body: string): Promise<MushiReporterComment | null>;
185
280
  /** Record a reporter feedback signal (e.g. `confirms`, `not_fixed`) on a report. Returns the outcome or null. */
@@ -190,6 +285,15 @@ interface MushiRNInstance {
190
285
  getHallOfFame(limit?: number): Promise<MushiHallOfFameEntry[]>;
191
286
  /** Manually record a host-defined activity event. */
192
287
  recordActivity(action: string, metadata?: Record<string, unknown>): void;
288
+ /**
289
+ * Record a product-analytics event, e.g. `track('checkout_started', { plan: 'pro' })`.
290
+ * Names must match `^[a-z][a-z0-9_]{1,63}$`; properties are flat, PII-looking
291
+ * keys are dropped and string values are scrubbed. Batched to
292
+ * POST /v1/sdk/events with `$surface: 'mobile'`. Returns true when queued.
293
+ */
294
+ track(event: string, properties?: Record<string, MushiPropertyValue>): boolean;
295
+ /** Grant or deny analytics consent (persisted in AsyncStorage when available). */
296
+ setConsent(state: 'granted' | 'denied'): void;
193
297
  /** Returns the current user's tier. */
194
298
  getTier(): Promise<MushiTierResult | null>;
195
299
  /** Returns the current user's reputation + point totals. */
@@ -226,7 +330,9 @@ declare function useMushiWidget(): {
226
330
  * - Slide-up modal built entirely on RN built-ins (Modal, Animated, PanResponder)
227
331
  * - Conversational flow: category → description → submit → confirmation
228
332
  * - Drag-to-dismiss via PanResponder on the handle area
229
- * - Dark/light theme via useColorScheme
333
+ * - Host-adaptive theme (`widget.theme`, see ../theme.ts) over light/dark
334
+ * - "Your reports": statuses come from the shared core table
335
+ * (`@mushi-mushi/core/reporter-ui`), so web and RN say the same thing
230
336
  *
231
337
  * DEPENDENCIES:
232
338
  * - React Native built-in APIs only (no third-party libs)
@@ -240,10 +346,14 @@ declare function useMushiWidget(): {
240
346
  * - PanResponder threshold: 80 px downward drag dismisses
241
347
  * - Animated.spring for slide-up, Animated.timing for backdrop
242
348
  * - Success confirmation auto-closes after 1.4 s
349
+ * - Thread loads show a skeleton, then Retry on failure; a failed reply keeps
350
+ * its text and offers Retry. Opening a thread marks it read.
243
351
  *
244
352
  * NOTES:
245
353
  * - KeyboardAvoidingView wraps the sheet so the text input stays visible
246
354
  * - Categories match the web SDK: bug, slow, visual, confusing, other
355
+ * - accessibilityViewIsModal is iOS-only; RN's Modal is its own window on
356
+ * Android, which keeps TalkBack focus inside the sheet there.
247
357
  */
248
358
 
249
359
  interface MushiBottomSheetProps {
@@ -267,6 +377,8 @@ interface MushiBottomSheetProps {
267
377
  assistantSuggestions?: string[];
268
378
  /** Poll My Reports while inbox tab is open. 0 disables polling. */
269
379
  inboxPollIntervalMs?: number;
380
+ /** Theme tokens (`widget.theme`); unset tokens use the neutral defaults. */
381
+ theme?: Partial<MushiRNTheme>;
270
382
  }
271
383
  declare const MushiBottomSheet: FC<MushiBottomSheetProps>;
272
384
 
@@ -390,4 +502,4 @@ declare class AsyncStorageQueue {
390
502
  private getAsyncStorage;
391
503
  }
392
504
 
393
- export { AsyncStorageQueue, type ConsoleEntry, type DeviceInfo, MUSHI_BANNER_DEFAULT_HEIGHT, MushiBanner, type MushiBannerProps, MushiBottomSheet, type MushiBottomSheetProps, MushiFloatingButton, type MushiFloatingButtonProps, MushiProvider, type MushiRNConfig, type MushiRNInstance, MushiTrigger, type MushiTriggerProps, type NavigationEntry, type NetworkEntry, getDeviceInfo, setupConsoleCapture, setupNetworkCapture, useMushi, useMushiReport, useMushiWidget, useNavigationCapture };
505
+ export { AsyncStorageQueue, type ConsoleEntry, type DeviceInfo, MUSHI_BANNER_DEFAULT_HEIGHT, MushiBanner, type MushiBannerProps, MushiBottomSheet, type MushiBottomSheetProps, MushiFloatingButton, type MushiFloatingButtonProps, MushiProvider, type MushiRNConfig, type MushiRNInstance, MushiTrigger, type MushiTriggerProps, type NavigationEntry, type NetworkEntry, type RNAnalyticsConfig, getDeviceInfo, setupConsoleCapture, setupNetworkCapture, useMushi, useMushiReport, useMushiWidget, useNavigationCapture };