@robylon/react-native-sdk 2.0.30 → 2.0.31-staging.0
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.
- package/lib/commonjs/Chatbotsdk.js +2 -2
- package/lib/commonjs/Chatbotsdk.js.map +1 -1
- package/lib/commonjs/FloatingButton/launchers/ImageLauncher.js +1 -1
- package/lib/commonjs/FloatingButton/launchers/ImageLauncher.js.map +1 -1
- package/lib/commonjs/FloatingButton/launchers/TextLauncher.js +1 -1
- package/lib/commonjs/FloatingButton/launchers/TextLauncher.js.map +1 -1
- package/lib/commonjs/FloatingButton/launchers/TextualImageLauncher.js +1 -1
- package/lib/commonjs/FloatingButton/launchers/TextualImageLauncher.js.map +1 -1
- package/lib/commonjs/FloatingButton.js +1 -1
- package/lib/commonjs/FloatingButton.js.map +1 -1
- package/lib/commonjs/LoadingIndicator.js +1 -1
- package/lib/commonjs/LoadingIndicator.js.map +1 -1
- package/lib/commonjs/Toast.js +1 -1
- package/lib/commonjs/Toast.js.map +1 -1
- package/lib/commonjs/components/DebugButton.js +1 -1
- package/lib/commonjs/components/DebugButton.js.map +1 -1
- package/lib/commonjs/config.js +1 -1
- package/lib/commonjs/config.js.map +1 -1
- package/lib/commonjs/constants.js +1 -1
- package/lib/commonjs/constants.js.map +1 -1
- package/lib/commonjs/utils/fileDownload.js +1 -1
- package/lib/commonjs/utils/fileDownload.js.map +1 -1
- package/lib/commonjs/versions/version.staging.js +1 -1
- package/lib/module/Chatbotsdk.js +2 -2
- package/lib/module/Chatbotsdk.js.map +1 -1
- package/lib/module/FloatingButton/launchers/ImageLauncher.js +1 -1
- package/lib/module/FloatingButton/launchers/ImageLauncher.js.map +1 -1
- package/lib/module/FloatingButton/launchers/TextLauncher.js +1 -1
- package/lib/module/FloatingButton/launchers/TextLauncher.js.map +1 -1
- package/lib/module/FloatingButton/launchers/TextualImageLauncher.js +1 -1
- package/lib/module/FloatingButton/launchers/TextualImageLauncher.js.map +1 -1
- package/lib/module/FloatingButton.js +1 -1
- package/lib/module/FloatingButton.js.map +1 -1
- package/lib/module/LoadingIndicator.js +1 -1
- package/lib/module/LoadingIndicator.js.map +1 -1
- package/lib/module/Toast.js +1 -1
- package/lib/module/Toast.js.map +1 -1
- package/lib/module/components/DebugButton.js +1 -1
- package/lib/module/components/DebugButton.js.map +1 -1
- package/lib/module/config.js +1 -1
- package/lib/module/config.js.map +1 -1
- package/lib/module/constants.js +1 -1
- package/lib/module/constants.js.map +1 -1
- package/lib/module/utils/fileDownload.js +1 -1
- package/lib/module/utils/fileDownload.js.map +1 -1
- package/lib/module/versions/version.staging.js +1 -1
- package/lib/typescript/Chatbotsdk.d.ts.map +1 -1
- package/lib/typescript/utils/fileDownload.d.ts +3 -2
- package/lib/typescript/utils/fileDownload.d.ts.map +1 -1
- package/lib/typescript/versions/version.staging.d.ts +1 -1
- package/package.json +6 -3
- package/src/Chatbotsdk.tsx +946 -0
- package/src/FloatingButton/launchers/ImageLauncher.tsx +75 -0
- package/src/FloatingButton/launchers/TextLauncher.tsx +84 -0
- package/src/FloatingButton/launchers/TextualImageLauncher.tsx +133 -0
- package/src/FloatingButton.tsx +97 -0
- package/src/LoadingIndicator.tsx +11 -0
- package/src/Toast.tsx +65 -0
- package/src/components/DebugButton.tsx +46 -0
- package/src/components/ErrorBoundary.tsx +38 -0
- package/src/config.ts +4 -0
- package/src/constants/errorConstants.ts +21 -0
- package/src/constants/fontStyles.ts +3 -0
- package/src/constants.ts +5 -0
- package/src/global.d.ts +11 -0
- package/src/hooks/useChatbotEvents.ts +93 -0
- package/src/index.tsx +10 -0
- package/src/openChatbot.ts +11 -0
- package/src/openChatbot.tsx +0 -0
- package/src/services/ErrorTrackingService.ts +217 -0
- package/src/types/events.ts +28 -0
- package/src/types/react-native-flipper-performance-plugin.d.ts +3 -0
- package/src/types/react-native-globals.d.ts +10 -0
- package/src/utils/animations.ts +120 -0
- package/src/utils/colorUtils.ts +35 -0
- package/src/utils/cookieUtils.ts +7 -0
- package/src/utils/debugConfig.ts +56 -0
- package/src/utils/debugMenu.ts +14 -0
- package/src/utils/errorHandler.ts +58 -0
- package/src/utils/fileDownload.ts +172 -0
- package/src/utils/logger.ts +14 -0
- package/src/utils/systemInfo.ts +110 -0
- package/src/utils/webViewStorage.ts +62 -0
- package/src/version.ts +2 -0
- package/src/versions/version.dev.ts +2 -0
- package/src/versions/version.production.ts +2 -0
- package/src/versions/version.staging.ts +2 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File: src/utils/fileDownload.ts
|
|
3
|
+
* Utility functions for handling file downloads
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Linking, Platform, Share } from "react-native";
|
|
7
|
+
import ReactNativeBlobUtil from "react-native-blob-util";
|
|
8
|
+
import { logger } from "./logger";
|
|
9
|
+
|
|
10
|
+
export interface DownloadRequest {
|
|
11
|
+
url: string;
|
|
12
|
+
filename?: string;
|
|
13
|
+
inPlace?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const normalizeUrl = (url: string): string => {
|
|
17
|
+
return url?.trim();
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const sanitizeFileName = (filename?: string): string => {
|
|
21
|
+
const fallbackName = `chat-transcript-${Date.now()}.pdf`;
|
|
22
|
+
const trimmedName = filename?.trim();
|
|
23
|
+
if (!trimmedName) return fallbackName;
|
|
24
|
+
return trimmedName.replace(/[<>:"/\\|?*\x00-\x1F]/g, "_");
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const isValidHttpUrl = (url: string): boolean => {
|
|
28
|
+
return /^https?:\/\//i.test(url);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const openUrlWithFallback = async (url: string): Promise<void> => {
|
|
32
|
+
try {
|
|
33
|
+
await Linking.openURL(url);
|
|
34
|
+
return;
|
|
35
|
+
} catch (primaryError) {
|
|
36
|
+
logger.warn("Primary URL open failed, trying browser fallback", {
|
|
37
|
+
url,
|
|
38
|
+
primaryError:
|
|
39
|
+
primaryError instanceof Error
|
|
40
|
+
? primaryError?.message
|
|
41
|
+
: String(primaryError),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
await Linking.openURL(`https://docs.google.com/viewer?url=${encodeURIComponent(url)}`);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const downloadOnAndroid = async (url: string, filename: string): Promise<void> => {
|
|
49
|
+
const { fs, config } = ReactNativeBlobUtil;
|
|
50
|
+
const downloadPath = `${fs.dirs.DownloadDir}/${filename}`;
|
|
51
|
+
await config({
|
|
52
|
+
fileCache: true,
|
|
53
|
+
path: downloadPath,
|
|
54
|
+
addAndroidDownloads: {
|
|
55
|
+
useDownloadManager: true,
|
|
56
|
+
notification: true,
|
|
57
|
+
title: filename,
|
|
58
|
+
description: "Downloading transcript",
|
|
59
|
+
mime: "application/pdf",
|
|
60
|
+
mediaScannable: true,
|
|
61
|
+
path: downloadPath,
|
|
62
|
+
},
|
|
63
|
+
}).fetch("GET", url);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const downloadOniOS = async (url: string, filename: string): Promise<void> => {
|
|
67
|
+
const { fs, config } = ReactNativeBlobUtil;
|
|
68
|
+
const iosPath = `${fs.dirs.DocumentDir}/${filename}`;
|
|
69
|
+
await config({
|
|
70
|
+
fileCache: true,
|
|
71
|
+
path: iosPath,
|
|
72
|
+
}).fetch("GET", url);
|
|
73
|
+
await Share.share({
|
|
74
|
+
url: `file://${iosPath}`,
|
|
75
|
+
message: filename,
|
|
76
|
+
title: filename,
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const downloadWithNativeStorage = async (
|
|
81
|
+
url: string,
|
|
82
|
+
filename?: string
|
|
83
|
+
): Promise<void> => {
|
|
84
|
+
const safeName = sanitizeFileName(filename);
|
|
85
|
+
if (Platform.OS === "android") {
|
|
86
|
+
await downloadOnAndroid(url, safeName);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
await downloadOniOS(url, safeName);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const shouldUseInPlaceDownload = (downloadRequest?: DownloadRequest): boolean => {
|
|
93
|
+
return downloadRequest?.inPlace === true;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Downloads a file using native storage when possible
|
|
98
|
+
* @param url - The URL of the file to download
|
|
99
|
+
* @param filename - Optional filename for the download
|
|
100
|
+
*/
|
|
101
|
+
export const downloadFile = async (
|
|
102
|
+
url: string,
|
|
103
|
+
filename?: string,
|
|
104
|
+
inPlace?: boolean
|
|
105
|
+
): Promise<void> => {
|
|
106
|
+
try {
|
|
107
|
+
const normalizedUrl = normalizeUrl(url);
|
|
108
|
+
logger.debug("Initiating file download", { url: normalizedUrl, filename });
|
|
109
|
+
|
|
110
|
+
// Validate URL
|
|
111
|
+
if (!normalizedUrl || typeof normalizedUrl !== "string") {
|
|
112
|
+
throw new Error("Invalid URL provided for download");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (!isValidHttpUrl(normalizedUrl)) {
|
|
116
|
+
throw new Error("Download URL must be an http/https URL");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (inPlace) {
|
|
120
|
+
try {
|
|
121
|
+
await downloadWithNativeStorage(normalizedUrl, filename);
|
|
122
|
+
logger.debug("File downloaded with native storage flow");
|
|
123
|
+
} catch (nativeDownloadError) {
|
|
124
|
+
logger.warn("Native download failed, falling back to URL open", {
|
|
125
|
+
url: normalizedUrl,
|
|
126
|
+
nativeDownloadError:
|
|
127
|
+
nativeDownloadError instanceof Error
|
|
128
|
+
? nativeDownloadError?.message
|
|
129
|
+
: String(nativeDownloadError),
|
|
130
|
+
});
|
|
131
|
+
await openUrlWithFallback(normalizedUrl);
|
|
132
|
+
logger.debug("File download opened with fallback browser flow");
|
|
133
|
+
}
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
await openUrlWithFallback(normalizedUrl);
|
|
138
|
+
logger.debug("File download opened with legacy URL flow");
|
|
139
|
+
} catch (error) {
|
|
140
|
+
logger.error("File download failed", {
|
|
141
|
+
url,
|
|
142
|
+
filename,
|
|
143
|
+
error: error instanceof Error ? error?.message : String(error),
|
|
144
|
+
});
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Handles download request from WebView post message
|
|
151
|
+
* @param downloadRequest - The download request object
|
|
152
|
+
*/
|
|
153
|
+
export const handleDownloadRequest = async (
|
|
154
|
+
downloadRequest: DownloadRequest
|
|
155
|
+
): Promise<void> => {
|
|
156
|
+
const { url, filename } = downloadRequest;
|
|
157
|
+
|
|
158
|
+
if (!url) {
|
|
159
|
+
logger.error("Download request missing URL");
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
await downloadFile(url, filename, shouldUseInPlaceDownload(downloadRequest));
|
|
165
|
+
} catch (error) {
|
|
166
|
+
logger.error("Failed to handle download request", {
|
|
167
|
+
downloadRequest,
|
|
168
|
+
error,
|
|
169
|
+
});
|
|
170
|
+
// Don't re-throw to avoid breaking the WebView message handling
|
|
171
|
+
}
|
|
172
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const logger = {
|
|
2
|
+
error: (message: string, error?: any) => {
|
|
3
|
+
console.error(message, error);
|
|
4
|
+
},
|
|
5
|
+
info: (message: string, data?: any) => {
|
|
6
|
+
console.info(message, data);
|
|
7
|
+
},
|
|
8
|
+
warn: (message: string, data?: any) => {
|
|
9
|
+
console.warn(message, data);
|
|
10
|
+
},
|
|
11
|
+
debug: (message: string, data?: any) => {
|
|
12
|
+
console.log(message, data);
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Platform, Dimensions, PixelRatio } from "react-native";
|
|
2
|
+
import { SDK_VERSION } from "../version";
|
|
3
|
+
|
|
4
|
+
interface ScreenSize {
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface SystemInfo {
|
|
10
|
+
platform: string;
|
|
11
|
+
os: string;
|
|
12
|
+
browser: string;
|
|
13
|
+
browser_version: string | null;
|
|
14
|
+
browser_language: string | null;
|
|
15
|
+
sdk_version: string;
|
|
16
|
+
device: string;
|
|
17
|
+
screen_size: ScreenSize;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Get device type based on platform and screen dimensions
|
|
21
|
+
const getDeviceType = (): string => {
|
|
22
|
+
const { width, height } = Dimensions.get("window");
|
|
23
|
+
const aspectRatio = height / width;
|
|
24
|
+
|
|
25
|
+
if (Platform.OS === "ios") {
|
|
26
|
+
return Platform.isPad ? "iPad" : "iPhone";
|
|
27
|
+
} else if (Platform.OS === "android") {
|
|
28
|
+
return aspectRatio < 1.6 ? "Android Tablet" : "Android Phone";
|
|
29
|
+
}
|
|
30
|
+
return "Desktop";
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Get screen dimensions with pixel ratio
|
|
34
|
+
const getScreenSize = (): ScreenSize => {
|
|
35
|
+
return {
|
|
36
|
+
width: Math.round(Dimensions.get("window").width * PixelRatio.get()),
|
|
37
|
+
height: Math.round(Dimensions.get("window").height * PixelRatio.get()),
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Get browser detection script
|
|
42
|
+
export const getBrowserAndOSInfoScript = (): string => {
|
|
43
|
+
return `
|
|
44
|
+
function getOSAndBrowser() {
|
|
45
|
+
const userAgent = navigator.userAgent;
|
|
46
|
+
|
|
47
|
+
// OS Detection
|
|
48
|
+
let os = "unknown";
|
|
49
|
+
if (userAgent.indexOf("Win") !== -1) os = "Windows";
|
|
50
|
+
else if (userAgent.indexOf("Mac") !== -1) os = "MacOS";
|
|
51
|
+
else if (userAgent.indexOf("Linux") !== -1) os = "Linux";
|
|
52
|
+
else if (userAgent.indexOf("Android") !== -1) os = "Android";
|
|
53
|
+
else if (userAgent.indexOf("like Mac") !== -1) os = "iOS";
|
|
54
|
+
|
|
55
|
+
// Browser Detection
|
|
56
|
+
let browser = "unknown";
|
|
57
|
+
let browserVersion = null;
|
|
58
|
+
if (userAgent.indexOf("Chrome") !== -1) {
|
|
59
|
+
browser = "Chrome";
|
|
60
|
+
const chromeMatch = userAgent.match(/Chrome\\/([0-9.]+)/);
|
|
61
|
+
browserVersion = chromeMatch ? chromeMatch[1] : null;
|
|
62
|
+
} else if (userAgent.indexOf("Safari") !== -1) {
|
|
63
|
+
browser = "Safari";
|
|
64
|
+
const safariMatch = userAgent.match(/Version\\/([0-9.]+)/);
|
|
65
|
+
browserVersion = safariMatch ? safariMatch[1] : null;
|
|
66
|
+
} else if (userAgent.indexOf("Firefox") !== -1) {
|
|
67
|
+
browser = "Firefox";
|
|
68
|
+
const firefoxMatch = userAgent.match(/Firefox\\/([0-9.]+)/);
|
|
69
|
+
browserVersion = firefoxMatch ? firefoxMatch[1] : null;
|
|
70
|
+
} else if (userAgent.indexOf("Edge") !== -1) {
|
|
71
|
+
browser = "Edge";
|
|
72
|
+
const edgeMatch = userAgent.match(/Edge\\/([0-9.]+)/);
|
|
73
|
+
browserVersion = edgeMatch ? edgeMatch[1] : null;
|
|
74
|
+
} else if (userAgent.indexOf("MSIE") !== -1 || userAgent.indexOf("Trident/") !== -1) {
|
|
75
|
+
browser = "IE";
|
|
76
|
+
const ieMatch = userAgent.match(/(?:MSIE |rv:)([0-9.]+)/);
|
|
77
|
+
browserVersion = ieMatch ? ieMatch[1] : null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Browser Language
|
|
81
|
+
const browserLanguage = navigator.language || navigator.languages?.[0] || null;
|
|
82
|
+
|
|
83
|
+
window.ReactNativeWebView.postMessage(JSON.stringify({
|
|
84
|
+
type: 'SYSTEM_INFO',
|
|
85
|
+
data: { os, browser, browser_version: browserVersion, browser_language: browserLanguage }
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
getOSAndBrowser();
|
|
89
|
+
true;
|
|
90
|
+
`;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// Get complete system information
|
|
94
|
+
export const getSystemInfo = (webViewSystemInfo: {
|
|
95
|
+
os: string;
|
|
96
|
+
browser: string;
|
|
97
|
+
browser_version?: string | null;
|
|
98
|
+
browser_language?: string | null;
|
|
99
|
+
}): SystemInfo => {
|
|
100
|
+
return {
|
|
101
|
+
platform: Platform.OS,
|
|
102
|
+
os: webViewSystemInfo.os || Platform.OS,
|
|
103
|
+
browser: webViewSystemInfo.browser || "WebView",
|
|
104
|
+
browser_version: webViewSystemInfo.browser_version || null,
|
|
105
|
+
browser_language: webViewSystemInfo.browser_language || null,
|
|
106
|
+
sdk_version: SDK_VERSION,
|
|
107
|
+
device: getDeviceType(),
|
|
108
|
+
screen_size: getScreenSize(),
|
|
109
|
+
};
|
|
110
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { logger } from "./logger";
|
|
2
|
+
|
|
3
|
+
export enum StorageMessageType {
|
|
4
|
+
GET_STORAGE = "GET_STORAGE",
|
|
5
|
+
SET_STORAGE = "SET_STORAGE",
|
|
6
|
+
STORAGE_READY = "STORAGE_READY",
|
|
7
|
+
STORAGE_ERROR = "STORAGE_ERROR",
|
|
8
|
+
STORAGE_RESPONSE = "STORAGE_RESPONSE",
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface StorageMessage {
|
|
12
|
+
type: StorageMessageType;
|
|
13
|
+
key?: string;
|
|
14
|
+
value?: string;
|
|
15
|
+
error?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const getStorageScript = () => `
|
|
19
|
+
window.handleStorageOperation = function(message) {
|
|
20
|
+
try {
|
|
21
|
+
switch(message.type) {
|
|
22
|
+
case '${StorageMessageType.GET_STORAGE}':
|
|
23
|
+
const value = localStorage.getItem(message.key);
|
|
24
|
+
window.ReactNativeWebView.postMessage(JSON.stringify({
|
|
25
|
+
type: '${StorageMessageType.STORAGE_RESPONSE}',
|
|
26
|
+
key: message.key,
|
|
27
|
+
value: value
|
|
28
|
+
}));
|
|
29
|
+
break;
|
|
30
|
+
case '${StorageMessageType.SET_STORAGE}':
|
|
31
|
+
localStorage.setItem(message.key, message.value);
|
|
32
|
+
window.ReactNativeWebView.postMessage(JSON.stringify({
|
|
33
|
+
type: '${StorageMessageType.STORAGE_RESPONSE}',
|
|
34
|
+
key: message.key,
|
|
35
|
+
value: message.value
|
|
36
|
+
}));
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
} catch (error) {
|
|
40
|
+
window.ReactNativeWebView.postMessage(JSON.stringify({
|
|
41
|
+
type: '${StorageMessageType.STORAGE_ERROR}',
|
|
42
|
+
error: error.message
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
window.ReactNativeWebView.postMessage(JSON.stringify({
|
|
48
|
+
type: '${StorageMessageType.STORAGE_READY}'
|
|
49
|
+
}));
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
export const createStorageMessage = (
|
|
53
|
+
type: StorageMessageType,
|
|
54
|
+
key?: string,
|
|
55
|
+
value?: string
|
|
56
|
+
): string => {
|
|
57
|
+
return `window.handleStorageOperation(${JSON.stringify({
|
|
58
|
+
type,
|
|
59
|
+
key,
|
|
60
|
+
value,
|
|
61
|
+
})});`;
|
|
62
|
+
};
|
package/src/version.ts
ADDED