@developer_tribe/react-native-comnyx 0.15.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 (210) hide show
  1. package/Comnyx.podspec +10 -2
  2. package/README.md +50 -0
  3. package/android/build.gradle +1 -0
  4. package/android/consumer-rules.pro +23 -0
  5. package/android/generated/java/com/comnyx/NativeComnyxSpec.java +46 -0
  6. package/android/generated/jni/RNComnyxSpec-generated.cpp +23 -1
  7. package/android/generated/jni/RNComnyxSpec.h +7 -0
  8. package/android/generated/jni/react/renderer/components/RNComnyxSpec/RNComnyxSpecJSI-generated.cpp +21 -0
  9. package/android/generated/jni/react/renderer/components/RNComnyxSpec/RNComnyxSpecJSI.h +70 -0
  10. package/android/src/main/AndroidManifest.xml +11 -1
  11. package/android/src/main/AndroidManifestNew.xml +11 -1
  12. package/android/src/main/java/com/comnyx/ComnyxMediaPickerModule.kt +91 -51
  13. package/android/src/main/java/com/comnyx/ComnyxModule.kt +7 -0
  14. package/android/src/main/java/com/comnyx/src/messaging/firebase/FirebaseMessagingService.kt +4 -6
  15. package/android/src/main/res/xml/comnyx_file_paths.xml +12 -0
  16. package/ios/APNService.swift +9 -9
  17. package/ios/Comnyx.swift +17 -8
  18. package/ios/ComnyxMediaPicker.swift +47 -26
  19. package/ios/ComnyxMessaging.swift +2 -0
  20. package/ios/PrivacyInfo.xcprivacy +32 -0
  21. package/ios/comnyx_post_install.rb +25 -0
  22. package/ios/generated/RCTAppDependencyProvider.h +25 -0
  23. package/ios/generated/RCTAppDependencyProvider.mm +55 -0
  24. package/ios/generated/RCTModulesConformingToProtocolsProvider.h +18 -0
  25. package/ios/generated/RCTModulesConformingToProtocolsProvider.mm +33 -0
  26. package/ios/generated/RCTThirdPartyComponentsProvider.h +16 -0
  27. package/ios/generated/RCTThirdPartyComponentsProvider.mm +23 -0
  28. package/ios/generated/RNComnyxSpec/RNComnyxSpec-generated.mm +53 -0
  29. package/ios/generated/RNComnyxSpec/RNComnyxSpec.h +67 -0
  30. package/ios/generated/RNComnyxSpecJSI-generated.cpp +38 -0
  31. package/ios/generated/RNComnyxSpecJSI.h +89 -0
  32. package/ios/generated/ReactAppDependencyProvider.podspec +34 -0
  33. package/lib/commonjs/NativeComnyxMediaPicker.js +19 -0
  34. package/lib/commonjs/NativeComnyxMediaPicker.js.map +1 -1
  35. package/lib/commonjs/api/conversations.js +6 -6
  36. package/lib/commonjs/api/conversations.js.map +1 -1
  37. package/lib/commonjs/api/customers.js +3 -2
  38. package/lib/commonjs/api/customers.js.map +1 -1
  39. package/lib/commonjs/api/media.js +20 -6
  40. package/lib/commonjs/api/media.js.map +1 -1
  41. package/lib/commonjs/api/messages.js +3 -2
  42. package/lib/commonjs/api/messages.js.map +1 -1
  43. package/lib/commonjs/components/ChatList.js +132 -90
  44. package/lib/commonjs/components/ChatList.js.map +1 -1
  45. package/lib/commonjs/components/ComnyxErrorBoundary.js +92 -0
  46. package/lib/commonjs/components/ComnyxErrorBoundary.js.map +1 -0
  47. package/lib/commonjs/components/CustomerForm.js +7 -3
  48. package/lib/commonjs/components/CustomerForm.js.map +1 -1
  49. package/lib/commonjs/components/InitFailed.js +77 -21
  50. package/lib/commonjs/components/InitFailed.js.map +1 -1
  51. package/lib/commonjs/components/MediaMessageItem.js +37 -10
  52. package/lib/commonjs/components/MediaMessageItem.js.map +1 -1
  53. package/lib/commonjs/components/MediaViewerModal.js +16 -3
  54. package/lib/commonjs/components/MediaViewerModal.js.map +1 -1
  55. package/lib/commonjs/components/MessageInput.js +83 -15
  56. package/lib/commonjs/components/MessageInput.js.map +1 -1
  57. package/lib/commonjs/components/MessageItem.js +1 -1
  58. package/lib/commonjs/components/MessageItem.js.map +1 -1
  59. package/lib/commonjs/hooks/usePolling.js +30 -21
  60. package/lib/commonjs/hooks/usePolling.js.map +1 -1
  61. package/lib/commonjs/hooks/useThemeColors.js +12 -1
  62. package/lib/commonjs/hooks/useThemeColors.js.map +1 -1
  63. package/lib/commonjs/index.js +6 -0
  64. package/lib/commonjs/index.js.map +1 -1
  65. package/lib/commonjs/notifications/initializeNotifications.js +19 -16
  66. package/lib/commonjs/notifications/initializeNotifications.js.map +1 -1
  67. package/lib/commonjs/register/Accumulator.js +19 -6
  68. package/lib/commonjs/register/Accumulator.js.map +1 -1
  69. package/lib/commonjs/register/collectData.js +1 -1
  70. package/lib/commonjs/register/collectData.js.map +1 -1
  71. package/lib/commonjs/register/login.js +5 -0
  72. package/lib/commonjs/register/login.js.map +1 -1
  73. package/lib/commonjs/store/store.js +6 -0
  74. package/lib/commonjs/store/store.js.map +1 -1
  75. package/lib/commonjs/support/ComnyxSupport.js +77 -16
  76. package/lib/commonjs/support/ComnyxSupport.js.map +1 -1
  77. package/lib/commonjs/support/SupportConfigContext.js +66 -0
  78. package/lib/commonjs/support/SupportConfigContext.js.map +1 -0
  79. package/lib/commonjs/support/index.js +7 -0
  80. package/lib/commonjs/support/index.js.map +1 -1
  81. package/lib/commonjs/types/Theme.js +30 -2
  82. package/lib/commonjs/types/Theme.js.map +1 -1
  83. package/lib/commonjs/version.js +1 -1
  84. package/lib/module/NativeComnyxMediaPicker.js +18 -0
  85. package/lib/module/NativeComnyxMediaPicker.js.map +1 -1
  86. package/lib/module/api/conversations.js +6 -6
  87. package/lib/module/api/conversations.js.map +1 -1
  88. package/lib/module/api/customers.js +3 -2
  89. package/lib/module/api/customers.js.map +1 -1
  90. package/lib/module/api/media.js +21 -6
  91. package/lib/module/api/media.js.map +1 -1
  92. package/lib/module/api/messages.js +3 -2
  93. package/lib/module/api/messages.js.map +1 -1
  94. package/lib/module/components/ChatList.js +133 -91
  95. package/lib/module/components/ChatList.js.map +1 -1
  96. package/lib/module/components/ComnyxErrorBoundary.js +87 -0
  97. package/lib/module/components/ComnyxErrorBoundary.js.map +1 -0
  98. package/lib/module/components/CustomerForm.js +7 -3
  99. package/lib/module/components/CustomerForm.js.map +1 -1
  100. package/lib/module/components/InitFailed.js +79 -23
  101. package/lib/module/components/InitFailed.js.map +1 -1
  102. package/lib/module/components/MediaMessageItem.js +37 -11
  103. package/lib/module/components/MediaMessageItem.js.map +1 -1
  104. package/lib/module/components/MediaViewerModal.js +15 -3
  105. package/lib/module/components/MediaViewerModal.js.map +1 -1
  106. package/lib/module/components/MessageInput.js +84 -16
  107. package/lib/module/components/MessageInput.js.map +1 -1
  108. package/lib/module/components/MessageItem.js +1 -1
  109. package/lib/module/components/MessageItem.js.map +1 -1
  110. package/lib/module/hooks/usePolling.js +30 -21
  111. package/lib/module/hooks/usePolling.js.map +1 -1
  112. package/lib/module/hooks/useThemeColors.js +13 -2
  113. package/lib/module/hooks/useThemeColors.js.map +1 -1
  114. package/lib/module/index.js +1 -0
  115. package/lib/module/index.js.map +1 -1
  116. package/lib/module/notifications/initializeNotifications.js +19 -16
  117. package/lib/module/notifications/initializeNotifications.js.map +1 -1
  118. package/lib/module/register/Accumulator.js +19 -6
  119. package/lib/module/register/Accumulator.js.map +1 -1
  120. package/lib/module/register/collectData.js +1 -1
  121. package/lib/module/register/collectData.js.map +1 -1
  122. package/lib/module/register/login.js +5 -0
  123. package/lib/module/register/login.js.map +1 -1
  124. package/lib/module/store/store.js +6 -0
  125. package/lib/module/store/store.js.map +1 -1
  126. package/lib/module/support/ComnyxSupport.js +79 -18
  127. package/lib/module/support/ComnyxSupport.js.map +1 -1
  128. package/lib/module/support/SupportConfigContext.js +59 -0
  129. package/lib/module/support/SupportConfigContext.js.map +1 -0
  130. package/lib/module/support/index.js +1 -0
  131. package/lib/module/support/index.js.map +1 -1
  132. package/lib/module/types/Theme.js +30 -2
  133. package/lib/module/types/Theme.js.map +1 -1
  134. package/lib/module/version.js +1 -1
  135. package/lib/typescript/src/NativeComnyxMediaPicker.d.ts +9 -0
  136. package/lib/typescript/src/NativeComnyxMediaPicker.d.ts.map +1 -1
  137. package/lib/typescript/src/api/conversations.d.ts +2 -2
  138. package/lib/typescript/src/api/conversations.d.ts.map +1 -1
  139. package/lib/typescript/src/api/customers.d.ts +1 -1
  140. package/lib/typescript/src/api/customers.d.ts.map +1 -1
  141. package/lib/typescript/src/api/media.d.ts +3 -3
  142. package/lib/typescript/src/api/media.d.ts.map +1 -1
  143. package/lib/typescript/src/api/messages.d.ts +1 -1
  144. package/lib/typescript/src/api/messages.d.ts.map +1 -1
  145. package/lib/typescript/src/components/ChatList.d.ts.map +1 -1
  146. package/lib/typescript/src/components/ComnyxErrorBoundary.d.ts +18 -0
  147. package/lib/typescript/src/components/ComnyxErrorBoundary.d.ts.map +1 -0
  148. package/lib/typescript/src/components/CustomerForm.d.ts.map +1 -1
  149. package/lib/typescript/src/components/InitFailed.d.ts +5 -2
  150. package/lib/typescript/src/components/InitFailed.d.ts.map +1 -1
  151. package/lib/typescript/src/components/MediaMessageItem.d.ts.map +1 -1
  152. package/lib/typescript/src/components/MediaViewerModal.d.ts.map +1 -1
  153. package/lib/typescript/src/components/MessageInput.d.ts.map +1 -1
  154. package/lib/typescript/src/components/MessageItem.d.ts.map +1 -1
  155. package/lib/typescript/src/hooks/usePolling.d.ts.map +1 -1
  156. package/lib/typescript/src/hooks/useThemeColors.d.ts.map +1 -1
  157. package/lib/typescript/src/index.d.ts +3 -0
  158. package/lib/typescript/src/index.d.ts.map +1 -1
  159. package/lib/typescript/src/notifications/initializeNotifications.d.ts.map +1 -1
  160. package/lib/typescript/src/register/Accumulator.d.ts.map +1 -1
  161. package/lib/typescript/src/register/collectData.d.ts +4 -1
  162. package/lib/typescript/src/register/collectData.d.ts.map +1 -1
  163. package/lib/typescript/src/register/login.d.ts.map +1 -1
  164. package/lib/typescript/src/store/store.d.ts +6 -2
  165. package/lib/typescript/src/store/store.d.ts.map +1 -1
  166. package/lib/typescript/src/support/ComnyxSupport.d.ts +80 -3
  167. package/lib/typescript/src/support/ComnyxSupport.d.ts.map +1 -1
  168. package/lib/typescript/src/support/SupportConfigContext.d.ts +98 -0
  169. package/lib/typescript/src/support/SupportConfigContext.d.ts.map +1 -0
  170. package/lib/typescript/src/support/index.d.ts +2 -0
  171. package/lib/typescript/src/support/index.d.ts.map +1 -1
  172. package/lib/typescript/src/types/Conversation.d.ts +2 -2
  173. package/lib/typescript/src/types/Conversation.d.ts.map +1 -1
  174. package/lib/typescript/src/types/Customer.d.ts +1 -1
  175. package/lib/typescript/src/types/Customer.d.ts.map +1 -1
  176. package/lib/typescript/src/types/MessageResponse.d.ts +7 -4
  177. package/lib/typescript/src/types/MessageResponse.d.ts.map +1 -1
  178. package/lib/typescript/src/types/Theme.d.ts +26 -0
  179. package/lib/typescript/src/types/Theme.d.ts.map +1 -1
  180. package/lib/typescript/src/version.d.ts +1 -1
  181. package/package.json +19 -30
  182. package/src/NativeComnyxMediaPicker.ts +18 -0
  183. package/src/api/conversations.ts +6 -4
  184. package/src/api/customers.ts +3 -1
  185. package/src/api/media.ts +32 -10
  186. package/src/api/messages.ts +3 -1
  187. package/src/components/ChatList.tsx +147 -99
  188. package/src/components/ComnyxErrorBoundary.tsx +91 -0
  189. package/src/components/CustomerForm.tsx +7 -3
  190. package/src/components/InitFailed.tsx +80 -16
  191. package/src/components/MediaMessageItem.tsx +48 -11
  192. package/src/components/MediaViewerModal.tsx +21 -8
  193. package/src/components/MessageInput.tsx +108 -17
  194. package/src/components/MessageItem.tsx +12 -13
  195. package/src/hooks/usePolling.ts +26 -11
  196. package/src/hooks/useThemeColors.ts +11 -2
  197. package/src/index.ts +16 -0
  198. package/src/notifications/initializeNotifications.ts +22 -20
  199. package/src/register/Accumulator.ts +26 -9
  200. package/src/register/collectData.ts +10 -2
  201. package/src/register/login.ts +5 -0
  202. package/src/store/store.ts +11 -3
  203. package/src/support/ComnyxSupport.tsx +172 -23
  204. package/src/support/SupportConfigContext.tsx +157 -0
  205. package/src/support/index.ts +11 -0
  206. package/src/types/Conversation.ts +2 -2
  207. package/src/types/Customer.ts +1 -2
  208. package/src/types/MessageResponse.ts +4 -4
  209. package/src/types/Theme.ts +38 -0
  210. package/src/version.ts +1 -1
@@ -1,4 +1,4 @@
1
- import type { CreateCustomerRequest } from '../types/Customer';
1
+ import type { CreateCustomerRequest, CustomParameter } from '../types/Customer';
2
2
 
3
3
  const ACCUMULATOR_DEBOUNCE_TIME_IN_MS = 3000;
4
4
 
@@ -21,11 +21,17 @@ export class Accumulator {
21
21
 
22
22
  add(data: Partial<Omit<CreateCustomerRequest, 'externalId'>>) {
23
23
  if (!this.registerData) {
24
- throw new Error('Register data is not set');
24
+ // Late events (push token callbacks, AppState changes) may arrive
25
+ // after logout() or before login(). Ignore silently instead of
26
+ // throwing so the host app is not crashed by SDK-internal races.
27
+ console.warn(
28
+ '[Comnyx] add() called before register/after logout — ignored'
29
+ );
30
+ return;
25
31
  }
26
32
 
27
33
  // First, create a map of existing custom parameters by name
28
- const customParamsMap = new Map<string, string>();
34
+ const customParamsMap = new Map<string, CustomParameter['value']>();
29
35
 
30
36
  // Add existing parameters to the map
31
37
  (this.registerData.customParameters || []).forEach((param) => {
@@ -59,10 +65,19 @@ export class Accumulator {
59
65
 
60
66
  async flush() {
61
67
  if (!this.registerData) {
62
- throw new Error('Register data is not set');
68
+ console.warn(
69
+ '[Comnyx] flush() called before register/after logout — ignored'
70
+ );
71
+ return;
63
72
  }
64
73
  if (this.listener) {
65
- await this.listener(this.registerData!);
74
+ try {
75
+ await this.listener(this.registerData!);
76
+ } catch (err) {
77
+ console.error('[Comnyx] flush listener error', err);
78
+ this.reset();
79
+ return;
80
+ }
66
81
  this.registerData = {
67
82
  ...this.registerData,
68
83
  customParameters: [],
@@ -74,14 +89,16 @@ export class Accumulator {
74
89
 
75
90
  debounce() {
76
91
  if (!this.registerData) {
77
- throw new Error('Register data is not set');
92
+ return;
78
93
  }
79
94
  this.reset();
80
95
  this.__select_time = setTimeout(() => {
81
- if (this.listener) {
82
- this.listener(this.registerData!);
96
+ if (this.listener && this.registerData) {
97
+ Promise.resolve(this.listener(this.registerData)).catch((err) => {
98
+ console.error('[Comnyx] debounce listener error', err);
99
+ });
83
100
  this.registerData = {
84
- ...this.registerData!,
101
+ ...this.registerData,
85
102
  customParameters: [],
86
103
  };
87
104
  this._isListenerCalledOnce = true;
@@ -1,12 +1,20 @@
1
+ import type { CustomParameter } from '../types/Customer';
1
2
  import { accumulator } from './Accumulator';
2
3
 
3
- export function collectData<T>(key: string | Record<string, any>, value?: T) {
4
+ export function collectData(key: string, value: CustomParameter['value']): void;
5
+ export function collectData(
6
+ data: Partial<Parameters<typeof accumulator.add>[0]>
7
+ ): void;
8
+ export function collectData(
9
+ key: string | Partial<Parameters<typeof accumulator.add>[0]>,
10
+ value?: CustomParameter['value']
11
+ ) {
4
12
  if (typeof key === 'string') {
5
13
  accumulator.add({
6
14
  customParameters: [
7
15
  {
8
16
  name: key,
9
- value,
17
+ value: value ?? null,
10
18
  },
11
19
  ],
12
20
  });
@@ -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
  }
@@ -6,6 +6,8 @@ import type { AppConversationMessage } from '../types/Conversation';
6
6
  import type { LanguageCode } from '../types/Language';
7
7
  import type { Customer } from '../types/Customer';
8
8
  import type { LocalizationKeys } from '../types/LocalizationKeys';
9
+ import type { GlobalTheme } from '../types/GlobalTheme';
10
+ import type { ThemeOverrideConfig } from '../types/Theme';
9
11
  import { mmkvStorage } from '../utils/mmkvStorage';
10
12
 
11
13
  interface AppStoreState {
@@ -16,7 +18,8 @@ interface AppStoreState {
16
18
  language: LanguageCode;
17
19
  theme: 'light' | 'dark';
18
20
  fake: boolean;
19
- themes: any;
21
+ themes: GlobalTheme;
22
+ themeOverride: ThemeOverrideConfig;
20
23
  firstMessage: AppConversationMessage | null;
21
24
  setData: (
22
25
  cb: (
@@ -28,7 +31,8 @@ interface AppStoreState {
28
31
  setLanguage: (language: LanguageCode) => void;
29
32
  setTheme: (theme: 'light' | 'dark') => void;
30
33
  setFake: (fake: boolean) => void;
31
- setThemes: (themes: any) => void;
34
+ setThemes: (themes: GlobalTheme) => void;
35
+ setThemeOverride: (themeOverride: ThemeOverrideConfig) => void;
32
36
  updateBaseDimensions: (config: {
33
37
  baseHeight: number;
34
38
  baseWidth: number;
@@ -57,6 +61,7 @@ export const storeCreator: StateCreator<AppStoreState> = (set, get) => ({
57
61
  theme: 'light',
58
62
  fake: false,
59
63
  themes: {},
64
+ themeOverride: {},
60
65
  firstMessage: null,
61
66
  formSubmitted: false,
62
67
  setData: (cb) => {
@@ -81,9 +86,12 @@ export const storeCreator: StateCreator<AppStoreState> = (set, get) => ({
81
86
  setFake: (fake: boolean) => {
82
87
  set({ fake });
83
88
  },
84
- setThemes: (themes: any) => {
89
+ setThemes: (themes: GlobalTheme) => {
85
90
  set({ themes });
86
91
  },
92
+ setThemeOverride: (themeOverride: ThemeOverrideConfig) => {
93
+ set({ themeOverride });
94
+ },
87
95
  updateBaseDimensions: ({ baseWidth, baseHeight }) => {
88
96
  set({ baseWidth, baseHeight });
89
97
  },
@@ -2,29 +2,113 @@ 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';
9
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
8
10
  import { ChatList } from '../components/ChatList';
9
11
  import { CustomerForm } from '../components/CustomerForm';
10
12
  import type { LanguageCode } from '../types/Language';
11
- import { useEffect, useState } from 'react';
13
+ import type { GlobalTheme } from '../types/GlobalTheme';
14
+ import type { ThemeOverrideConfig } from '../types/Theme';
15
+ import { useEffect, useMemo, useState, type ReactElement } from 'react';
12
16
  import { AppText } from '../components/AppText';
13
17
  import { useThemeColors } from '../hooks/useThemeColors';
14
18
  import { usePolling } from '../hooks/usePolling';
15
19
  import { useAppStore } from '../store/store';
16
20
  import { ScaledSheet } from '../components/ScaledSheet';
17
21
  import { accumulator } from '../register/Accumulator';
22
+ import {
23
+ SupportConfigProvider,
24
+ type SupportConfig,
25
+ setGlobalSupportErrorReporter,
26
+ reportSupportError,
27
+ } from './SupportConfigContext';
18
28
  const closeIcon = require('../assets/x-close.png');
19
29
 
20
30
  interface SupportComnyxProps {
31
+ /** Language code used for localised UI strings. Defaults to `'en'`. */
21
32
  language?: LanguageCode;
22
- 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';
40
+ /** Render the chat in fake/demo mode (no network). */
23
41
  fake?: boolean;
42
+ /** Called when the user taps the close button. */
24
43
  onBack: () => void;
25
- themes?: any;
44
+ /**
45
+ * Font-family provider. Shape: `{ family: (languageCode) => FamilyWeight }`.
46
+ * Not related to colour theming — use `themeOverride` for colours.
47
+ */
48
+ themes?: GlobalTheme;
49
+ /**
50
+ * Partial override for the built-in colour palette. Only the tokens you
51
+ * specify are replaced; the rest fall back to the default light/dark theme.
52
+ *
53
+ * @example
54
+ * <ComnyxSupport
55
+ * themeOverride={{
56
+ * light: { primary: '#FF6B6B', link: '#FF9999' },
57
+ * dark: { primary: '#FF9999' },
58
+ * }}
59
+ * />
60
+ */
61
+ themeOverride?: ThemeOverrideConfig;
62
+ /**
63
+ * @deprecated No longer consumed. Kept for backward compatibility and will
64
+ * be removed in a future major release.
65
+ */
26
66
  rtlFix?: boolean;
67
+ /** Additional style merged onto the root container. */
27
68
  containerStyle?: StyleProp<ViewStyle>;
69
+ /**
70
+ * Replace the built-in header. Receives `{ onBack }`. Return `null` to hide
71
+ * the header entirely.
72
+ */
73
+ renderHeader?: SupportConfig['renderHeader'];
74
+ /** Replace the built-in empty-state card shown when there are no messages. */
75
+ renderEmptyState?: SupportConfig['renderEmptyState'];
76
+ /**
77
+ * Replace the built-in init-failed screen. Receives `{ retry }` to trigger a
78
+ * first-page refetch.
79
+ */
80
+ renderErrorState?: SupportConfig['renderErrorState'];
81
+ /**
82
+ * Wrap or replace individual message bubbles. Receives
83
+ * `{ message, defaultNode }` — return `defaultNode` as-is for no change,
84
+ * wrap it for decorations (reactions, swipe actions), or render a fully
85
+ * custom bubble.
86
+ */
87
+ renderMessage?: SupportConfig['renderMessage'];
88
+ /**
89
+ * Called immediately before a message is dispatched. Throw or reject to
90
+ * cancel the send — the user's input and any pending media are preserved.
91
+ * Use for analytics, rate limiting, moderation or injecting extra context.
92
+ */
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'];
28
112
  }
29
113
 
30
114
  export function ComnyxSupport({
@@ -33,7 +117,14 @@ export function ComnyxSupport({
33
117
  fake = false,
34
118
  onBack,
35
119
  themes,
120
+ themeOverride,
36
121
  containerStyle,
122
+ renderHeader,
123
+ renderEmptyState,
124
+ renderErrorState,
125
+ renderMessage,
126
+ onBeforeSend,
127
+ onError,
37
128
  }: SupportComnyxProps) {
38
129
  const { customer, formSubmitted } = useAppStore((s) => ({
39
130
  customer: s.customer,
@@ -41,15 +132,52 @@ export function ComnyxSupport({
41
132
  }));
42
133
  const [initLoading, setInitLoading] = useState(true);
43
134
  const themeColors = useThemeColors();
135
+ const insets = useSafeAreaInsets();
44
136
  usePolling();
45
137
 
138
+ const supportConfig = useMemo<SupportConfig>(
139
+ () => ({
140
+ renderHeader,
141
+ renderEmptyState,
142
+ renderErrorState,
143
+ renderMessage,
144
+ onBeforeSend,
145
+ onError,
146
+ }),
147
+ [
148
+ renderHeader,
149
+ renderEmptyState,
150
+ renderErrorState,
151
+ renderMessage,
152
+ onBeforeSend,
153
+ onError,
154
+ ]
155
+ );
156
+
157
+ useEffect(() => {
158
+ setGlobalSupportErrorReporter(onError);
159
+ return () => {
160
+ setGlobalSupportErrorReporter(undefined);
161
+ };
162
+ }, [onError]);
163
+
46
164
  useEffect(() => {
47
165
  if (!accumulator.isListenerCalledOnce()) {
48
- accumulator.flush().then(() => {
49
- setTimeout(() => {
166
+ accumulator
167
+ .flush()
168
+ .then(() => {
169
+ setTimeout(() => {
170
+ setInitLoading(false);
171
+ }, 100);
172
+ })
173
+ .catch((err) => {
174
+ console.error('[Comnyx] accumulator.flush failed', err);
175
+ reportSupportError(err, {
176
+ section: 'accumulator',
177
+ recoverable: true,
178
+ });
50
179
  setInitLoading(false);
51
- }, 100);
52
- });
180
+ });
53
181
  } else {
54
182
  setInitLoading(false);
55
183
  }
@@ -62,25 +190,43 @@ export function ComnyxSupport({
62
190
  }, [customer, customer?.external_id]);
63
191
 
64
192
  useEffect(() => {
65
- //TOOD: getState().initApp({language,theme,fake,rtlFix,themes})
66
193
  useAppStore.getState().setLanguage(language);
67
- useAppStore.getState().setTheme(theme);
68
194
  useAppStore.getState().setFake(fake);
69
195
  if (themes) {
70
196
  useAppStore.getState().setThemes(themes);
71
197
  }
72
- }, [language, theme, fake, themes]);
198
+ if (themeOverride) {
199
+ useAppStore.getState().setThemeOverride(themeOverride);
200
+ }
201
+ }, [language, fake, themes, themeOverride]);
73
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]);
215
+
216
+ let body: ReactElement;
74
217
  if (!customer) {
75
218
  //NOTE: customer yoksa register hiç çalışmamış
76
- return (
219
+ body = (
77
220
  <View
78
221
  style={[styles.container, { backgroundColor: themeColors.background }]}
79
222
  >
80
223
  <TouchableOpacity
81
224
  activeOpacity={1}
82
- style={[styles.iconContainer]}
225
+ style={[styles.iconContainer, { top: Math.max(insets.top, 16) + 16 }]}
83
226
  onPress={onBack}
227
+ accessibilityRole="button"
228
+ accessibilityLabel="Close support"
229
+ hitSlop={{ top: 12, bottom: 12, left: 12, right: 12 }}
84
230
  >
85
231
  <Image
86
232
  source={closeIcon}
@@ -95,25 +241,29 @@ export function ComnyxSupport({
95
241
  );
96
242
  } else if (!formSubmitted) {
97
243
  //NOTE: customer var ama forSubmitted false (name===null)
98
- return (
244
+ body = (
99
245
  <CustomerForm
100
246
  loading={initLoading}
101
247
  onBack={onBack}
102
248
  containerStyle={containerStyle}
103
249
  />
104
250
  );
251
+ } else {
252
+ body = (
253
+ <View
254
+ style={[
255
+ styles.container,
256
+ { backgroundColor: themeColors.background },
257
+ containerStyle,
258
+ ]}
259
+ >
260
+ <ChatList initLoading={initLoading} onBack={onBack} />
261
+ </View>
262
+ );
105
263
  }
106
264
 
107
265
  return (
108
- <View
109
- style={[
110
- styles.container,
111
- { backgroundColor: themeColors.background },
112
- containerStyle,
113
- ]}
114
- >
115
- <ChatList initLoading={initLoading} onBack={onBack} />
116
- </View>
266
+ <SupportConfigProvider value={supportConfig}>{body}</SupportConfigProvider>
117
267
  );
118
268
  }
119
269
 
@@ -124,7 +274,6 @@ const styles = ScaledSheet.create({
124
274
  },
125
275
  iconContainer: {
126
276
  position: 'absolute',
127
- top: '60@vs',
128
277
  left: '24@s',
129
278
  },
130
279
  closeIcon: {
@@ -0,0 +1,157 @@
1
+ import { createContext, useContext, type ReactNode } from 'react';
2
+ import type { AppConversationMessage } from '../types/Conversation';
3
+
4
+ export interface SupportHeaderRenderProps {
5
+ /** Invoke to close the support screen — same callback passed to `onBack`. */
6
+ onBack?: () => void;
7
+ }
8
+
9
+ export interface SupportErrorRenderProps {
10
+ /** Trigger a retry. Safe to call multiple times. */
11
+ retry: () => void;
12
+ }
13
+
14
+ export interface SupportMessageRenderProps {
15
+ message: AppConversationMessage;
16
+ /** The built-in bubble node — render it as-is, wrap it, or ignore it. */
17
+ defaultNode: ReactNode;
18
+ }
19
+
20
+ export interface SupportSendPayload {
21
+ /** Plain text content the user is sending. Empty when only media is attached. */
22
+ content: string;
23
+ /** Number of media attachments. 0 for text-only messages. */
24
+ mediaCount: number;
25
+ /** Types of the attached media. Empty for text-only messages. */
26
+ mediaTypes: Array<'image' | 'video'>;
27
+ }
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
+
75
+ export interface SupportConfig {
76
+ /**
77
+ * Replace the built-in top header (close button + "Support team / Live" row).
78
+ * Return any React node; returning `null` hides the header entirely.
79
+ */
80
+ renderHeader?: (props: SupportHeaderRenderProps) => ReactNode;
81
+ /** Replace the built-in empty-state card shown when there are no messages. */
82
+ renderEmptyState?: () => ReactNode;
83
+ /**
84
+ * Replace the built-in init-failed screen. The `retry` callback re-runs the
85
+ * first-page fetch.
86
+ */
87
+ renderErrorState?: (props: SupportErrorRenderProps) => ReactNode;
88
+ /**
89
+ * Replace or wrap individual message bubbles. `defaultNode` is the
90
+ * built-in `<MessageItem />` — return it for no change, wrap it to add
91
+ * decorations (reactions, swipe actions), or substitute a fully custom
92
+ * bubble.
93
+ */
94
+ renderMessage?: (props: SupportMessageRenderProps) => ReactNode;
95
+ /**
96
+ * Called immediately before a message is dispatched to the backend. Throw
97
+ * (or reject the returned promise) to cancel the send — the user's input
98
+ * and any pending media are preserved and the UI re-enables.
99
+ *
100
+ * Use for analytics, rate limiting, moderation, or injecting extra context.
101
+ */
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;
109
+ }
110
+
111
+ const EMPTY_CONFIG: SupportConfig = {};
112
+
113
+ const SupportConfigContext = createContext<SupportConfig>(EMPTY_CONFIG);
114
+
115
+ export function SupportConfigProvider({
116
+ value,
117
+ children,
118
+ }: {
119
+ value: SupportConfig;
120
+ children: ReactNode;
121
+ }) {
122
+ return (
123
+ <SupportConfigContext.Provider value={value}>
124
+ {children}
125
+ </SupportConfigContext.Provider>
126
+ );
127
+ }
128
+
129
+ export function useSupportConfig(): SupportConfig {
130
+ return useContext(SupportConfigContext);
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
+ }
@@ -1 +1,12 @@
1
1
  export { ComnyxSupport } from './ComnyxSupport';
2
+ export type {
3
+ SupportConfig,
4
+ SupportHeaderRenderProps,
5
+ SupportErrorRenderProps,
6
+ SupportMessageRenderProps,
7
+ SupportSendPayload,
8
+ SupportErrorSection,
9
+ SupportErrorContext,
10
+ SupportErrorReporter,
11
+ } from './SupportConfigContext';
12
+ export { ComnyxErrorBoundary } from '../components/ComnyxErrorBoundary';
@@ -2,8 +2,8 @@ export interface ConversationMessage {
2
2
  id: number;
3
3
  content: string;
4
4
  created_at: string;
5
- user?: any | null;
6
- bot?: any | null;
5
+ user?: unknown | null;
6
+ bot?: unknown | null;
7
7
  customer?: { name: string; profile_photo_url: null } | null;
8
8
  files?: Array<{
9
9
  id: number;
@@ -1,7 +1,6 @@
1
1
  export interface CustomParameter {
2
2
  name: string;
3
- //JSON any, Record<string, T>, Its added to prevent any type errors. T could be any primitive type.
4
- value: any;
3
+ value: string | number | boolean | null;
5
4
  }
6
5
 
7
6
  export interface OneSignalIntegrationParameters {
@@ -6,9 +6,9 @@ export interface MessageResponse {
6
6
  updated_at: string;
7
7
  created_at: string;
8
8
  id: number;
9
- user: null | any;
10
- customer: null | any;
11
- bot: null | any;
9
+ user: null | unknown;
10
+ customer: null | { name: string; profile_photo_url: null };
11
+ bot: null | unknown;
12
12
  conversation: {
13
13
  id: number;
14
14
  project_id: number;
@@ -55,6 +55,6 @@ export interface MessageResponse {
55
55
  };
56
56
  };
57
57
  };
58
- events: any[];
58
+ events: unknown[];
59
59
  };
60
60
  }