@developer_tribe/react-native-comnyx 0.16.0 → 0.16.1

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 (82) hide show
  1. package/lib/commonjs/components/ChatList.js +39 -45
  2. package/lib/commonjs/components/ChatList.js.map +1 -1
  3. package/lib/commonjs/components/CustomerForm.js +5 -1
  4. package/lib/commonjs/components/CustomerForm.js.map +1 -1
  5. package/lib/commonjs/components/InitFailed.js +77 -21
  6. package/lib/commonjs/components/InitFailed.js.map +1 -1
  7. package/lib/commonjs/components/MediaMessageItem.js +33 -7
  8. package/lib/commonjs/components/MediaMessageItem.js.map +1 -1
  9. package/lib/commonjs/components/MediaViewerModal.js +16 -3
  10. package/lib/commonjs/components/MediaViewerModal.js.map +1 -1
  11. package/lib/commonjs/components/MessageInput.js +20 -2
  12. package/lib/commonjs/components/MessageInput.js.map +1 -1
  13. package/lib/commonjs/hooks/usePolling.js +5 -0
  14. package/lib/commonjs/hooks/usePolling.js.map +1 -1
  15. package/lib/commonjs/index.js +6 -0
  16. package/lib/commonjs/index.js.map +1 -1
  17. package/lib/commonjs/register/login.js +5 -0
  18. package/lib/commonjs/register/login.js.map +1 -1
  19. package/lib/commonjs/support/ComnyxSupport.js +31 -15
  20. package/lib/commonjs/support/ComnyxSupport.js.map +1 -1
  21. package/lib/commonjs/support/SupportConfigContext.js +42 -0
  22. package/lib/commonjs/support/SupportConfigContext.js.map +1 -1
  23. package/lib/commonjs/support/index.js +7 -0
  24. package/lib/commonjs/support/index.js.map +1 -1
  25. package/lib/commonjs/version.js +1 -1
  26. package/lib/module/components/ChatList.js +40 -46
  27. package/lib/module/components/ChatList.js.map +1 -1
  28. package/lib/module/components/CustomerForm.js +5 -1
  29. package/lib/module/components/CustomerForm.js.map +1 -1
  30. package/lib/module/components/InitFailed.js +79 -23
  31. package/lib/module/components/InitFailed.js.map +1 -1
  32. package/lib/module/components/MediaMessageItem.js +33 -8
  33. package/lib/module/components/MediaMessageItem.js.map +1 -1
  34. package/lib/module/components/MediaViewerModal.js +15 -3
  35. package/lib/module/components/MediaViewerModal.js.map +1 -1
  36. package/lib/module/components/MessageInput.js +21 -3
  37. package/lib/module/components/MessageInput.js.map +1 -1
  38. package/lib/module/hooks/usePolling.js +5 -0
  39. package/lib/module/hooks/usePolling.js.map +1 -1
  40. package/lib/module/index.js +2 -0
  41. package/lib/module/index.js.map +1 -1
  42. package/lib/module/register/login.js +5 -0
  43. package/lib/module/register/login.js.map +1 -1
  44. package/lib/module/support/ComnyxSupport.js +33 -17
  45. package/lib/module/support/ComnyxSupport.js.map +1 -1
  46. package/lib/module/support/SupportConfigContext.js +40 -0
  47. package/lib/module/support/SupportConfigContext.js.map +1 -1
  48. package/lib/module/support/index.js +1 -0
  49. package/lib/module/support/index.js.map +1 -1
  50. package/lib/module/version.js +1 -1
  51. package/lib/typescript/src/components/ChatList.d.ts.map +1 -1
  52. package/lib/typescript/src/components/CustomerForm.d.ts.map +1 -1
  53. package/lib/typescript/src/components/InitFailed.d.ts +5 -2
  54. package/lib/typescript/src/components/InitFailed.d.ts.map +1 -1
  55. package/lib/typescript/src/components/MediaMessageItem.d.ts.map +1 -1
  56. package/lib/typescript/src/components/MediaViewerModal.d.ts.map +1 -1
  57. package/lib/typescript/src/components/MessageInput.d.ts.map +1 -1
  58. package/lib/typescript/src/hooks/usePolling.d.ts.map +1 -1
  59. package/lib/typescript/src/index.d.ts +2 -1
  60. package/lib/typescript/src/index.d.ts.map +1 -1
  61. package/lib/typescript/src/register/login.d.ts.map +1 -1
  62. package/lib/typescript/src/support/ComnyxSupport.d.ts +26 -3
  63. package/lib/typescript/src/support/ComnyxSupport.d.ts.map +1 -1
  64. package/lib/typescript/src/support/SupportConfigContext.d.ts +40 -0
  65. package/lib/typescript/src/support/SupportConfigContext.d.ts.map +1 -1
  66. package/lib/typescript/src/support/index.d.ts +2 -1
  67. package/lib/typescript/src/support/index.d.ts.map +1 -1
  68. package/lib/typescript/src/version.d.ts +1 -1
  69. package/package.json +10 -8
  70. package/src/components/ChatList.tsx +33 -45
  71. package/src/components/CustomerForm.tsx +5 -1
  72. package/src/components/InitFailed.tsx +80 -16
  73. package/src/components/MediaMessageItem.tsx +38 -8
  74. package/src/components/MediaViewerModal.tsx +21 -8
  75. package/src/components/MessageInput.tsx +20 -2
  76. package/src/hooks/usePolling.ts +5 -0
  77. package/src/index.ts +4 -0
  78. package/src/register/login.ts +5 -0
  79. package/src/support/ComnyxSupport.tsx +57 -14
  80. package/src/support/SupportConfigContext.tsx +78 -0
  81. package/src/support/index.ts +4 -0
  82. package/src/version.ts +1 -1
@@ -6,6 +6,7 @@ import {
6
6
  StatusBar,
7
7
  useWindowDimensions,
8
8
  } from 'react-native';
9
+ import FastImage from '@d11/react-native-fast-image';
9
10
  import { useState, useEffect } from 'react';
10
11
  import { AppText } from './AppText';
11
12
  import { ScaledSheet } from './ScaledSheet';
@@ -61,15 +62,27 @@ export function MediaViewerModal({
61
62
  >
62
63
  <AppText style={styles.closeIcon}>✕</AppText>
63
64
  </TouchableOpacity>
64
- <View style={[styles.mediaContainer, { width, height: height * 0.7 }]}>
65
+ <View style={[styles.mediaContainer, { width, height }]}>
65
66
  {displayUri && !imageError ? (
66
- <Image
67
- source={{ uri: displayUri }}
68
- style={{ width, height: height * 0.9 }}
69
- resizeMode="contain"
70
- resizeMethod="resize"
71
- onError={() => setImageError(true)}
72
- />
67
+ /^https?:\/\//i.test(displayUri) ? (
68
+ <FastImage
69
+ source={{
70
+ uri: displayUri,
71
+ cache: FastImage.cacheControl.immutable,
72
+ }}
73
+ style={{ width, height }}
74
+ resizeMode={FastImage.resizeMode.contain}
75
+ onError={() => setImageError(true)}
76
+ />
77
+ ) : (
78
+ <Image
79
+ source={{ uri: displayUri }}
80
+ style={{ width, height }}
81
+ resizeMode="contain"
82
+ resizeMethod="resize"
83
+ onError={() => setImageError(true)}
84
+ />
85
+ )
73
86
  ) : (
74
87
  <View
75
88
  style={[
@@ -17,7 +17,10 @@ import { useIsRtl } from '../hooks/isRtl';
17
17
  import { useAppStore } from '../store/store';
18
18
  import { MediaPickerButton } from './MediaPickerButton';
19
19
  import type { MediaAsset } from '../types/MediaTypes';
20
- import { useSupportConfig } from '../support/SupportConfigContext';
20
+ import {
21
+ useSupportConfig,
22
+ reportSupportError,
23
+ } from '../support/SupportConfigContext';
21
24
 
22
25
  const sendDark = require('../assets/arrow-right.png');
23
26
  const circleXIcon = require('../assets/x-circle.png');
@@ -100,7 +103,7 @@ export function MessageInput({
100
103
  //TODO: ??
101
104
  }
102
105
  })
103
- .catch(() => {
106
+ .catch((err) => {
104
107
  const data = useAppStore.getState().data;
105
108
  if (data) {
106
109
  const itemIndex = data.findIndex((item) => item.local_id === localId);
@@ -125,6 +128,11 @@ export function MessageInput({
125
128
  } else {
126
129
  //TODO: ??
127
130
  }
131
+ reportSupportError(err, {
132
+ section: 'send',
133
+ recoverable: true,
134
+ extras: { contentLength: value.length },
135
+ });
128
136
  });
129
137
  setValue('');
130
138
  }, [value, customer, scrollToBottom]);
@@ -296,6 +304,15 @@ export function MessageInput({
296
304
  }
297
305
  }
298
306
  console.error('[Comnyx] Media upload failed:', error);
307
+ reportSupportError(error, {
308
+ section: 'upload',
309
+ recoverable: true,
310
+ extras: {
311
+ assetCount: assets.length,
312
+ mediaTypes: assets.map((a) => a.type),
313
+ aborted: controller.signal.aborted,
314
+ },
315
+ });
299
316
  } finally {
300
317
  if (uploadAbortRef.current === controller) {
301
318
  uploadAbortRef.current = null;
@@ -454,6 +471,7 @@ export function MessageInput({
454
471
  <Image
455
472
  style={[
456
473
  styles.sendIcon,
474
+ { tintColor: themeColors.text },
457
475
  isRtl && { transform: [{ rotate: '180deg' }] },
458
476
  isSending && { opacity: 0.4 },
459
477
  ]}
@@ -1,6 +1,7 @@
1
1
  import { useEffect } from 'react';
2
2
  import { getNewCustomerConversation } from '../api';
3
3
  import { useAppStore } from '../store/store';
4
+ import { reportSupportError } from '../support/SupportConfigContext';
4
5
 
5
6
  const NEW_MESSAGES_CHECK_INTERVAL = 10000;
6
7
 
@@ -45,6 +46,10 @@ export function usePolling() {
45
46
  .catch((err) => {
46
47
  if (controller.signal.aborted) return;
47
48
  console.warn('[Comnyx] Polling failed:', err);
49
+ reportSupportError(err, {
50
+ section: 'polling',
51
+ recoverable: true,
52
+ });
48
53
  });
49
54
  }, NEW_MESSAGES_CHECK_INTERVAL);
50
55
 
package/src/index.ts CHANGED
@@ -16,7 +16,11 @@ export type {
16
16
  SupportErrorRenderProps,
17
17
  SupportMessageRenderProps,
18
18
  SupportSendPayload,
19
+ SupportErrorSection,
20
+ SupportErrorContext,
21
+ SupportErrorReporter,
19
22
  } from './support';
23
+ export { ComnyxErrorBoundary } from './support';
20
24
 
21
25
  //deprecated
22
26
  export { registerOneSignalForComnyx } from './register/collectData';
@@ -3,6 +3,7 @@ import { isInitCalled, setLoginForAxios } from '../api/api';
3
3
  import type { CreateCustomerRequest } from '../types/Customer';
4
4
  import { useAppStore } from '../store/store';
5
5
  import { updateCustomer } from '../api/customers';
6
+ import { reportSupportError } from '../support/SupportConfigContext';
6
7
 
7
8
  interface LoginOptions {
8
9
  externalId: string;
@@ -27,6 +28,10 @@ export function login(loginOptions: LoginOptions) {
27
28
  useAppStore.getState().setCustomer(data.customer);
28
29
  } catch (error) {
29
30
  console.error('[Comnyx] Error in login', error);
31
+ reportSupportError(error, {
32
+ section: 'init',
33
+ recoverable: false,
34
+ });
30
35
  }
31
36
  });
32
37
  }
@@ -2,6 +2,7 @@ import {
2
2
  View,
3
3
  TouchableOpacity,
4
4
  Image,
5
+ Appearance,
5
6
  type ViewStyle,
6
7
  type StyleProp,
7
8
  } from 'react-native';
@@ -21,15 +22,21 @@ import { accumulator } from '../register/Accumulator';
21
22
  import {
22
23
  SupportConfigProvider,
23
24
  type SupportConfig,
25
+ setGlobalSupportErrorReporter,
26
+ reportSupportError,
24
27
  } from './SupportConfigContext';
25
- import { ComnyxErrorBoundary } from '../components/ComnyxErrorBoundary';
26
28
  const closeIcon = require('../assets/x-close.png');
27
29
 
28
30
  interface SupportComnyxProps {
29
31
  /** Language code used for localised UI strings. Defaults to `'en'`. */
30
32
  language?: LanguageCode;
31
- /** Colour palette mode. Defaults to `'dark'`. */
32
- theme?: 'light' | 'dark';
33
+ /**
34
+ * Colour palette mode. Defaults to `'dark'`.
35
+ *
36
+ * Pass `'system'` to follow the OS colour scheme — the palette flips
37
+ * automatically when the user toggles dark/light at the system level.
38
+ */
39
+ theme?: 'light' | 'dark' | 'system';
33
40
  /** Render the chat in fake/demo mode (no network). */
34
41
  fake?: boolean;
35
42
  /** Called when the user taps the close button. */
@@ -84,9 +91,27 @@ interface SupportComnyxProps {
84
91
  * Use for analytics, rate limiting, moderation or injecting extra context.
85
92
  */
86
93
  onBeforeSend?: SupportConfig['onBeforeSend'];
94
+ /**
95
+ * Receives non-render operational errors caught inside the SDK (network
96
+ * failures, polling errors, upload crashes, customer-form submit errors).
97
+ * Wire to your host's error tracker — typically `Bugsnag.notify`:
98
+ *
99
+ * ```tsx
100
+ * onError={(err, ctx) => {
101
+ * if (err instanceof Error) {
102
+ * Bugsnag.notify(err, (event) => event.addMetadata('comnyx', ctx));
103
+ * }
104
+ * }}
105
+ * ```
106
+ *
107
+ * Render-time exceptions intentionally bubble to your root React error
108
+ * boundary (e.g. `@bugsnag/plugin-react`) so the existing crash reporting
109
+ * path keeps working unchanged.
110
+ */
111
+ onError?: SupportConfig['onError'];
87
112
  }
88
113
 
89
- function ComnyxSupportInner({
114
+ export function ComnyxSupport({
90
115
  language = 'en',
91
116
  theme = 'dark',
92
117
  fake = false,
@@ -99,6 +124,7 @@ function ComnyxSupportInner({
99
124
  renderErrorState,
100
125
  renderMessage,
101
126
  onBeforeSend,
127
+ onError,
102
128
  }: SupportComnyxProps) {
103
129
  const { customer, formSubmitted } = useAppStore((s) => ({
104
130
  customer: s.customer,
@@ -116,6 +142,7 @@ function ComnyxSupportInner({
116
142
  renderErrorState,
117
143
  renderMessage,
118
144
  onBeforeSend,
145
+ onError,
119
146
  }),
120
147
  [
121
148
  renderHeader,
@@ -123,9 +150,17 @@ function ComnyxSupportInner({
123
150
  renderErrorState,
124
151
  renderMessage,
125
152
  onBeforeSend,
153
+ onError,
126
154
  ]
127
155
  );
128
156
 
157
+ useEffect(() => {
158
+ setGlobalSupportErrorReporter(onError);
159
+ return () => {
160
+ setGlobalSupportErrorReporter(undefined);
161
+ };
162
+ }, [onError]);
163
+
129
164
  useEffect(() => {
130
165
  if (!accumulator.isListenerCalledOnce()) {
131
166
  accumulator
@@ -137,6 +172,10 @@ function ComnyxSupportInner({
137
172
  })
138
173
  .catch((err) => {
139
174
  console.error('[Comnyx] accumulator.flush failed', err);
175
+ reportSupportError(err, {
176
+ section: 'accumulator',
177
+ recoverable: true,
178
+ });
140
179
  setInitLoading(false);
141
180
  });
142
181
  } else {
@@ -152,7 +191,6 @@ function ComnyxSupportInner({
152
191
 
153
192
  useEffect(() => {
154
193
  useAppStore.getState().setLanguage(language);
155
- useAppStore.getState().setTheme(theme);
156
194
  useAppStore.getState().setFake(fake);
157
195
  if (themes) {
158
196
  useAppStore.getState().setThemes(themes);
@@ -160,7 +198,20 @@ function ComnyxSupportInner({
160
198
  if (themeOverride) {
161
199
  useAppStore.getState().setThemeOverride(themeOverride);
162
200
  }
163
- }, [language, theme, fake, themes, themeOverride]);
201
+ }, [language, fake, themes, themeOverride]);
202
+
203
+ useEffect(() => {
204
+ const setTheme = useAppStore.getState().setTheme;
205
+ if (theme !== 'system') {
206
+ setTheme(theme);
207
+ return undefined;
208
+ }
209
+ setTheme(Appearance.getColorScheme() === 'dark' ? 'dark' : 'light');
210
+ const sub = Appearance.addChangeListener(({ colorScheme }) => {
211
+ setTheme(colorScheme === 'dark' ? 'dark' : 'light');
212
+ });
213
+ return () => sub.remove();
214
+ }, [theme]);
164
215
 
165
216
  let body: ReactElement;
166
217
  if (!customer) {
@@ -216,14 +267,6 @@ function ComnyxSupportInner({
216
267
  );
217
268
  }
218
269
 
219
- export function ComnyxSupport(props: SupportComnyxProps) {
220
- return (
221
- <ComnyxErrorBoundary onBack={props.onBack}>
222
- <ComnyxSupportInner {...props} />
223
- </ComnyxErrorBoundary>
224
- );
225
- }
226
-
227
270
  const styles = ScaledSheet.create({
228
271
  container: {
229
272
  flex: 1,
@@ -26,6 +26,52 @@ export interface SupportSendPayload {
26
26
  mediaTypes: Array<'image' | 'video'>;
27
27
  }
28
28
 
29
+ /**
30
+ * Where inside the SDK an error originated. Helpful for dashboard grouping
31
+ * and for deciding which errors are user-visible vs silent.
32
+ */
33
+ export type SupportErrorSection =
34
+ | 'init'
35
+ | 'pagination'
36
+ | 'polling'
37
+ | 'send'
38
+ | 'upload'
39
+ | 'media-picker'
40
+ | 'customer-form'
41
+ | 'accumulator';
42
+
43
+ export interface SupportErrorContext {
44
+ /** Which subsystem the error came from. */
45
+ section: SupportErrorSection;
46
+ /**
47
+ * True when the SDK already recovered (e.g. surfaced a retry affordance to
48
+ * the user). False for fire-and-forget failures worth investigating.
49
+ */
50
+ recoverable: boolean;
51
+ /** Additional structured context — usage varies per section. */
52
+ extras?: Record<string, unknown>;
53
+ }
54
+
55
+ /**
56
+ * Called whenever the SDK catches an operational error it cannot surface
57
+ * itself. Wire this to your error tracker — for Bugsnag:
58
+ *
59
+ * ```tsx
60
+ * onError={(err, ctx) => {
61
+ * if (err instanceof Error) {
62
+ * Bugsnag.notify(err, (event) => event.addMetadata('comnyx', ctx));
63
+ * }
64
+ * }}
65
+ * ```
66
+ *
67
+ * Render-time exceptions are intentionally NOT funnelled here — they bubble
68
+ * to the host's React error boundary so `@bugsnag/plugin-react` sees them.
69
+ */
70
+ export type SupportErrorReporter = (
71
+ error: unknown,
72
+ context: SupportErrorContext
73
+ ) => void;
74
+
29
75
  export interface SupportConfig {
30
76
  /**
31
77
  * Replace the built-in top header (close button + "Support team / Live" row).
@@ -54,6 +100,12 @@ export interface SupportConfig {
54
100
  * Use for analytics, rate limiting, moderation, or injecting extra context.
55
101
  */
56
102
  onBeforeSend?: (payload: SupportSendPayload) => void | Promise<void>;
103
+ /**
104
+ * Receives non-render operational errors the SDK catches internally. Route
105
+ * to your host's error tracker (e.g. `Bugsnag.notify`) so failures are not
106
+ * silently swallowed to the console.
107
+ */
108
+ onError?: SupportErrorReporter;
57
109
  }
58
110
 
59
111
  const EMPTY_CONFIG: SupportConfig = {};
@@ -77,3 +129,29 @@ export function SupportConfigProvider({
77
129
  export function useSupportConfig(): SupportConfig {
78
130
  return useContext(SupportConfigContext);
79
131
  }
132
+
133
+ /**
134
+ * Module-level error forwarder so hooks that run outside of a render pass
135
+ * (accumulator.flush, polling tick) can still report. Set once per mount.
136
+ */
137
+ let globalErrorReporter: SupportErrorReporter | undefined;
138
+
139
+ export function setGlobalSupportErrorReporter(
140
+ reporter: SupportErrorReporter | undefined
141
+ ) {
142
+ globalErrorReporter = reporter;
143
+ }
144
+
145
+ export function reportSupportError(
146
+ error: unknown,
147
+ context: SupportErrorContext
148
+ ) {
149
+ if (globalErrorReporter) {
150
+ try {
151
+ globalErrorReporter(error, context);
152
+ } catch (reporterErr) {
153
+ // Never let a broken reporter take down the SDK.
154
+ console.error('[Comnyx] onError reporter threw:', reporterErr);
155
+ }
156
+ }
157
+ }
@@ -5,4 +5,8 @@ export type {
5
5
  SupportErrorRenderProps,
6
6
  SupportMessageRenderProps,
7
7
  SupportSendPayload,
8
+ SupportErrorSection,
9
+ SupportErrorContext,
10
+ SupportErrorReporter,
8
11
  } from './SupportConfigContext';
12
+ export { ComnyxErrorBoundary } from '../components/ComnyxErrorBoundary';
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  // This file is auto-generated. Do not edit manually.
2
- export const VERSION = '0.16.0';
2
+ export const VERSION = '0.16.1';