@robylon/react-native-sdk 2.0.21-dev.2 → 2.0.21-staging.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 (65) hide show
  1. package/.npmignore.development +18 -0
  2. package/.npmignore.production +8 -0
  3. package/.npmignore.staging +7 -0
  4. package/babel.config.js +17 -0
  5. package/lib/commonjs/Chatbotsdk.js +1 -1
  6. package/lib/commonjs/Chatbotsdk.js.map +1 -1
  7. package/lib/commonjs/version.js +1 -1
  8. package/lib/commonjs/versions/version.staging.js +1 -1
  9. package/lib/commonjs/versions/version.staging.js.map +1 -1
  10. package/lib/module/Chatbotsdk.js +1 -1
  11. package/lib/module/Chatbotsdk.js.map +1 -1
  12. package/lib/module/openChatbot.js.map +1 -1
  13. package/lib/module/version.js +1 -1
  14. package/lib/module/versions/version.staging.js +1 -1
  15. package/lib/module/versions/version.staging.js.map +1 -1
  16. package/lib/typescript/Chatbotsdk.d.ts.map +1 -1
  17. package/lib/typescript/version.d.ts +1 -1
  18. package/lib/typescript/version.d.ts.map +1 -1
  19. package/lib/typescript/versions/version.staging.d.ts +1 -1
  20. package/lib/typescript/versions/version.staging.d.ts.map +1 -1
  21. package/package.json +3 -3
  22. package/scripts/create-branch.js +577 -0
  23. package/scripts/create-version-tag.js +29 -0
  24. package/scripts/get-next-version.js +29 -0
  25. package/scripts/husky-setup.js +32 -0
  26. package/scripts/prevent-direct-branch.js +37 -0
  27. package/scripts/publish-version.js +13 -0
  28. package/scripts/release.js +77 -0
  29. package/scripts/setup-git-hooks.js +18 -0
  30. package/scripts/update-version.js +28 -0
  31. package/scripts/validate-branch-name.sh +65 -0
  32. package/scripts/validate-publish.js +48 -0
  33. package/src/ChatbotWebview.tsx +44 -0
  34. package/src/Chatbotsdk.tsx +777 -0
  35. package/src/FloatingButton.tsx +88 -0
  36. package/src/LoadingIndicator.tsx +11 -0
  37. package/src/Toast.tsx +65 -0
  38. package/src/components/DebugButton.tsx +46 -0
  39. package/src/components/ErrorBoundary.tsx +38 -0
  40. package/src/config.ts +4 -0
  41. package/src/constants/errorConstants.ts +21 -0
  42. package/src/constants.ts +4 -0
  43. package/src/global.d.ts +11 -0
  44. package/src/hooks/useChatbotEvents.ts +93 -0
  45. package/src/index.tsx +10 -0
  46. package/src/openChatbot.ts +11 -0
  47. package/src/openChatbot.tsx +0 -0
  48. package/src/services/ErrorTrackingService.ts +217 -0
  49. package/src/types/events.ts +25 -0
  50. package/src/types/react-native-flipper-performance-plugin.d.ts +3 -0
  51. package/src/types/react-native-globals.d.ts +10 -0
  52. package/src/utils/cookieUtils.ts +7 -0
  53. package/src/utils/debugConfig.ts +56 -0
  54. package/src/utils/debugMenu.ts +14 -0
  55. package/src/utils/errorHandler.ts +58 -0
  56. package/src/utils/logger.ts +14 -0
  57. package/src/utils/systemInfo.ts +84 -0
  58. package/src/utils/webViewStorage.ts +62 -0
  59. package/src/version.ts +2 -0
  60. package/src/versions/version.dev.ts +2 -0
  61. package/src/versions/version.production.ts +2 -0
  62. package/src/versions/version.staging.ts +2 -0
  63. package/tsconfig.build.json +16 -0
  64. package/tsconfig.json +26 -0
  65. package/usage/react-native-ios-docs.md +0 -185
@@ -0,0 +1,777 @@
1
+ import React, {
2
+ useState,
3
+ useEffect,
4
+ useRef,
5
+ useMemo,
6
+ useCallback,
7
+ } from "react";
8
+ import {
9
+ View,
10
+ StyleSheet,
11
+ Animated,
12
+ Dimensions,
13
+ Platform,
14
+ StatusBar,
15
+ PixelRatio,
16
+ FlexAlignType,
17
+ } from "react-native";
18
+ import { WebView, WebViewMessageEvent } from "react-native-webview";
19
+ import FloatingButton from "./FloatingButton";
20
+ import { API_URL, BASE_CHATBOT_URL } from "./constants";
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";
42
+
43
+ export interface ChatbotProps {
44
+ api_key: string;
45
+ user_id?: string | null | number;
46
+ user_token?: string;
47
+ user_profile?: {
48
+ email?: string;
49
+ name?: string;
50
+ mobile?: string;
51
+ is_test_user?: boolean;
52
+ };
53
+ onEvent?: ChatbotEventHandler;
54
+ }
55
+
56
+ interface InternalChatbotProps extends ChatbotProps {
57
+ show_floating_button?: boolean;
58
+ onMessage?: (message: any) => void;
59
+ isFullScreen?: boolean;
60
+ enableAnimation?: boolean;
61
+ onOpen?: () => void;
62
+ onClose?: () => void;
63
+ }
64
+
65
+ interface ChatbotMessage {
66
+ type: string;
67
+ data?: any;
68
+ level?: string;
69
+ }
70
+
71
+ export enum ChatbotInterfaceType {
72
+ WIDGET = "WIDGET",
73
+ POPOVER = "POPOVER",
74
+ EMBED = "EMBED",
75
+ }
76
+
77
+ export enum WidgetPositionEnums {
78
+ RIGHT = "Right",
79
+ LEFT = "Left",
80
+ }
81
+
82
+ export interface WidgetInterfaceProperties {
83
+ position: WidgetPositionEnums;
84
+ side_spacing?: number;
85
+ bottom_spacing?: number;
86
+ }
87
+
88
+ export enum LauncherType {
89
+ TEXT = "TEXT",
90
+ IMAGE = "IMAGE",
91
+ TEXTUAL_IMAGE = "TEXTUAL_IMAGE",
92
+ }
93
+
94
+ export interface ChatbotConfig {
95
+ brand_colour: string;
96
+ image_url?: string;
97
+ chat_interface_config?: {
98
+ chat_bubble_prompts?: string[];
99
+ display_name?: string;
100
+ welcome_message?: string;
101
+ redirect_url?: string;
102
+ };
103
+ interface_type?: ChatbotInterfaceType;
104
+ interface_properties?: WidgetInterfaceProperties;
105
+ launcher_type: LauncherType;
106
+ launcher_properties: {
107
+ text?: string;
108
+ };
109
+ images: {
110
+ launcher_image_url?: {
111
+ url: string;
112
+ };
113
+ };
114
+ }
115
+
116
+ const Chatbot: React.FC<InternalChatbotProps> = ({
117
+ api_key,
118
+ user_id,
119
+ user_token,
120
+ show_floating_button = true,
121
+ onEvent,
122
+ onMessage,
123
+ isFullScreen = true,
124
+ enableAnimation = true,
125
+ onOpen,
126
+ onClose,
127
+ user_profile,
128
+ }) => {
129
+ const [isWebViewVisible, setIsWebViewVisible] = useState(false);
130
+ const [chatbotConfig, setChatbotConfig] = useState<ChatbotConfig | null>(
131
+ null
132
+ );
133
+ const [loading, setLoading] = useState(true);
134
+ const webViewRef = useRef<WebView>(null);
135
+ const fadeAnim = useRef(new Animated.Value(0)).current;
136
+ const isFirstLoadRef = useRef(true);
137
+ const [toastVisible, setToastVisible] = useState(false);
138
+ const [toastMessage, setToastMessage] = useState("");
139
+ const [effectiveUserId, setEffectiveUserId] = useState<string>();
140
+ const [isStorageReady, setIsStorageReady] = useState(false);
141
+ const pendingStorageOps = useRef<Array<() => void>>([]);
142
+ const memoryCache = useRef<Record<string, string>>({});
143
+
144
+ const [systemInfo, setSystemInfo] = useState({ os: "", browser: "" });
145
+
146
+ // Create chatbotConfig object for events
147
+ const chatbotConfigForEvents = useMemo(
148
+ () => ({
149
+ userId: effectiveUserId,
150
+ isAnonymous: !user_id && user_id !== 0,
151
+ // Add other config properties as needed
152
+ }),
153
+ [effectiveUserId, user_id]
154
+ );
155
+
156
+ // Initialize event handling
157
+ const { emitEvent, onInternalEvent } = useChatbotEvents({
158
+ api_key,
159
+ chatbotConfig: chatbotConfigForEvents ?? {},
160
+ user_profile: user_profile,
161
+ onEvent: onEvent,
162
+ systemInfo: systemInfo ?? { os: "", browser: "" },
163
+ });
164
+
165
+ const triggerAppInit = () => {
166
+ if (webViewRef.current) {
167
+ webViewRef.current.injectJavaScript(`
168
+ // Trigger APP_READY equivalent
169
+ window.ReactNativeWebView.postMessage(JSON.stringify({ type: 'APP_READY' }));
170
+ true;
171
+ `);
172
+ }
173
+ };
174
+
175
+ const executeStorageOp = useCallback(
176
+ (operation: () => void) => {
177
+ if (isStorageReady) {
178
+ operation();
179
+ } else {
180
+ pendingStorageOps.current.push(operation);
181
+ }
182
+ },
183
+ [isStorageReady]
184
+ );
185
+
186
+ // Handle WebView messages
187
+ const handleMessage = useCallback(
188
+ (event: WebViewMessageEvent) => {
189
+ const { data } = event.nativeEvent;
190
+
191
+ try {
192
+ const parsedData: ChatbotMessage = JSON.parse(data);
193
+
194
+ // Handle storage messages
195
+ if (
196
+ Object.values(StorageMessageType).includes(
197
+ parsedData.type as StorageMessageType
198
+ )
199
+ ) {
200
+ const storageMessage = parsedData as StorageMessage;
201
+ switch (storageMessage.type) {
202
+ case StorageMessageType.STORAGE_READY:
203
+ setIsStorageReady(true);
204
+ pendingStorageOps.current.forEach((op) => op());
205
+ pendingStorageOps.current = [];
206
+ // Try to get stored ID immediately after storage is ready
207
+ webViewRef.current?.injectJavaScript(
208
+ createStorageMessage(
209
+ StorageMessageType.GET_STORAGE,
210
+ "rblyn_anon"
211
+ )
212
+ );
213
+ break;
214
+ case StorageMessageType.STORAGE_RESPONSE:
215
+ if (storageMessage.key && storageMessage.value) {
216
+ memoryCache.current[storageMessage.key] = storageMessage.value;
217
+ if (storageMessage.key === "rblyn_anon") {
218
+ setEffectiveUserId(storageMessage.value);
219
+ }
220
+ }
221
+ break;
222
+ case StorageMessageType.STORAGE_ERROR:
223
+ logger.error("WebView storage error:", storageMessage.error);
224
+ break;
225
+ }
226
+ return;
227
+ }
228
+
229
+ // Add debug logging
230
+ if (__DEV__) {
231
+ if (parsedData?.type === "CONSOLE") {
232
+ console.log(`WebView ${parsedData?.level}:`, parsedData?.data);
233
+ return;
234
+ }
235
+ if (parsedData?.type === "ERROR") {
236
+ console.error("WebView Error:", parsedData?.data);
237
+ return;
238
+ }
239
+ }
240
+
241
+ if (parsedData?.type === "SYSTEM_INFO") {
242
+ setSystemInfo(parsedData?.data ?? { os: "", browser: "" });
243
+ return;
244
+ }
245
+
246
+ if (parsedData?.type === "APP_READY") {
247
+ emitEvent(ChatbotEventType.CHATBOT_APP_READY);
248
+ if (webViewRef.current && isWebViewVisible) {
249
+ webViewRef.current?.injectJavaScript(getBrowserAndOSInfoScript());
250
+
251
+ const messageData = {
252
+ name: "registerUserId",
253
+ action: "registerUserId",
254
+ data: {
255
+ userId: effectiveUserId,
256
+ token: user_token ? `${user_token}` : undefined,
257
+ userProfile: {
258
+ ...user_profile,
259
+ ...getSystemInfo(systemInfo),
260
+ },
261
+ },
262
+ };
263
+ logger.debug("messageData====>", JSON.stringify(messageData));
264
+ webViewRef.current?.injectJavaScript(`
265
+ window.postMessage(${JSON.stringify({
266
+ name: "openFrame",
267
+ domain: "app-domain.com",
268
+ })}, '*');
269
+ window.postMessage(${JSON.stringify(messageData)}, '*');
270
+ `);
271
+ }
272
+ }
273
+
274
+ // Handle other message types
275
+ switch (parsedData?.type) {
276
+ case "close_chatbot":
277
+ closeWebView();
278
+ break;
279
+ case "CHATBOT_LOADED":
280
+ emitEvent(ChatbotEventType.CHATBOT_LOADED, parsedData?.data);
281
+ break;
282
+ case "CHAT_INITIALIZED":
283
+ emitEvent(ChatbotEventType.CHAT_INITIALIZED, parsedData?.data);
284
+ break;
285
+ case "SESSION_REFRESHED":
286
+ emitEvent(ChatbotEventType.SESSION_REFRESHED, parsedData?.data);
287
+ break;
288
+ case "CHAT_INITIALIZATION_FAILED":
289
+ emitEvent(
290
+ ChatbotEventType.CHAT_INITIALIZATION_FAILED,
291
+ parsedData?.data
292
+ );
293
+ break;
294
+ default:
295
+ if (onMessage) onMessage(parsedData ?? {});
296
+ }
297
+ } catch (error) {
298
+ errorTracker.trackError(
299
+ error instanceof Error
300
+ ? error
301
+ : new Error("Failed to parse WebView message"),
302
+ "ChatbotSDK",
303
+ {
304
+ type: ErrorTypes.RUNTIME_ERROR,
305
+ context: { messageData: data },
306
+ }
307
+ );
308
+ }
309
+ },
310
+ [
311
+ emitEvent,
312
+ isWebViewVisible,
313
+ onMessage,
314
+ systemInfo,
315
+ effectiveUserId,
316
+ user_token,
317
+ user_profile,
318
+ ]
319
+ );
320
+
321
+ // Initialize user ID
322
+ useEffect(() => {
323
+ const initializeUserId = async () => {
324
+ let userId = user_id ? String(user_id) : undefined;
325
+
326
+ if (!userId) {
327
+ // Try memory cache first
328
+ userId = memoryCache.current["rblyn_anon"];
329
+
330
+ if (!userId) {
331
+ userId = generateUUID();
332
+ executeStorageOp(() => {
333
+ webViewRef.current?.injectJavaScript(
334
+ createStorageMessage(
335
+ StorageMessageType.SET_STORAGE,
336
+ "rblyn_anon",
337
+ userId!
338
+ )
339
+ );
340
+ });
341
+ }
342
+ }
343
+
344
+ setEffectiveUserId(userId);
345
+ };
346
+
347
+ initializeUserId();
348
+ }, [user_id, executeStorageOp]);
349
+
350
+ // Initialize WebView storage
351
+ useEffect(() => {
352
+ if (webViewRef.current) {
353
+ webViewRef.current.injectJavaScript(getStorageScript());
354
+ // Try to get stored ID once storage is ready
355
+ executeStorageOp(() => {
356
+ webViewRef.current?.injectJavaScript(
357
+ createStorageMessage(StorageMessageType.GET_STORAGE, "rblyn_anon")
358
+ );
359
+ });
360
+ }
361
+ }, []);
362
+
363
+ useEffect(() => {
364
+ const fetchChatbotConfig = async () => {
365
+ try {
366
+ const endpointUrl = `${API_URL}/chat/chatbot/get/`;
367
+ const payload = {
368
+ client_user_id: effectiveUserId,
369
+ org_id: api_key,
370
+ token: user_token,
371
+ extra_info: {},
372
+ };
373
+
374
+ // setToastMessage(endpointUrl + " : " + BASE_CHATBOT_URL);
375
+ // setToastVisible(true);
376
+
377
+ const response = await fetch(endpointUrl, {
378
+ method: "POST",
379
+ headers: {
380
+ "Content-Type": "application/json",
381
+ },
382
+ body: JSON.stringify(payload),
383
+ });
384
+
385
+ if (!response.ok) {
386
+ throw new Error("Failed to fetch chatbot configuration");
387
+ }
388
+
389
+ const data = await response.json();
390
+ const orgInfo = data?.user?.org_info;
391
+
392
+ if (orgInfo) {
393
+ const config: ChatbotConfig = {
394
+ brand_colour: orgInfo.brand_config?.colors?.brand_color || "",
395
+ image_url: orgInfo.brand_config?.launcher_logo_url || "",
396
+ chat_interface_config: {
397
+ chat_bubble_prompts: [],
398
+ display_name: orgInfo.brand_config?.display_name || "",
399
+ welcome_message:
400
+ orgInfo.brand_config?.welcome_message ||
401
+ "Hey! What can we help you with today?",
402
+ redirect_url: orgInfo.brand_config?.redirect_url || "",
403
+ },
404
+ interface_properties: {
405
+ position:
406
+ orgInfo.brand_config?.interface_properties?.position ||
407
+ WidgetPositionEnums.RIGHT,
408
+ side_spacing:
409
+ orgInfo.brand_config?.interface_properties?.side_spacing ?? 20,
410
+ bottom_spacing:
411
+ orgInfo.brand_config?.interface_properties?.bottom_spacing ??
412
+ 20,
413
+ },
414
+ interface_type:
415
+ orgInfo.brand_config?.interface_type ||
416
+ ChatbotInterfaceType.WIDGET,
417
+ launcher_type:
418
+ orgInfo.brand_config?.launcher_type || LauncherType.IMAGE,
419
+ launcher_properties: {
420
+ text: orgInfo.brand_config?.launcher_properties?.text || "",
421
+ },
422
+ images: {
423
+ launcher_image_url: {
424
+ url:
425
+ orgInfo.brand_config?.images?.launcher_image_url?.url || "",
426
+ },
427
+ },
428
+ };
429
+ setChatbotConfig(config);
430
+ }
431
+ } catch (error) {
432
+ errorTracker.trackError(
433
+ error instanceof Error
434
+ ? error
435
+ : new Error("Failed to fetch chatbot configuration"),
436
+ "ChatbotSDK",
437
+ {
438
+ type: ErrorTypes.NETWORK_ERROR,
439
+ context: { api_key, user_id },
440
+ }
441
+ );
442
+ } finally {
443
+ setLoading(false);
444
+ }
445
+ };
446
+
447
+ if (api_key && effectiveUserId) {
448
+ fetchChatbotConfig();
449
+ }
450
+ }, [api_key, effectiveUserId, user_token]);
451
+
452
+ function constructUrl(): string {
453
+ if (!effectiveUserId) return "";
454
+ const params = new URLSearchParams({
455
+ id: api_key,
456
+ });
457
+ console.log("BASE_CHATBOT_URL SDK_VERSION", BASE_CHATBOT_URL);
458
+ return `${BASE_CHATBOT_URL}?${params.toString()}`;
459
+ }
460
+
461
+ // Handle opening the chatbot
462
+ const openWebView = useCallback(() => {
463
+ setIsWebViewVisible(true);
464
+ if (enableAnimation) {
465
+ Animated.timing(fadeAnim, {
466
+ toValue: 1,
467
+ duration: 250,
468
+ useNativeDriver: true,
469
+ }).start(() => {
470
+ onOpen?.();
471
+ emitEvent(ChatbotEventType.CHATBOT_OPENED);
472
+ if (!isFirstLoadRef.current) {
473
+ setTimeout(triggerAppInit, 100);
474
+ }
475
+ });
476
+ } else {
477
+ fadeAnim.setValue(1);
478
+ requestAnimationFrame(() => {
479
+ onOpen?.();
480
+ emitEvent(ChatbotEventType.CHATBOT_OPENED);
481
+ if (!isFirstLoadRef.current) {
482
+ setTimeout(triggerAppInit, 100);
483
+ }
484
+ });
485
+ }
486
+ }, [enableAnimation, fadeAnim, onOpen, emitEvent]);
487
+
488
+ // Handle closing the chatbot
489
+ const closeWebView = useCallback(() => {
490
+ if (webViewRef.current) {
491
+ webViewRef.current.postMessage(
492
+ JSON.stringify({
493
+ name: "closeFrame",
494
+ domain: "app-domain.com",
495
+ })
496
+ );
497
+ }
498
+
499
+ const complete = () => {
500
+ setIsWebViewVisible(false);
501
+ onClose?.();
502
+ emitEvent(ChatbotEventType.CHATBOT_CLOSED);
503
+ };
504
+
505
+ if (enableAnimation) {
506
+ Animated.timing(fadeAnim, {
507
+ toValue: 0,
508
+ duration: 200,
509
+ useNativeDriver: true,
510
+ }).start(complete);
511
+ } else {
512
+ fadeAnim.setValue(0);
513
+ requestAnimationFrame(complete);
514
+ }
515
+ }, [enableAnimation, fadeAnim, onClose, emitEvent]);
516
+
517
+ // Emit button loaded event when component mounts
518
+ useEffect(() => {
519
+ if (show_floating_button && chatbotConfig) {
520
+ emitEvent(ChatbotEventType.CHATBOT_BUTTON_LOADED);
521
+ }
522
+ }, [show_floating_button, chatbotConfig, emitEvent]);
523
+
524
+ const getScreenDimensions = () => {
525
+ const windowHeight = Dimensions.get("window").height;
526
+ const windowWidth = Dimensions.get("window").width;
527
+ const statusBarHeight =
528
+ Platform.OS === "ios" ? 0 : StatusBar.currentHeight || 0;
529
+
530
+ return {
531
+ height: windowHeight - statusBarHeight,
532
+ width: windowWidth,
533
+ };
534
+ };
535
+
536
+ const screenDimensions = getScreenDimensions();
537
+
538
+ useEffect(() => {
539
+ if (__DEV__) {
540
+ enableNetworkDebug();
541
+ }
542
+ }, []);
543
+
544
+ // Deliberate error for testing error boundary
545
+ // useEffect(() => {
546
+ // if (__DEV__) {
547
+ // // Set up global error handlers
548
+ // setupGlobalErrorHandlers();
549
+
550
+ // // Test async error
551
+ // setTimeout(() => {
552
+ // Promise.reject(new Error("Test async error"));
553
+ // }, 1000);
554
+
555
+ // // Test sync error
556
+ // setTimeout(() => {
557
+ // throw new Error("Test sync error");
558
+ // }, 2000);
559
+ // }
560
+ // }, []);
561
+
562
+ const position = chatbotConfig?.interface_properties?.position || "Right";
563
+ const sideSpacing = chatbotConfig?.interface_properties?.side_spacing ?? 20;
564
+ const bottomSpacing =
565
+ chatbotConfig?.interface_properties?.bottom_spacing ?? 20;
566
+
567
+ const mainContainerStyle = useMemo(() => {
568
+ return {
569
+ ...styles.mainContainer,
570
+ [position.toLowerCase()]: `${sideSpacing ?? 20}px`,
571
+ alignItems:
572
+ position === "Right" ? "flex-end" : ("flex-start" as FlexAlignType),
573
+ // Explicitly clear mobile-specific styles
574
+ top: undefined,
575
+ [position === "Right" ? "left" : "right"]: undefined,
576
+ };
577
+ }, [position, sideSpacing, styles.mainContainer]);
578
+
579
+ console.log("MAINCONTAINERSTYLE", mainContainerStyle, position, sideSpacing);
580
+
581
+ return (
582
+ <ErrorBoundary componentName="ChatbotSDK">
583
+ <View style={mainContainerStyle}>
584
+ {!loading && (
585
+ <>
586
+ <ErrorBoundary componentName="FloatingButton">
587
+ {show_floating_button && api_key && chatbotConfig && (
588
+ <FloatingButton
589
+ isWebViewVisible={isWebViewVisible}
590
+ onPress={() => {
591
+ emitEvent(ChatbotEventType.CHATBOT_BUTTON_CLICKED);
592
+ openWebView();
593
+ }}
594
+ brandColor={chatbotConfig.brand_colour}
595
+ imageUrl={chatbotConfig.image_url}
596
+ />
597
+ )}
598
+ </ErrorBoundary>
599
+ <View
600
+ style={[
601
+ styles.webViewWrapper,
602
+ { height: isWebViewVisible ? "100%" : 0 },
603
+ ]}
604
+ pointerEvents={isWebViewVisible ? "auto" : "none"}
605
+ >
606
+ <Animated.View
607
+ style={[styles.fullScreenContainer, { opacity: 1 }]}
608
+ >
609
+ <ErrorBoundary componentName="ChatbotWebView">
610
+ <WebView
611
+ ref={webViewRef}
612
+ source={{
613
+ uri: constructUrl(),
614
+ }}
615
+ onMessage={handleMessage}
616
+ style={styles.webview}
617
+ containerStyle={
618
+ isFullScreen
619
+ ? {
620
+ width: screenDimensions.width,
621
+ height: screenDimensions.height - 40,
622
+ }
623
+ : undefined
624
+ }
625
+ allowsInlineMediaPlayback={true}
626
+ mediaPlaybackRequiresUserAction={false}
627
+ allowFileAccess={false}
628
+ geolocationEnabled={false}
629
+ javaScriptEnabled={true}
630
+ domStorageEnabled={true}
631
+ cacheEnabled={true}
632
+ scrollEnabled={true}
633
+ bounces={false}
634
+ onShouldStartLoadWithRequest={(request) => {
635
+ return request.url.startsWith(BASE_CHATBOT_URL);
636
+ }}
637
+ startInLoadingState={isFirstLoadRef.current}
638
+ onLoadEnd={() => {
639
+ if (__DEV__) {
640
+ enableWebViewDebug(webViewRef);
641
+ }
642
+ webViewRef.current?.injectJavaScript(`
643
+ if (!document.querySelector('meta[name="viewport"]')) {
644
+ var meta = document.createElement('meta');
645
+ meta.name = 'viewport';
646
+ meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no';
647
+ document.getElementsByTagName('head')[0].appendChild(meta);
648
+ }
649
+
650
+ if (!document.querySelector('#injectedStyle')) {
651
+ const style = document.createElement('style');
652
+ style.id = 'injectedStyle';
653
+ style.textContent = \`
654
+ html, body, #root, #__next {
655
+ height: 100% !important;
656
+ min-height: 100% !important;
657
+ overflow: hidden !important;
658
+ }
659
+ \`;
660
+ document.head.appendChild(style);
661
+ }
662
+
663
+ window.sendToChatbot = function(message) {
664
+ window.ReactNativeWebView.postMessage(JSON.stringify(message));
665
+ };
666
+
667
+ // Only create observer if it doesn't exist
668
+ if (!window.closeButtonObserver) {
669
+ // Setup mutation observer to watch for close button
670
+ window.closeButtonObserver = new MutationObserver(function(mutations) {
671
+ const closeButton = document.querySelector('.chatbot-close-button');
672
+ if (closeButton && !closeButton.hasAttribute('listener-attached')) {
673
+ closeButton.setAttribute('listener-attached', 'true');
674
+ closeButton.addEventListener('click', () => {
675
+ try {
676
+ const message = { type: 'close_chatbot' };
677
+ window.ReactNativeWebView.postMessage(JSON.stringify(message));
678
+ } catch (error) {
679
+ console.error('Error sending close message:', error);
680
+ }
681
+ });
682
+ console.log('Close button listener attached');
683
+ }
684
+ });
685
+
686
+ // Start observing
687
+ window.closeButtonObserver.observe(document.body, {
688
+ childList: true,
689
+ subtree: true
690
+ });
691
+
692
+ // Initial check for existing button
693
+ const existingButton = document.querySelector('.chatbot-close-button');
694
+ if (existingButton && !existingButton.hasAttribute('listener-attached')) {
695
+ existingButton.setAttribute('listener-attached', 'true');
696
+ existingButton.addEventListener('click', () => {
697
+ try {
698
+ const message = { type: 'close_chatbot' };
699
+ window.ReactNativeWebView.postMessage(JSON.stringify(message));
700
+ } catch (error) {
701
+ console.error('Error sending close message:', error);
702
+ }
703
+ });
704
+ console.log('Close button listener attached to existing button');
705
+ }
706
+ }
707
+
708
+ true;
709
+ `);
710
+ isFirstLoadRef.current = false;
711
+ }}
712
+ onError={(syntheticEvent) => {
713
+ const { nativeEvent } = syntheticEvent;
714
+ errorTracker.trackError(
715
+ new Error(nativeEvent.description),
716
+ "ChatbotWebView",
717
+ {
718
+ type: ErrorTypes.RUNTIME_ERROR,
719
+ context: {
720
+ url: nativeEvent.url,
721
+ code: nativeEvent.code,
722
+ },
723
+ }
724
+ );
725
+ }}
726
+ />
727
+ </ErrorBoundary>
728
+ </Animated.View>
729
+ </View>
730
+ </>
731
+ )}
732
+ {/* <Toast message={toastMessage} visible={toastVisible} duration={10000} /> */}
733
+ {__DEV__ && <DebugButton />}
734
+ </View>
735
+ </ErrorBoundary>
736
+ );
737
+ };
738
+
739
+ const styles = StyleSheet.create({
740
+ mainContainer: {
741
+ flex: 1,
742
+ position: "relative",
743
+ overflow: "visible",
744
+ },
745
+ webViewWrapper: {
746
+ position: "absolute",
747
+ top: 0,
748
+ left: 0,
749
+ right: 0,
750
+ bottom: 0,
751
+ overflow: "visible",
752
+ zIndex: 10000,
753
+ backgroundColor: "#ffffff",
754
+ },
755
+ fullScreenContainer: {
756
+ position: "absolute",
757
+ top: 0,
758
+ left: 0,
759
+ right: 0,
760
+ bottom: 0,
761
+ backgroundColor: "white",
762
+ zIndex: 10000,
763
+ overflow: "visible",
764
+ // height: Platform.OS === "android" ? "100%" : "100%",
765
+ },
766
+ inlineContainer: {
767
+ flex: 1,
768
+ backgroundColor: "white",
769
+ },
770
+ webview: {
771
+ flex: 1,
772
+ backgroundColor: "white",
773
+ zIndex: 10000,
774
+ },
775
+ });
776
+
777
+ export default Chatbot;