@robylon/react-native-sdk 2.0.16-staging.2 → 2.0.16-staging.3

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 (137) hide show
  1. package/.npmignore.development +18 -0
  2. package/babel.config.js +15 -15
  3. package/ios/Podfile +12 -0
  4. package/lib/commonjs/Chatbotsdk.js +1 -1
  5. package/lib/commonjs/Chatbotsdk.js.map +1 -1
  6. package/lib/commonjs/FloatingButton.js +1 -1
  7. package/lib/commonjs/FloatingButton.js.map +1 -1
  8. package/lib/commonjs/components/DebugButton.js +2 -0
  9. package/lib/commonjs/components/DebugButton.js.map +1 -0
  10. package/lib/commonjs/components/ErrorBoundary.js +2 -0
  11. package/lib/commonjs/components/ErrorBoundary.js.map +1 -0
  12. package/lib/commonjs/constants/errorConstants.js +2 -0
  13. package/lib/commonjs/constants/errorConstants.js.map +1 -0
  14. package/lib/commonjs/constants.js +1 -1
  15. package/lib/commonjs/constants.js.map +1 -1
  16. package/lib/commonjs/hooks/useChatbotEvents.js +2 -0
  17. package/lib/commonjs/hooks/useChatbotEvents.js.map +1 -0
  18. package/lib/commonjs/index.js +1 -1
  19. package/lib/commonjs/index.js.map +1 -1
  20. package/lib/commonjs/services/ErrorTrackingService.js +2 -0
  21. package/lib/commonjs/services/ErrorTrackingService.js.map +1 -0
  22. package/lib/commonjs/types/events.js +2 -0
  23. package/lib/commonjs/types/events.js.map +1 -0
  24. package/lib/commonjs/types/react-native-flipper-performance-plugin.d.js +2 -0
  25. package/lib/commonjs/types/react-native-flipper-performance-plugin.d.js.map +1 -0
  26. package/lib/commonjs/types/react-native-globals.d.js +2 -0
  27. package/lib/commonjs/types/react-native-globals.d.js.map +1 -0
  28. package/lib/commonjs/utils/cookieUtils.js +2 -0
  29. package/lib/commonjs/utils/cookieUtils.js.map +1 -0
  30. package/lib/commonjs/utils/debugConfig.js +2 -0
  31. package/lib/commonjs/utils/debugConfig.js.map +1 -0
  32. package/lib/commonjs/utils/debugMenu.js +2 -0
  33. package/lib/commonjs/utils/debugMenu.js.map +1 -0
  34. package/lib/commonjs/utils/errorHandler.js +2 -0
  35. package/lib/commonjs/utils/errorHandler.js.map +1 -0
  36. package/lib/commonjs/utils/logger.js +2 -0
  37. package/lib/commonjs/utils/logger.js.map +1 -0
  38. package/lib/commonjs/utils/systemInfo.js +2 -0
  39. package/lib/commonjs/utils/systemInfo.js.map +1 -0
  40. package/lib/commonjs/utils/webViewStorage.js +2 -0
  41. package/lib/commonjs/utils/webViewStorage.js.map +1 -0
  42. package/lib/commonjs/version.js +2 -0
  43. package/lib/commonjs/version.js.map +1 -0
  44. package/lib/module/Chatbotsdk.js +1 -1
  45. package/lib/module/Chatbotsdk.js.map +1 -1
  46. package/lib/module/FloatingButton.js +1 -1
  47. package/lib/module/FloatingButton.js.map +1 -1
  48. package/lib/module/components/DebugButton.js +2 -0
  49. package/lib/module/components/DebugButton.js.map +1 -0
  50. package/lib/module/components/ErrorBoundary.js +2 -0
  51. package/lib/module/components/ErrorBoundary.js.map +1 -0
  52. package/lib/module/constants/errorConstants.js +2 -0
  53. package/lib/module/constants/errorConstants.js.map +1 -0
  54. package/lib/module/constants.js +1 -1
  55. package/lib/module/constants.js.map +1 -1
  56. package/lib/module/hooks/useChatbotEvents.js +2 -0
  57. package/lib/module/hooks/useChatbotEvents.js.map +1 -0
  58. package/lib/module/index.js +1 -1
  59. package/lib/module/index.js.map +1 -1
  60. package/lib/module/services/ErrorTrackingService.js +2 -0
  61. package/lib/module/services/ErrorTrackingService.js.map +1 -0
  62. package/lib/module/types/events.js +2 -0
  63. package/lib/module/types/events.js.map +1 -0
  64. package/lib/module/types/react-native-flipper-performance-plugin.d.js +2 -0
  65. package/lib/module/types/react-native-flipper-performance-plugin.d.js.map +1 -0
  66. package/lib/module/types/react-native-globals.d.js +2 -0
  67. package/lib/module/types/react-native-globals.d.js.map +1 -0
  68. package/lib/module/utils/cookieUtils.js +2 -0
  69. package/lib/module/utils/cookieUtils.js.map +1 -0
  70. package/lib/module/utils/debugConfig.js +2 -0
  71. package/lib/module/utils/debugConfig.js.map +1 -0
  72. package/lib/module/utils/debugMenu.js +2 -0
  73. package/lib/module/utils/debugMenu.js.map +1 -0
  74. package/lib/module/utils/errorHandler.js +2 -0
  75. package/lib/module/utils/errorHandler.js.map +1 -0
  76. package/lib/module/utils/logger.js +2 -0
  77. package/lib/module/utils/logger.js.map +1 -0
  78. package/lib/module/utils/systemInfo.js +2 -0
  79. package/lib/module/utils/systemInfo.js.map +1 -0
  80. package/lib/module/utils/webViewStorage.js +2 -0
  81. package/lib/module/utils/webViewStorage.js.map +1 -0
  82. package/lib/module/version.js +2 -0
  83. package/lib/module/version.js.map +1 -0
  84. package/lib/typescript/Chatbotsdk.d.ts +9 -10
  85. package/lib/typescript/Chatbotsdk.d.ts.map +1 -1
  86. package/lib/typescript/FloatingButton.d.ts.map +1 -1
  87. package/lib/typescript/components/DebugButton.d.ts +4 -0
  88. package/lib/typescript/components/DebugButton.d.ts.map +1 -0
  89. package/lib/typescript/components/ErrorBoundary.d.ts +16 -0
  90. package/lib/typescript/components/ErrorBoundary.d.ts.map +1 -0
  91. package/lib/typescript/constants/errorConstants.d.ts +18 -0
  92. package/lib/typescript/constants/errorConstants.d.ts.map +1 -0
  93. package/lib/typescript/hooks/useChatbotEvents.d.ts +17 -0
  94. package/lib/typescript/hooks/useChatbotEvents.d.ts.map +1 -0
  95. package/lib/typescript/index.d.ts +5 -1
  96. package/lib/typescript/index.d.ts.map +1 -1
  97. package/lib/typescript/services/ErrorTrackingService.d.ts +34 -0
  98. package/lib/typescript/services/ErrorTrackingService.d.ts.map +1 -0
  99. package/lib/typescript/types/events.d.ts +22 -0
  100. package/lib/typescript/types/events.d.ts.map +1 -0
  101. package/lib/typescript/utils/cookieUtils.d.ts +2 -0
  102. package/lib/typescript/utils/cookieUtils.d.ts.map +1 -0
  103. package/lib/typescript/utils/debugConfig.d.ts +8 -0
  104. package/lib/typescript/utils/debugConfig.d.ts.map +1 -0
  105. package/lib/typescript/utils/debugMenu.d.ts +3 -0
  106. package/lib/typescript/utils/debugMenu.d.ts.map +1 -0
  107. package/lib/typescript/utils/errorHandler.d.ts +2 -0
  108. package/lib/typescript/utils/errorHandler.d.ts.map +1 -0
  109. package/lib/typescript/utils/logger.d.ts +7 -0
  110. package/lib/typescript/utils/logger.d.ts.map +1 -0
  111. package/lib/typescript/utils/systemInfo.d.ts +19 -0
  112. package/lib/typescript/utils/systemInfo.d.ts.map +1 -0
  113. package/lib/typescript/utils/webViewStorage.d.ts +16 -0
  114. package/lib/typescript/utils/webViewStorage.d.ts.map +1 -0
  115. package/lib/typescript/version.d.ts +2 -0
  116. package/lib/typescript/version.d.ts.map +1 -0
  117. package/package.json +8 -2
  118. package/scripts/generate-version.js +9 -0
  119. package/src/Chatbotsdk.tsx +426 -151
  120. package/src/FloatingButton.tsx +18 -1
  121. package/src/components/DebugButton.tsx +46 -0
  122. package/src/components/ErrorBoundary.tsx +38 -0
  123. package/src/constants/errorConstants.ts +21 -0
  124. package/src/hooks/useChatbotEvents.ts +93 -0
  125. package/src/index.tsx +9 -1
  126. package/src/services/ErrorTrackingService.ts +217 -0
  127. package/src/types/events.ts +25 -0
  128. package/src/types/react-native-flipper-performance-plugin.d.ts +3 -0
  129. package/src/types/react-native-globals.d.ts +10 -0
  130. package/src/utils/cookieUtils.ts +7 -0
  131. package/src/utils/debugConfig.ts +56 -0
  132. package/src/utils/debugMenu.ts +14 -0
  133. package/src/utils/errorHandler.ts +58 -0
  134. package/src/utils/logger.ts +14 -0
  135. package/src/utils/systemInfo.ts +84 -0
  136. package/src/utils/webViewStorage.ts +62 -0
  137. package/src/version.ts +2 -0
@@ -1,4 +1,10 @@
1
- import React, { useState, useEffect, useRef, useMemo } from "react";
1
+ import React, {
2
+ useState,
3
+ useEffect,
4
+ useRef,
5
+ useMemo,
6
+ useCallback,
7
+ } from "react";
2
8
  import {
3
9
  View,
4
10
  StyleSheet,
@@ -6,32 +12,55 @@ import {
6
12
  Dimensions,
7
13
  Platform,
8
14
  StatusBar,
15
+ PixelRatio,
9
16
  } from "react-native";
10
17
  import { WebView, WebViewMessageEvent } from "react-native-webview";
11
18
  import FloatingButton from "./FloatingButton";
12
19
  import { API_URL, BASE_CHATBOT_URL } from "./constants";
20
+ import { SDK_VERSION } from "./version";
21
+ import { getSystemInfo, getBrowserAndOSInfoScript } from "./utils/systemInfo";
22
+ import { useChatbotEvents } from "./hooks/useChatbotEvents";
23
+ import { ChatbotEventType, ChatbotEventHandler } from "./types/events";
24
+ import { logger } from "./utils/logger";
25
+ import { enableNetworkDebug, enableWebViewDebug } from "./utils/debugConfig";
26
+ import DebugButton from "./components/DebugButton";
27
+ import { ErrorBoundary } from "./components/ErrorBoundary";
28
+ import {
29
+ ErrorCodes,
30
+ ErrorTypes,
31
+ ErrorMessages,
32
+ } from "./constants/errorConstants";
33
+ import { setupGlobalErrorHandlers } from "./utils/errorHandler";
34
+ import { errorTracker } from "./services/ErrorTrackingService";
35
+ import { generateUUID } from "./utils/cookieUtils";
36
+ import {
37
+ StorageMessageType,
38
+ StorageMessage,
39
+ getStorageScript,
40
+ createStorageMessage,
41
+ } from "./utils/webViewStorage";
13
42
 
14
- interface ChatbotSDKProps {
43
+ export interface ChatbotProps {
15
44
  api_key: string;
16
- user_id?: string;
45
+ user_id?: string | null | number;
17
46
  user_token?: string;
18
- additional_params?: Record<string, string>;
47
+ user_profile?: Record<string, any>;
48
+ onEvent?: ChatbotEventHandler;
49
+ }
50
+
51
+ interface InternalChatbotProps extends ChatbotProps {
19
52
  show_floating_button?: boolean;
20
53
  onMessage?: (message: any) => void;
21
54
  isFullScreen?: boolean;
22
55
  enableAnimation?: boolean;
23
56
  onOpen?: () => void;
24
57
  onClose?: () => void;
25
- user_info?: {
26
- name?: string;
27
- email?: string;
28
- mobile?: string;
29
- };
30
58
  }
31
59
 
32
60
  interface ChatbotMessage {
33
61
  type: string;
34
62
  data?: any;
63
+ level?: string;
35
64
  }
36
65
 
37
66
  interface ChatbotConfig {
@@ -40,18 +69,18 @@ interface ChatbotConfig {
40
69
  welcome_message: string;
41
70
  }
42
71
 
43
- const ChatbotSDK: React.FC<ChatbotSDKProps> = ({
72
+ const Chatbot: React.FC<InternalChatbotProps> = ({
44
73
  api_key,
45
74
  user_id,
46
75
  user_token,
47
- additional_params = {},
48
76
  show_floating_button = true,
77
+ onEvent,
49
78
  onMessage,
50
79
  isFullScreen = true,
51
80
  enableAnimation = true,
52
81
  onOpen,
53
82
  onClose,
54
- user_info,
83
+ user_profile,
55
84
  }) => {
56
85
  const [isWebViewVisible, setIsWebViewVisible] = useState(false);
57
86
  const [chatbotConfig, setChatbotConfig] = useState<ChatbotConfig | null>(
@@ -63,15 +92,31 @@ const ChatbotSDK: React.FC<ChatbotSDKProps> = ({
63
92
  const isFirstLoadRef = useRef(true);
64
93
  const [toastVisible, setToastVisible] = useState(false);
65
94
  const [toastMessage, setToastMessage] = useState("");
66
- const webViewUrl = useMemo(
67
- () => constructUrl(),
68
- [api_key, user_id, additional_params]
95
+ const [effectiveUserId, setEffectiveUserId] = useState<string>();
96
+ const [isStorageReady, setIsStorageReady] = useState(false);
97
+ const pendingStorageOps = useRef<Array<() => void>>([]);
98
+ const memoryCache = useRef<Record<string, string>>({});
99
+
100
+ const [systemInfo, setSystemInfo] = useState({ os: "", browser: "" });
101
+
102
+ // Create chatbotConfig object for events
103
+ const chatbotConfigForEvents = useMemo(
104
+ () => ({
105
+ userId: effectiveUserId,
106
+ isAnonymous: !user_id && user_id !== 0,
107
+ // Add other config properties as needed
108
+ }),
109
+ [effectiveUserId, user_id]
69
110
  );
70
111
 
71
- const stableAdditionalParams = useMemo(
72
- () => additional_params,
73
- [JSON.stringify(additional_params)]
74
- );
112
+ // Initialize event handling
113
+ const { emitEvent, onInternalEvent } = useChatbotEvents({
114
+ api_key,
115
+ chatbotConfig: chatbotConfigForEvents ?? {},
116
+ user_profile: user_profile,
117
+ onEvent: onEvent,
118
+ systemInfo: systemInfo ?? { os: "", browser: "" },
119
+ });
75
120
 
76
121
  const triggerAppInit = () => {
77
122
  if (webViewRef.current) {
@@ -83,15 +128,203 @@ const ChatbotSDK: React.FC<ChatbotSDKProps> = ({
83
128
  }
84
129
  };
85
130
 
131
+ const executeStorageOp = useCallback(
132
+ (operation: () => void) => {
133
+ if (isStorageReady) {
134
+ operation();
135
+ } else {
136
+ pendingStorageOps.current.push(operation);
137
+ }
138
+ },
139
+ [isStorageReady]
140
+ );
141
+
142
+ // Handle WebView messages
143
+ const handleMessage = useCallback(
144
+ (event: WebViewMessageEvent) => {
145
+ const { data } = event.nativeEvent;
146
+
147
+ try {
148
+ const parsedData: ChatbotMessage = JSON.parse(data);
149
+
150
+ // Handle storage messages
151
+ if (
152
+ Object.values(StorageMessageType).includes(
153
+ parsedData.type as StorageMessageType
154
+ )
155
+ ) {
156
+ const storageMessage = parsedData as StorageMessage;
157
+ switch (storageMessage.type) {
158
+ case StorageMessageType.STORAGE_READY:
159
+ setIsStorageReady(true);
160
+ pendingStorageOps.current.forEach((op) => op());
161
+ pendingStorageOps.current = [];
162
+ // Try to get stored ID immediately after storage is ready
163
+ webViewRef.current?.injectJavaScript(
164
+ createStorageMessage(
165
+ StorageMessageType.GET_STORAGE,
166
+ "rblyn_anon"
167
+ )
168
+ );
169
+ break;
170
+ case StorageMessageType.STORAGE_RESPONSE:
171
+ if (storageMessage.key && storageMessage.value) {
172
+ memoryCache.current[storageMessage.key] = storageMessage.value;
173
+ if (storageMessage.key === "rblyn_anon") {
174
+ setEffectiveUserId(storageMessage.value);
175
+ }
176
+ }
177
+ break;
178
+ case StorageMessageType.STORAGE_ERROR:
179
+ logger.error("WebView storage error:", storageMessage.error);
180
+ break;
181
+ }
182
+ return;
183
+ }
184
+
185
+ // Add debug logging
186
+ if (__DEV__) {
187
+ if (parsedData?.type === "CONSOLE") {
188
+ console.log(`WebView ${parsedData?.level}:`, parsedData?.data);
189
+ return;
190
+ }
191
+ if (parsedData?.type === "ERROR") {
192
+ console.error("WebView Error:", parsedData?.data);
193
+ return;
194
+ }
195
+ }
196
+
197
+ if (parsedData?.type === "SYSTEM_INFO") {
198
+ setSystemInfo(parsedData?.data ?? { os: "", browser: "" });
199
+ return;
200
+ }
201
+
202
+ if (parsedData?.type === "APP_READY") {
203
+ emitEvent(ChatbotEventType.CHATBOT_APP_READY);
204
+ if (webViewRef.current && isWebViewVisible) {
205
+ webViewRef.current?.injectJavaScript(getBrowserAndOSInfoScript());
206
+
207
+ const messageData = {
208
+ name: "registerUserId",
209
+ action: "registerUserId",
210
+ data: {
211
+ userId: effectiveUserId,
212
+ token: user_token ? `${user_token}` : undefined,
213
+ userProfile: {
214
+ ...user_profile,
215
+ ...getSystemInfo(systemInfo),
216
+ },
217
+ },
218
+ };
219
+ logger.debug("messageData====>", JSON.stringify(messageData));
220
+ webViewRef.current?.injectJavaScript(`
221
+ window.postMessage(${JSON.stringify({
222
+ name: "openFrame",
223
+ domain: "app-domain.com",
224
+ })}, '*');
225
+ window.postMessage(${JSON.stringify(messageData)}, '*');
226
+ `);
227
+ }
228
+ }
229
+
230
+ // Handle other message types
231
+ switch (parsedData?.type) {
232
+ case "close_chatbot":
233
+ closeWebView();
234
+ break;
235
+ case "CHATBOT_LOADED":
236
+ emitEvent(ChatbotEventType.CHATBOT_LOADED, parsedData?.data);
237
+ break;
238
+ case "CHAT_INITIALIZED":
239
+ emitEvent(ChatbotEventType.CHAT_INITIALIZED, parsedData?.data);
240
+ break;
241
+ case "SESSION_REFRESHED":
242
+ emitEvent(ChatbotEventType.SESSION_REFRESHED, parsedData?.data);
243
+ break;
244
+ case "CHAT_INITIALIZATION_FAILED":
245
+ emitEvent(
246
+ ChatbotEventType.CHAT_INITIALIZATION_FAILED,
247
+ parsedData?.data
248
+ );
249
+ break;
250
+ default:
251
+ if (onMessage) onMessage(parsedData ?? {});
252
+ }
253
+ } catch (error) {
254
+ errorTracker.trackError(
255
+ error instanceof Error
256
+ ? error
257
+ : new Error("Failed to parse WebView message"),
258
+ "ChatbotSDK",
259
+ {
260
+ type: ErrorTypes.RUNTIME_ERROR,
261
+ context: { messageData: data },
262
+ }
263
+ );
264
+ }
265
+ },
266
+ [
267
+ emitEvent,
268
+ isWebViewVisible,
269
+ onMessage,
270
+ systemInfo,
271
+ effectiveUserId,
272
+ user_token,
273
+ user_profile,
274
+ ]
275
+ );
276
+
277
+ // Initialize user ID
278
+ useEffect(() => {
279
+ const initializeUserId = async () => {
280
+ let userId = user_id ? String(user_id) : undefined;
281
+
282
+ if (!userId) {
283
+ // Try memory cache first
284
+ userId = memoryCache.current["rblyn_anon"];
285
+
286
+ if (!userId) {
287
+ userId = generateUUID();
288
+ executeStorageOp(() => {
289
+ webViewRef.current?.injectJavaScript(
290
+ createStorageMessage(
291
+ StorageMessageType.SET_STORAGE,
292
+ "rblyn_anon",
293
+ userId!
294
+ )
295
+ );
296
+ });
297
+ }
298
+ }
299
+
300
+ setEffectiveUserId(userId);
301
+ };
302
+
303
+ initializeUserId();
304
+ }, [user_id, executeStorageOp]);
305
+
306
+ // Initialize WebView storage
307
+ useEffect(() => {
308
+ if (webViewRef.current) {
309
+ webViewRef.current.injectJavaScript(getStorageScript());
310
+ // Try to get stored ID once storage is ready
311
+ executeStorageOp(() => {
312
+ webViewRef.current?.injectJavaScript(
313
+ createStorageMessage(StorageMessageType.GET_STORAGE, "rblyn_anon")
314
+ );
315
+ });
316
+ }
317
+ }, []);
318
+
86
319
  useEffect(() => {
87
320
  const fetchChatbotConfig = async () => {
88
321
  try {
89
322
  const endpointUrl = `${API_URL}/chat/chatbot/get/`;
90
323
  const payload = {
91
- client_user_id: user_id,
324
+ client_user_id: effectiveUserId,
92
325
  org_id: api_key,
93
326
  token: user_token,
94
- extra_info: stableAdditionalParams,
327
+ extra_info: {},
95
328
  };
96
329
 
97
330
  // setToastMessage(endpointUrl + " : " + BASE_CHATBOT_URL);
@@ -120,29 +353,37 @@ const ChatbotSDK: React.FC<ChatbotSDKProps> = ({
120
353
  });
121
354
  }
122
355
  } catch (error) {
123
- console.error("Error fetching chatbot configuration:", error);
356
+ errorTracker.trackError(
357
+ error instanceof Error
358
+ ? error
359
+ : new Error("Failed to fetch chatbot configuration"),
360
+ "ChatbotSDK",
361
+ {
362
+ type: ErrorTypes.NETWORK_ERROR,
363
+ context: { api_key, user_id },
364
+ }
365
+ );
124
366
  } finally {
125
367
  setLoading(false);
126
368
  }
127
369
  };
128
370
 
129
- if (api_key && user_id) {
371
+ if (api_key && effectiveUserId) {
130
372
  fetchChatbotConfig();
131
373
  }
132
- }, [api_key, user_id, user_token, stableAdditionalParams]);
374
+ }, [api_key, effectiveUserId, user_token]);
133
375
 
134
376
  function constructUrl(): string {
135
- if (!user_id) return "";
377
+ if (!effectiveUserId) return "";
136
378
  const params = new URLSearchParams({
137
379
  id: api_key,
138
- user_id,
139
- ...additional_params,
140
380
  });
141
- console.log("BASE_CHATBOT_URL", BASE_CHATBOT_URL);
381
+ console.log("BASE_CHATBOT_URL SDK_VERSION", BASE_CHATBOT_URL, SDK_VERSION);
142
382
  return `${BASE_CHATBOT_URL}?${params.toString()}`;
143
383
  }
144
384
 
145
- const openWebView = () => {
385
+ // Handle opening the chatbot
386
+ const openWebView = useCallback(() => {
146
387
  setIsWebViewVisible(true);
147
388
  if (enableAnimation) {
148
389
  Animated.timing(fadeAnim, {
@@ -151,8 +392,8 @@ const ChatbotSDK: React.FC<ChatbotSDKProps> = ({
151
392
  useNativeDriver: true,
152
393
  }).start(() => {
153
394
  onOpen?.();
395
+ emitEvent(ChatbotEventType.CHATBOT_OPENED);
154
396
  if (!isFirstLoadRef.current) {
155
- // If not first load, manually trigger initialization
156
397
  setTimeout(triggerAppInit, 100);
157
398
  }
158
399
  });
@@ -160,16 +401,17 @@ const ChatbotSDK: React.FC<ChatbotSDKProps> = ({
160
401
  fadeAnim.setValue(1);
161
402
  requestAnimationFrame(() => {
162
403
  onOpen?.();
404
+ emitEvent(ChatbotEventType.CHATBOT_OPENED);
163
405
  if (!isFirstLoadRef.current) {
164
406
  setTimeout(triggerAppInit, 100);
165
407
  }
166
408
  });
167
409
  }
168
- };
410
+ }, [enableAnimation, fadeAnim, onOpen, emitEvent]);
169
411
 
170
- const closeWebView = () => {
412
+ // Handle closing the chatbot
413
+ const closeWebView = useCallback(() => {
171
414
  if (webViewRef.current) {
172
- console.log("closeWebView");
173
415
  webViewRef.current.postMessage(
174
416
  JSON.stringify({
175
417
  name: "closeFrame",
@@ -181,6 +423,7 @@ const ChatbotSDK: React.FC<ChatbotSDKProps> = ({
181
423
  const complete = () => {
182
424
  setIsWebViewVisible(false);
183
425
  onClose?.();
426
+ emitEvent(ChatbotEventType.CHATBOT_CLOSED);
184
427
  };
185
428
 
186
429
  if (enableAnimation) {
@@ -193,58 +436,14 @@ const ChatbotSDK: React.FC<ChatbotSDKProps> = ({
193
436
  fadeAnim.setValue(0);
194
437
  requestAnimationFrame(complete);
195
438
  }
196
- };
197
-
198
- const handleMessage = (event: WebViewMessageEvent) => {
199
- const { data } = event.nativeEvent;
200
-
201
- try {
202
- const parsedData: ChatbotMessage = JSON.parse(data);
439
+ }, [enableAnimation, fadeAnim, onClose, emitEvent]);
203
440
 
204
- if (parsedData.type === "APP_READY") {
205
- console.log("APP_READY", webViewRef.current && isWebViewVisible);
206
- if (webViewRef.current && isWebViewVisible) {
207
- webViewRef.current.injectJavaScript(`
208
- window.postMessage(${JSON.stringify({
209
- name: "openFrame",
210
- domain: "app-domain.com",
211
- })}, '*');
212
- window.postMessage(${JSON.stringify({
213
- name: "registerUserId",
214
- action: "registerUserId",
215
- domain: "app-domain.com",
216
- data: {
217
- userId: `${user_id}`,
218
- token: `${user_token}`,
219
- user_info: {
220
- ...user_info,
221
- platform: Platform.OS,
222
- SDK_VERSION: "${SDK_VERSION}",
223
- OS: Platform.OS,
224
- browser: Platform.OS,
225
- },
226
- },
227
- })}, '*');
228
- `);
229
- }
230
- }
231
-
232
- if (typeof parsedData !== "object" || !parsedData.type) {
233
- console.warn("Invalid message structure");
234
- return;
235
- }
236
-
237
- switch (parsedData.type) {
238
- case "close_chatbot":
239
- closeWebView();
240
- break;
241
- default:
242
- if (onMessage) onMessage(parsedData);
243
- }
244
- } catch (error) {
245
- console.error("Failed to parse or process WebView message:", error);
441
+ // Emit button loaded event when component mounts
442
+ useEffect(() => {
443
+ if (show_floating_button && chatbotConfig) {
444
+ emitEvent(ChatbotEventType.CHATBOT_BUTTON_LOADED);
246
445
  }
247
- };
446
+ }, [show_floating_button, chatbotConfig, emitEvent]);
248
447
 
249
448
  const getScreenDimensions = () => {
250
449
  const windowHeight = Dimensions.get("window").height;
@@ -260,60 +459,89 @@ const ChatbotSDK: React.FC<ChatbotSDKProps> = ({
260
459
 
261
460
  const screenDimensions = getScreenDimensions();
262
461
 
462
+ useEffect(() => {
463
+ if (__DEV__) {
464
+ enableNetworkDebug();
465
+ }
466
+ }, []);
467
+
468
+ // Deliberate error for testing error boundary
469
+ // useEffect(() => {
470
+ // if (__DEV__) {
471
+ // // Set up global error handlers
472
+ // setupGlobalErrorHandlers();
473
+
474
+ // // Test async error
475
+ // setTimeout(() => {
476
+ // Promise.reject(new Error("Test async error"));
477
+ // }, 1000);
478
+
479
+ // // Test sync error
480
+ // setTimeout(() => {
481
+ // throw new Error("Test sync error");
482
+ // }, 2000);
483
+ // }
484
+ // }, []);
485
+
263
486
  return (
264
- <View style={styles.mainContainer}>
265
- {!loading && (
266
- <>
267
- {show_floating_button && api_key && chatbotConfig && (
268
- <FloatingButton
269
- onPress={openWebView}
270
- brandColor={chatbotConfig.brand_color}
271
- imageUrl={chatbotConfig.image_url}
272
- />
273
- )}
274
- <View
275
- style={[
276
- styles.webViewWrapper,
277
- { height: isWebViewVisible ? "100%" : 0 },
278
- ]}
279
- pointerEvents={isWebViewVisible ? "auto" : "none"}
280
- >
281
- <Animated.View
487
+ <ErrorBoundary componentName="ChatbotSDK">
488
+ <View style={styles.mainContainer}>
489
+ {!loading && (
490
+ <>
491
+ <ErrorBoundary componentName="FloatingButton">
492
+ {show_floating_button && api_key && chatbotConfig && (
493
+ <FloatingButton
494
+ onPress={() => {
495
+ emitEvent(ChatbotEventType.CHATBOT_BUTTON_CLICKED);
496
+ openWebView();
497
+ }}
498
+ brandColor={chatbotConfig.brand_color}
499
+ imageUrl={chatbotConfig.image_url}
500
+ />
501
+ )}
502
+ </ErrorBoundary>
503
+ <View
282
504
  style={[
283
- isFullScreen
284
- ? styles.fullScreenContainer
285
- : styles.inlineContainer,
286
- { opacity: fadeAnim },
505
+ styles.webViewWrapper,
506
+ { height: isWebViewVisible ? "100%" : 0 },
287
507
  ]}
508
+ pointerEvents={isWebViewVisible ? "auto" : "none"}
288
509
  >
289
- <WebView
290
- ref={webViewRef}
291
- source={{ uri: webViewUrl }}
292
- onMessage={handleMessage}
293
- style={styles.webview}
294
- containerStyle={
295
- isFullScreen
296
- ? {
297
- width: screenDimensions.width,
298
- height: screenDimensions.height,
510
+ <Animated.View
511
+ style={[styles.fullScreenContainer, { opacity: fadeAnim }]}
512
+ >
513
+ <ErrorBoundary componentName="ChatbotWebView">
514
+ <WebView
515
+ ref={webViewRef}
516
+ source={{ uri: constructUrl() }}
517
+ onMessage={handleMessage}
518
+ style={styles.webview}
519
+ containerStyle={
520
+ isFullScreen
521
+ ? {
522
+ width: screenDimensions.width,
523
+ height: screenDimensions.height,
524
+ }
525
+ : undefined
526
+ }
527
+ allowsInlineMediaPlayback={true}
528
+ mediaPlaybackRequiresUserAction={false}
529
+ allowFileAccess={false}
530
+ geolocationEnabled={false}
531
+ javaScriptEnabled={true}
532
+ domStorageEnabled={true}
533
+ cacheEnabled={true}
534
+ scrollEnabled={true}
535
+ bounces={false}
536
+ onShouldStartLoadWithRequest={(request) => {
537
+ return request.url.startsWith(BASE_CHATBOT_URL);
538
+ }}
539
+ startInLoadingState={isFirstLoadRef.current}
540
+ onLoadEnd={() => {
541
+ if (__DEV__) {
542
+ enableWebViewDebug(webViewRef);
299
543
  }
300
- : undefined
301
- }
302
- allowsInlineMediaPlayback={true}
303
- mediaPlaybackRequiresUserAction={false}
304
- allowFileAccess={false}
305
- geolocationEnabled={false}
306
- javaScriptEnabled={true}
307
- domStorageEnabled={true}
308
- cacheEnabled={true}
309
- scrollEnabled={true}
310
- bounces={false}
311
- onShouldStartLoadWithRequest={(request) => {
312
- return request.url.startsWith(BASE_CHATBOT_URL);
313
- }}
314
- startInLoadingState={isFirstLoadRef.current}
315
- onLoadEnd={() => {
316
- webViewRef.current?.injectJavaScript(`
544
+ webViewRef.current?.injectJavaScript(`
317
545
  if (!document.querySelector('meta[name="viewport"]')) {
318
546
  var meta = document.createElement('meta');
319
547
  meta.name = 'viewport';
@@ -338,28 +566,75 @@ const ChatbotSDK: React.FC<ChatbotSDKProps> = ({
338
566
  window.ReactNativeWebView.postMessage(JSON.stringify(message));
339
567
  };
340
568
 
341
- var closeButton = document.querySelector('.chatbot-close-button');
342
- if (closeButton) {
343
- closeButton.addEventListener('click', () => {
344
- window.sendToChatbot({ type: 'close_chatbot' });
569
+ // Only create observer if it doesn't exist
570
+ if (!window.closeButtonObserver) {
571
+ // Setup mutation observer to watch for close button
572
+ window.closeButtonObserver = new MutationObserver(function(mutations) {
573
+ const closeButton = document.querySelector('.chatbot-close-button');
574
+ if (closeButton && !closeButton.hasAttribute('listener-attached')) {
575
+ closeButton.setAttribute('listener-attached', 'true');
576
+ closeButton.addEventListener('click', () => {
577
+ try {
578
+ const message = { type: 'close_chatbot' };
579
+ window.ReactNativeWebView.postMessage(JSON.stringify(message));
580
+ } catch (error) {
581
+ console.error('Error sending close message:', error);
582
+ }
583
+ });
584
+ console.log('Close button listener attached');
585
+ }
586
+ });
587
+
588
+ // Start observing
589
+ window.closeButtonObserver.observe(document.body, {
590
+ childList: true,
591
+ subtree: true
345
592
  });
593
+
594
+ // Initial check for existing button
595
+ const existingButton = document.querySelector('.chatbot-close-button');
596
+ if (existingButton && !existingButton.hasAttribute('listener-attached')) {
597
+ existingButton.setAttribute('listener-attached', 'true');
598
+ existingButton.addEventListener('click', () => {
599
+ try {
600
+ const message = { type: 'close_chatbot' };
601
+ window.ReactNativeWebView.postMessage(JSON.stringify(message));
602
+ } catch (error) {
603
+ console.error('Error sending close message:', error);
604
+ }
605
+ });
606
+ console.log('Close button listener attached to existing button');
607
+ }
346
608
  }
347
-
609
+
348
610
  true;
349
611
  `);
350
- isFirstLoadRef.current = false;
351
- }}
352
- onError={(syntheticEvent) => {
353
- const { nativeEvent } = syntheticEvent;
354
- console.warn("WebView error:", nativeEvent);
355
- }}
356
- />
357
- </Animated.View>
358
- </View>
359
- </>
360
- )}
361
- {/* <Toast message={toastMessage} visible={toastVisible} duration={10000} /> */}
362
- </View>
612
+ isFirstLoadRef.current = false;
613
+ }}
614
+ onError={(syntheticEvent) => {
615
+ const { nativeEvent } = syntheticEvent;
616
+ errorTracker.trackError(
617
+ new Error(nativeEvent.description),
618
+ "ChatbotWebView",
619
+ {
620
+ type: ErrorTypes.RUNTIME_ERROR,
621
+ context: {
622
+ url: nativeEvent.url,
623
+ code: nativeEvent.code,
624
+ },
625
+ }
626
+ );
627
+ }}
628
+ />
629
+ </ErrorBoundary>
630
+ </Animated.View>
631
+ </View>
632
+ </>
633
+ )}
634
+ {/* <Toast message={toastMessage} visible={toastVisible} duration={10000} /> */}
635
+ {__DEV__ && <DebugButton />}
636
+ </View>
637
+ </ErrorBoundary>
363
638
  );
364
639
  };
365
640
 
@@ -396,4 +671,4 @@ const styles = StyleSheet.create({
396
671
  },
397
672
  });
398
673
 
399
- export default ChatbotSDK;
674
+ export default Chatbot;