@robylon/react-native-sdk 2.0.16-staging.1 → 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 (134) 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/hooks/useChatbotEvents.js +2 -0
  15. package/lib/commonjs/hooks/useChatbotEvents.js.map +1 -0
  16. package/lib/commonjs/index.js +1 -1
  17. package/lib/commonjs/index.js.map +1 -1
  18. package/lib/commonjs/services/ErrorTrackingService.js +2 -0
  19. package/lib/commonjs/services/ErrorTrackingService.js.map +1 -0
  20. package/lib/commonjs/types/events.js +2 -0
  21. package/lib/commonjs/types/events.js.map +1 -0
  22. package/lib/commonjs/types/react-native-flipper-performance-plugin.d.js +2 -0
  23. package/lib/commonjs/types/react-native-flipper-performance-plugin.d.js.map +1 -0
  24. package/lib/commonjs/types/react-native-globals.d.js +2 -0
  25. package/lib/commonjs/types/react-native-globals.d.js.map +1 -0
  26. package/lib/commonjs/utils/cookieUtils.js +2 -0
  27. package/lib/commonjs/utils/cookieUtils.js.map +1 -0
  28. package/lib/commonjs/utils/debugConfig.js +2 -0
  29. package/lib/commonjs/utils/debugConfig.js.map +1 -0
  30. package/lib/commonjs/utils/debugMenu.js +2 -0
  31. package/lib/commonjs/utils/debugMenu.js.map +1 -0
  32. package/lib/commonjs/utils/errorHandler.js +2 -0
  33. package/lib/commonjs/utils/errorHandler.js.map +1 -0
  34. package/lib/commonjs/utils/logger.js +2 -0
  35. package/lib/commonjs/utils/logger.js.map +1 -0
  36. package/lib/commonjs/utils/systemInfo.js +2 -0
  37. package/lib/commonjs/utils/systemInfo.js.map +1 -0
  38. package/lib/commonjs/utils/webViewStorage.js +2 -0
  39. package/lib/commonjs/utils/webViewStorage.js.map +1 -0
  40. package/lib/commonjs/version.js +2 -0
  41. package/lib/commonjs/version.js.map +1 -0
  42. package/lib/module/Chatbotsdk.js +1 -1
  43. package/lib/module/Chatbotsdk.js.map +1 -1
  44. package/lib/module/FloatingButton.js +1 -1
  45. package/lib/module/FloatingButton.js.map +1 -1
  46. package/lib/module/components/DebugButton.js +2 -0
  47. package/lib/module/components/DebugButton.js.map +1 -0
  48. package/lib/module/components/ErrorBoundary.js +2 -0
  49. package/lib/module/components/ErrorBoundary.js.map +1 -0
  50. package/lib/module/constants/errorConstants.js +2 -0
  51. package/lib/module/constants/errorConstants.js.map +1 -0
  52. package/lib/module/hooks/useChatbotEvents.js +2 -0
  53. package/lib/module/hooks/useChatbotEvents.js.map +1 -0
  54. package/lib/module/index.js +1 -1
  55. package/lib/module/index.js.map +1 -1
  56. package/lib/module/openChatbot.js +2 -1
  57. package/lib/module/services/ErrorTrackingService.js +2 -0
  58. package/lib/module/services/ErrorTrackingService.js.map +1 -0
  59. package/lib/module/types/events.js +2 -0
  60. package/lib/module/types/events.js.map +1 -0
  61. package/lib/module/types/react-native-flipper-performance-plugin.d.js +2 -0
  62. package/lib/module/types/react-native-flipper-performance-plugin.d.js.map +1 -0
  63. package/lib/module/types/react-native-globals.d.js +2 -0
  64. package/lib/module/types/react-native-globals.d.js.map +1 -0
  65. package/lib/module/utils/cookieUtils.js +2 -0
  66. package/lib/module/utils/cookieUtils.js.map +1 -0
  67. package/lib/module/utils/debugConfig.js +2 -0
  68. package/lib/module/utils/debugConfig.js.map +1 -0
  69. package/lib/module/utils/debugMenu.js +2 -0
  70. package/lib/module/utils/debugMenu.js.map +1 -0
  71. package/lib/module/utils/errorHandler.js +2 -0
  72. package/lib/module/utils/errorHandler.js.map +1 -0
  73. package/lib/module/utils/logger.js +2 -0
  74. package/lib/module/utils/logger.js.map +1 -0
  75. package/lib/module/utils/systemInfo.js +2 -0
  76. package/lib/module/utils/systemInfo.js.map +1 -0
  77. package/lib/module/utils/webViewStorage.js +2 -0
  78. package/lib/module/utils/webViewStorage.js.map +1 -0
  79. package/lib/module/version.js +2 -0
  80. package/lib/module/version.js.map +1 -0
  81. package/lib/typescript/Chatbotsdk.d.ts +9 -10
  82. package/lib/typescript/Chatbotsdk.d.ts.map +1 -1
  83. package/lib/typescript/FloatingButton.d.ts.map +1 -1
  84. package/lib/typescript/components/DebugButton.d.ts +4 -0
  85. package/lib/typescript/components/DebugButton.d.ts.map +1 -0
  86. package/lib/typescript/components/ErrorBoundary.d.ts +16 -0
  87. package/lib/typescript/components/ErrorBoundary.d.ts.map +1 -0
  88. package/lib/typescript/constants/errorConstants.d.ts +18 -0
  89. package/lib/typescript/constants/errorConstants.d.ts.map +1 -0
  90. package/lib/typescript/hooks/useChatbotEvents.d.ts +17 -0
  91. package/lib/typescript/hooks/useChatbotEvents.d.ts.map +1 -0
  92. package/lib/typescript/index.d.ts +5 -1
  93. package/lib/typescript/index.d.ts.map +1 -1
  94. package/lib/typescript/services/ErrorTrackingService.d.ts +34 -0
  95. package/lib/typescript/services/ErrorTrackingService.d.ts.map +1 -0
  96. package/lib/typescript/types/events.d.ts +22 -0
  97. package/lib/typescript/types/events.d.ts.map +1 -0
  98. package/lib/typescript/utils/cookieUtils.d.ts +2 -0
  99. package/lib/typescript/utils/cookieUtils.d.ts.map +1 -0
  100. package/lib/typescript/utils/debugConfig.d.ts +8 -0
  101. package/lib/typescript/utils/debugConfig.d.ts.map +1 -0
  102. package/lib/typescript/utils/debugMenu.d.ts +3 -0
  103. package/lib/typescript/utils/debugMenu.d.ts.map +1 -0
  104. package/lib/typescript/utils/errorHandler.d.ts +2 -0
  105. package/lib/typescript/utils/errorHandler.d.ts.map +1 -0
  106. package/lib/typescript/utils/logger.d.ts +7 -0
  107. package/lib/typescript/utils/logger.d.ts.map +1 -0
  108. package/lib/typescript/utils/systemInfo.d.ts +19 -0
  109. package/lib/typescript/utils/systemInfo.d.ts.map +1 -0
  110. package/lib/typescript/utils/webViewStorage.d.ts +16 -0
  111. package/lib/typescript/utils/webViewStorage.d.ts.map +1 -0
  112. package/lib/typescript/version.d.ts +2 -0
  113. package/lib/typescript/version.d.ts.map +1 -0
  114. package/package.json +8 -2
  115. package/scripts/generate-version.js +9 -0
  116. package/src/Chatbotsdk.tsx +426 -150
  117. package/src/FloatingButton.tsx +18 -1
  118. package/src/components/DebugButton.tsx +46 -0
  119. package/src/components/ErrorBoundary.tsx +38 -0
  120. package/src/constants/errorConstants.ts +21 -0
  121. package/src/hooks/useChatbotEvents.ts +93 -0
  122. package/src/index.tsx +9 -1
  123. package/src/services/ErrorTrackingService.ts +217 -0
  124. package/src/types/events.ts +25 -0
  125. package/src/types/react-native-flipper-performance-plugin.d.ts +3 -0
  126. package/src/types/react-native-globals.d.ts +10 -0
  127. package/src/utils/cookieUtils.ts +7 -0
  128. package/src/utils/debugConfig.ts +56 -0
  129. package/src/utils/debugMenu.ts +14 -0
  130. package/src/utils/errorHandler.ts +58 -0
  131. package/src/utils/logger.ts +14 -0
  132. package/src/utils/systemInfo.ts +84 -0
  133. package/src/utils/webViewStorage.ts +62 -0
  134. 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,28 +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
  });
381
+ console.log("BASE_CHATBOT_URL SDK_VERSION", BASE_CHATBOT_URL, SDK_VERSION);
141
382
  return `${BASE_CHATBOT_URL}?${params.toString()}`;
142
383
  }
143
384
 
144
- const openWebView = () => {
385
+ // Handle opening the chatbot
386
+ const openWebView = useCallback(() => {
145
387
  setIsWebViewVisible(true);
146
388
  if (enableAnimation) {
147
389
  Animated.timing(fadeAnim, {
@@ -150,8 +392,8 @@ const ChatbotSDK: React.FC<ChatbotSDKProps> = ({
150
392
  useNativeDriver: true,
151
393
  }).start(() => {
152
394
  onOpen?.();
395
+ emitEvent(ChatbotEventType.CHATBOT_OPENED);
153
396
  if (!isFirstLoadRef.current) {
154
- // If not first load, manually trigger initialization
155
397
  setTimeout(triggerAppInit, 100);
156
398
  }
157
399
  });
@@ -159,16 +401,17 @@ const ChatbotSDK: React.FC<ChatbotSDKProps> = ({
159
401
  fadeAnim.setValue(1);
160
402
  requestAnimationFrame(() => {
161
403
  onOpen?.();
404
+ emitEvent(ChatbotEventType.CHATBOT_OPENED);
162
405
  if (!isFirstLoadRef.current) {
163
406
  setTimeout(triggerAppInit, 100);
164
407
  }
165
408
  });
166
409
  }
167
- };
410
+ }, [enableAnimation, fadeAnim, onOpen, emitEvent]);
168
411
 
169
- const closeWebView = () => {
412
+ // Handle closing the chatbot
413
+ const closeWebView = useCallback(() => {
170
414
  if (webViewRef.current) {
171
- console.log("closeWebView");
172
415
  webViewRef.current.postMessage(
173
416
  JSON.stringify({
174
417
  name: "closeFrame",
@@ -180,6 +423,7 @@ const ChatbotSDK: React.FC<ChatbotSDKProps> = ({
180
423
  const complete = () => {
181
424
  setIsWebViewVisible(false);
182
425
  onClose?.();
426
+ emitEvent(ChatbotEventType.CHATBOT_CLOSED);
183
427
  };
184
428
 
185
429
  if (enableAnimation) {
@@ -192,58 +436,14 @@ const ChatbotSDK: React.FC<ChatbotSDKProps> = ({
192
436
  fadeAnim.setValue(0);
193
437
  requestAnimationFrame(complete);
194
438
  }
195
- };
196
-
197
- const handleMessage = (event: WebViewMessageEvent) => {
198
- const { data } = event.nativeEvent;
199
-
200
- try {
201
- const parsedData: ChatbotMessage = JSON.parse(data);
439
+ }, [enableAnimation, fadeAnim, onClose, emitEvent]);
202
440
 
203
- if (parsedData.type === "APP_READY") {
204
- console.log("APP_READY", webViewRef.current && isWebViewVisible);
205
- if (webViewRef.current && isWebViewVisible) {
206
- webViewRef.current.injectJavaScript(`
207
- window.postMessage(${JSON.stringify({
208
- name: "openFrame",
209
- domain: "app-domain.com",
210
- })}, '*');
211
- window.postMessage(${JSON.stringify({
212
- name: "registerUserId",
213
- action: "registerUserId",
214
- domain: "app-domain.com",
215
- data: {
216
- userId: `${user_id}`,
217
- token: `${user_token}`,
218
- user_info: {
219
- ...user_info,
220
- platform: Platform.OS,
221
- SDK_VERSION: "${SDK_VERSION}",
222
- OS: Platform.OS,
223
- browser: Platform.OS,
224
- },
225
- },
226
- })}, '*');
227
- `);
228
- }
229
- }
230
-
231
- if (typeof parsedData !== "object" || !parsedData.type) {
232
- console.warn("Invalid message structure");
233
- return;
234
- }
235
-
236
- switch (parsedData.type) {
237
- case "close_chatbot":
238
- closeWebView();
239
- break;
240
- default:
241
- if (onMessage) onMessage(parsedData);
242
- }
243
- } catch (error) {
244
- 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);
245
445
  }
246
- };
446
+ }, [show_floating_button, chatbotConfig, emitEvent]);
247
447
 
248
448
  const getScreenDimensions = () => {
249
449
  const windowHeight = Dimensions.get("window").height;
@@ -259,60 +459,89 @@ const ChatbotSDK: React.FC<ChatbotSDKProps> = ({
259
459
 
260
460
  const screenDimensions = getScreenDimensions();
261
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
+
262
486
  return (
263
- <View style={styles.mainContainer}>
264
- {!loading && (
265
- <>
266
- {show_floating_button && api_key && chatbotConfig && (
267
- <FloatingButton
268
- onPress={openWebView}
269
- brandColor={chatbotConfig.brand_color}
270
- imageUrl={chatbotConfig.image_url}
271
- />
272
- )}
273
- <View
274
- style={[
275
- styles.webViewWrapper,
276
- { height: isWebViewVisible ? "100%" : 0 },
277
- ]}
278
- pointerEvents={isWebViewVisible ? "auto" : "none"}
279
- >
280
- <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
281
504
  style={[
282
- isFullScreen
283
- ? styles.fullScreenContainer
284
- : styles.inlineContainer,
285
- { opacity: fadeAnim },
505
+ styles.webViewWrapper,
506
+ { height: isWebViewVisible ? "100%" : 0 },
286
507
  ]}
508
+ pointerEvents={isWebViewVisible ? "auto" : "none"}
287
509
  >
288
- <WebView
289
- ref={webViewRef}
290
- source={{ uri: webViewUrl }}
291
- onMessage={handleMessage}
292
- style={styles.webview}
293
- containerStyle={
294
- isFullScreen
295
- ? {
296
- width: screenDimensions.width,
297
- 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);
298
543
  }
299
- : undefined
300
- }
301
- allowsInlineMediaPlayback={true}
302
- mediaPlaybackRequiresUserAction={false}
303
- allowFileAccess={false}
304
- geolocationEnabled={false}
305
- javaScriptEnabled={true}
306
- domStorageEnabled={true}
307
- cacheEnabled={true}
308
- scrollEnabled={true}
309
- bounces={false}
310
- onShouldStartLoadWithRequest={(request) => {
311
- return request.url.startsWith(BASE_CHATBOT_URL);
312
- }}
313
- startInLoadingState={isFirstLoadRef.current}
314
- onLoadEnd={() => {
315
- webViewRef.current?.injectJavaScript(`
544
+ webViewRef.current?.injectJavaScript(`
316
545
  if (!document.querySelector('meta[name="viewport"]')) {
317
546
  var meta = document.createElement('meta');
318
547
  meta.name = 'viewport';
@@ -337,28 +566,75 @@ const ChatbotSDK: React.FC<ChatbotSDKProps> = ({
337
566
  window.ReactNativeWebView.postMessage(JSON.stringify(message));
338
567
  };
339
568
 
340
- var closeButton = document.querySelector('.chatbot-close-button');
341
- if (closeButton) {
342
- closeButton.addEventListener('click', () => {
343
- 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
344
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
+ }
345
608
  }
346
-
609
+
347
610
  true;
348
611
  `);
349
- isFirstLoadRef.current = false;
350
- }}
351
- onError={(syntheticEvent) => {
352
- const { nativeEvent } = syntheticEvent;
353
- console.warn("WebView error:", nativeEvent);
354
- }}
355
- />
356
- </Animated.View>
357
- </View>
358
- </>
359
- )}
360
- {/* <Toast message={toastMessage} visible={toastVisible} duration={10000} /> */}
361
- </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>
362
638
  );
363
639
  };
364
640
 
@@ -395,4 +671,4 @@ const styles = StyleSheet.create({
395
671
  },
396
672
  });
397
673
 
398
- export default ChatbotSDK;
674
+ export default Chatbot;