@robylon/react-native-sdk 2.0.30 → 2.0.31-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.
- 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 +192 -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,75 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Image, StyleSheet, TouchableOpacity, View } from "react-native";
|
|
3
|
+
import { ChatbotConfig } from "../../Chatbotsdk";
|
|
4
|
+
import { errorTracker } from "../../services/ErrorTrackingService";
|
|
5
|
+
import { ErrorTypes } from "../../constants/errorConstants";
|
|
6
|
+
|
|
7
|
+
interface ImageLauncherProps {
|
|
8
|
+
chatbotConfig: ChatbotConfig;
|
|
9
|
+
onPress: () => void;
|
|
10
|
+
isWebViewVisible: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const ImageLauncher: React.FC<ImageLauncherProps> = ({
|
|
14
|
+
chatbotConfig,
|
|
15
|
+
onPress,
|
|
16
|
+
isWebViewVisible,
|
|
17
|
+
}) => {
|
|
18
|
+
const handleImageError = () => {
|
|
19
|
+
errorTracker.trackError(
|
|
20
|
+
new Error("Failed to load launcher image"),
|
|
21
|
+
"ImageLauncher",
|
|
22
|
+
{
|
|
23
|
+
type: ErrorTypes.RUNTIME_ERROR,
|
|
24
|
+
context: { imageUrl: chatbotConfig?.images?.launcher_image_url?.url },
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<TouchableOpacity
|
|
31
|
+
style={[
|
|
32
|
+
styles.button,
|
|
33
|
+
{
|
|
34
|
+
backgroundColor: isWebViewVisible
|
|
35
|
+
? "transparent"
|
|
36
|
+
: chatbotConfig.brand_colour,
|
|
37
|
+
},
|
|
38
|
+
isWebViewVisible && styles.disabledButton,
|
|
39
|
+
]}
|
|
40
|
+
onPress={isWebViewVisible ? undefined : onPress}
|
|
41
|
+
activeOpacity={isWebViewVisible ? 1 : 0.7}
|
|
42
|
+
>
|
|
43
|
+
<Image
|
|
44
|
+
source={{ uri: chatbotConfig?.images?.launcher_image_url?.url }}
|
|
45
|
+
style={styles.buttonImage}
|
|
46
|
+
onError={handleImageError}
|
|
47
|
+
/>
|
|
48
|
+
</TouchableOpacity>
|
|
49
|
+
);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const styles = StyleSheet.create({
|
|
53
|
+
button: {
|
|
54
|
+
borderRadius: 24,
|
|
55
|
+
width: 48,
|
|
56
|
+
height: 48,
|
|
57
|
+
justifyContent: "center",
|
|
58
|
+
alignItems: "center",
|
|
59
|
+
elevation: 5,
|
|
60
|
+
shadowColor: "#000",
|
|
61
|
+
shadowOffset: { width: 0, height: 2 },
|
|
62
|
+
shadowOpacity: 0.25,
|
|
63
|
+
shadowRadius: 3.84,
|
|
64
|
+
},
|
|
65
|
+
buttonImage: {
|
|
66
|
+
width: 48,
|
|
67
|
+
height: 48,
|
|
68
|
+
borderRadius: 24,
|
|
69
|
+
},
|
|
70
|
+
disabledButton: {
|
|
71
|
+
backgroundColor: "rgba(0, 0, 0, 0)",
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
export default ImageLauncher;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
|
|
3
|
+
import { ChatbotConfig } from "../../Chatbotsdk";
|
|
4
|
+
import { getBestFontColor } from "../../utils/colorUtils";
|
|
5
|
+
|
|
6
|
+
interface TextLauncherProps {
|
|
7
|
+
chatbotConfig: ChatbotConfig;
|
|
8
|
+
onPress: () => void;
|
|
9
|
+
isWebViewVisible: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const TextLauncher: React.FC<TextLauncherProps> = ({
|
|
13
|
+
chatbotConfig,
|
|
14
|
+
onPress,
|
|
15
|
+
isWebViewVisible,
|
|
16
|
+
}) => {
|
|
17
|
+
console.log("TEXT_LAUNCHER", {
|
|
18
|
+
brand_colour: chatbotConfig?.brand_colour,
|
|
19
|
+
isWebViewVisible,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const containerStyle = {
|
|
23
|
+
...styles.container,
|
|
24
|
+
backgroundColor: isWebViewVisible
|
|
25
|
+
? "transparent"
|
|
26
|
+
: chatbotConfig?.brand_colour || "#007AFF",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const buttonTextStyle = {
|
|
30
|
+
...styles.buttonText,
|
|
31
|
+
color: getBestFontColor(chatbotConfig?.brand_colour || "#ffffff"),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<View style={[containerStyle, isWebViewVisible && styles.disabledButton]}>
|
|
36
|
+
<TouchableOpacity
|
|
37
|
+
style={[styles.button]}
|
|
38
|
+
onPress={isWebViewVisible ? undefined : onPress}
|
|
39
|
+
activeOpacity={isWebViewVisible ? 1 : 0.7}
|
|
40
|
+
>
|
|
41
|
+
<View style={styles.textContainer}>
|
|
42
|
+
<Text style={buttonTextStyle} numberOfLines={1}>
|
|
43
|
+
{chatbotConfig?.launcher_properties?.text || "Chat"}
|
|
44
|
+
</Text>
|
|
45
|
+
</View>
|
|
46
|
+
</TouchableOpacity>
|
|
47
|
+
</View>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const styles = StyleSheet.create({
|
|
52
|
+
container: {
|
|
53
|
+
maxWidth: 160,
|
|
54
|
+
minWidth: 120,
|
|
55
|
+
height: 40,
|
|
56
|
+
borderRadius: 9999,
|
|
57
|
+
shadowColor: "#000",
|
|
58
|
+
shadowOffset: { width: 0, height: 0 },
|
|
59
|
+
shadowOpacity: 0.24,
|
|
60
|
+
shadowRadius: 16,
|
|
61
|
+
elevation: 5,
|
|
62
|
+
},
|
|
63
|
+
button: {
|
|
64
|
+
flex: 1,
|
|
65
|
+
paddingHorizontal: 20,
|
|
66
|
+
paddingVertical: 9,
|
|
67
|
+
justifyContent: "center",
|
|
68
|
+
alignItems: "center",
|
|
69
|
+
},
|
|
70
|
+
textContainer: {
|
|
71
|
+
flex: 1,
|
|
72
|
+
minWidth: 0,
|
|
73
|
+
},
|
|
74
|
+
buttonText: {
|
|
75
|
+
fontSize: 14,
|
|
76
|
+
fontWeight: "600",
|
|
77
|
+
textAlign: "center",
|
|
78
|
+
},
|
|
79
|
+
disabledButton: {
|
|
80
|
+
backgroundColor: "rgba(0, 0, 0, 0)",
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
export default TextLauncher;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { StyleSheet, Text, TouchableOpacity, View, Image } from "react-native";
|
|
3
|
+
import { ChatbotConfig } from "../../Chatbotsdk";
|
|
4
|
+
import { errorTracker } from "../../services/ErrorTrackingService";
|
|
5
|
+
import { ErrorTypes } from "../../constants/errorConstants";
|
|
6
|
+
import { getBestFontColor } from "../../utils/colorUtils";
|
|
7
|
+
import { FONT_FAMILY, FONT_LINE_HEIGHT } from "../../constants/fontStyles";
|
|
8
|
+
|
|
9
|
+
interface TextualImageLauncherProps {
|
|
10
|
+
chatbotConfig: ChatbotConfig;
|
|
11
|
+
onPress: () => void;
|
|
12
|
+
isWebViewVisible: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const TextualImageLauncher: React.FC<TextualImageLauncherProps> = ({
|
|
16
|
+
chatbotConfig,
|
|
17
|
+
onPress,
|
|
18
|
+
isWebViewVisible,
|
|
19
|
+
}) => {
|
|
20
|
+
console.log("TEXTUALIMAGE_LAUNCHER_DEBUG", {
|
|
21
|
+
brand_colour: chatbotConfig?.brand_colour,
|
|
22
|
+
text: chatbotConfig?.launcher_properties?.text,
|
|
23
|
+
isWebViewVisible,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const containerStyle = {
|
|
27
|
+
...styles.container,
|
|
28
|
+
backgroundColor: isWebViewVisible
|
|
29
|
+
? "transparent"
|
|
30
|
+
: chatbotConfig?.brand_colour || "#007AFF",
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const handleImageError = () => {
|
|
34
|
+
errorTracker.trackError(
|
|
35
|
+
new Error("Failed to load launcher image"),
|
|
36
|
+
"TextualImageLauncher",
|
|
37
|
+
{
|
|
38
|
+
type: ErrorTypes.RUNTIME_ERROR,
|
|
39
|
+
context: { imageUrl: chatbotConfig?.images?.launcher_image_url?.url },
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const buttonTextStyle = {
|
|
45
|
+
...styles.buttonText,
|
|
46
|
+
color: getBestFontColor(chatbotConfig?.brand_colour || "#ffffff"),
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
console.log("TEXTUALIMAGE_LAUNCHER_TEXT_STYLE", {
|
|
50
|
+
brand_colour: chatbotConfig?.brand_colour,
|
|
51
|
+
text_color: buttonTextStyle.color,
|
|
52
|
+
text: chatbotConfig?.launcher_properties?.text,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<View style={[containerStyle, isWebViewVisible && styles.disabledButton]}>
|
|
57
|
+
<TouchableOpacity
|
|
58
|
+
style={styles.button}
|
|
59
|
+
onPress={isWebViewVisible ? undefined : onPress}
|
|
60
|
+
activeOpacity={isWebViewVisible ? 1 : 0.7}
|
|
61
|
+
>
|
|
62
|
+
<View style={styles.contentContainer}>
|
|
63
|
+
<View style={styles.textContainer}>
|
|
64
|
+
<Text
|
|
65
|
+
style={buttonTextStyle}
|
|
66
|
+
numberOfLines={1}
|
|
67
|
+
ellipsizeMode="tail"
|
|
68
|
+
>
|
|
69
|
+
{chatbotConfig?.launcher_properties?.text || "Chat"}
|
|
70
|
+
</Text>
|
|
71
|
+
</View>
|
|
72
|
+
<View style={styles.imageContainer}>
|
|
73
|
+
<Image
|
|
74
|
+
source={{ uri: chatbotConfig?.images?.launcher_image_url?.url }}
|
|
75
|
+
style={styles.buttonImage}
|
|
76
|
+
onError={handleImageError}
|
|
77
|
+
/>
|
|
78
|
+
</View>
|
|
79
|
+
</View>
|
|
80
|
+
</TouchableOpacity>
|
|
81
|
+
</View>
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const styles = StyleSheet.create({
|
|
86
|
+
container: {
|
|
87
|
+
minWidth: 140,
|
|
88
|
+
maxWidth: 208,
|
|
89
|
+
height: 40,
|
|
90
|
+
borderRadius: 9999,
|
|
91
|
+
shadowColor: "#000",
|
|
92
|
+
shadowOffset: { width: 0, height: 0 },
|
|
93
|
+
shadowOpacity: 0.24,
|
|
94
|
+
shadowRadius: 16,
|
|
95
|
+
elevation: 5,
|
|
96
|
+
},
|
|
97
|
+
button: {
|
|
98
|
+
flex: 1,
|
|
99
|
+
height: "100%",
|
|
100
|
+
},
|
|
101
|
+
contentContainer: {
|
|
102
|
+
flex: 1,
|
|
103
|
+
flexDirection: "row",
|
|
104
|
+
alignItems: "center",
|
|
105
|
+
paddingLeft: 20,
|
|
106
|
+
height: "100%",
|
|
107
|
+
},
|
|
108
|
+
textContainer: {
|
|
109
|
+
flexShrink: 1,
|
|
110
|
+
flexGrow: 0,
|
|
111
|
+
marginRight: 8,
|
|
112
|
+
},
|
|
113
|
+
buttonText: {
|
|
114
|
+
fontSize: 14,
|
|
115
|
+
fontWeight: "600",
|
|
116
|
+
},
|
|
117
|
+
imageContainer: {
|
|
118
|
+
width: 40,
|
|
119
|
+
height: 40,
|
|
120
|
+
borderRadius: 9999,
|
|
121
|
+
overflow: "hidden",
|
|
122
|
+
},
|
|
123
|
+
buttonImage: {
|
|
124
|
+
width: "100%",
|
|
125
|
+
height: "100%",
|
|
126
|
+
resizeMode: "cover",
|
|
127
|
+
},
|
|
128
|
+
disabledButton: {
|
|
129
|
+
backgroundColor: "rgba(0, 0, 0, 0)",
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
export default TextualImageLauncher;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import React, { useMemo, useEffect, useRef } from "react";
|
|
2
|
+
import { View, StyleSheet, FlexAlignType, Animated } from "react-native";
|
|
3
|
+
import { ChatbotConfig, LauncherType } from "./Chatbotsdk";
|
|
4
|
+
import ImageLauncher from "./FloatingButton/launchers/ImageLauncher";
|
|
5
|
+
import TextLauncher from "./FloatingButton/launchers/TextLauncher";
|
|
6
|
+
import TextualImageLauncher from "./FloatingButton/launchers/TextualImageLauncher";
|
|
7
|
+
import { animateButtonScale } from "./utils/animations";
|
|
8
|
+
|
|
9
|
+
interface FloatingButtonProps {
|
|
10
|
+
onPress: () => void;
|
|
11
|
+
brandColor?: string;
|
|
12
|
+
imageUrl?: string;
|
|
13
|
+
isWebViewVisible: boolean;
|
|
14
|
+
chatbotConfig: ChatbotConfig;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const FloatingButton: React.FC<FloatingButtonProps> = ({
|
|
18
|
+
onPress,
|
|
19
|
+
isWebViewVisible,
|
|
20
|
+
chatbotConfig,
|
|
21
|
+
}) => {
|
|
22
|
+
const buttonScale = useRef(new Animated.Value(1)).current;
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
animateButtonScale(buttonScale, isWebViewVisible);
|
|
26
|
+
}, [isWebViewVisible, buttonScale]);
|
|
27
|
+
|
|
28
|
+
console.log("FLOATING_BUTTON_CONFIG", {
|
|
29
|
+
config: chatbotConfig,
|
|
30
|
+
brand_colour: chatbotConfig?.brand_colour,
|
|
31
|
+
launcher_type: chatbotConfig?.launcher_type,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const position = chatbotConfig?.interface_properties?.position || "Right";
|
|
35
|
+
const sideSpacing = chatbotConfig?.interface_properties?.side_spacing ?? 20;
|
|
36
|
+
const bottomSpacing =
|
|
37
|
+
chatbotConfig?.interface_properties?.bottom_spacing ?? 20;
|
|
38
|
+
|
|
39
|
+
const buttonContainerStyle = useMemo(() => {
|
|
40
|
+
return {
|
|
41
|
+
...styles.container,
|
|
42
|
+
[position.toLowerCase()]: sideSpacing ?? 20,
|
|
43
|
+
alignItems:
|
|
44
|
+
position === "Right" ? "flex-end" : ("flex-start" as FlexAlignType),
|
|
45
|
+
bottom: bottomSpacing ?? 20,
|
|
46
|
+
transform: [{ scale: buttonScale }],
|
|
47
|
+
};
|
|
48
|
+
}, [position, sideSpacing, bottomSpacing, buttonScale]);
|
|
49
|
+
|
|
50
|
+
const renderLauncher = () => {
|
|
51
|
+
switch (chatbotConfig?.launcher_type) {
|
|
52
|
+
case LauncherType.TEXT:
|
|
53
|
+
return (
|
|
54
|
+
<TextLauncher
|
|
55
|
+
chatbotConfig={chatbotConfig}
|
|
56
|
+
onPress={onPress}
|
|
57
|
+
isWebViewVisible={isWebViewVisible}
|
|
58
|
+
/>
|
|
59
|
+
);
|
|
60
|
+
case LauncherType.TEXTUAL_IMAGE:
|
|
61
|
+
return (
|
|
62
|
+
<TextualImageLauncher
|
|
63
|
+
chatbotConfig={chatbotConfig}
|
|
64
|
+
onPress={onPress}
|
|
65
|
+
isWebViewVisible={isWebViewVisible}
|
|
66
|
+
/>
|
|
67
|
+
);
|
|
68
|
+
case LauncherType.IMAGE:
|
|
69
|
+
default:
|
|
70
|
+
return (
|
|
71
|
+
<ImageLauncher
|
|
72
|
+
chatbotConfig={chatbotConfig}
|
|
73
|
+
onPress={onPress}
|
|
74
|
+
isWebViewVisible={isWebViewVisible}
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<Animated.View
|
|
82
|
+
style={buttonContainerStyle}
|
|
83
|
+
pointerEvents={isWebViewVisible ? "none" : "auto"}
|
|
84
|
+
>
|
|
85
|
+
{renderLauncher()}
|
|
86
|
+
</Animated.View>
|
|
87
|
+
);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const styles = StyleSheet.create({
|
|
91
|
+
container: {
|
|
92
|
+
position: "absolute",
|
|
93
|
+
zIndex: 998,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
export default FloatingButton;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { View, ActivityIndicator } from "react-native";
|
|
3
|
+
import { WebView } from "react-native-webview";
|
|
4
|
+
|
|
5
|
+
const LoadingIndicator: React.FC = () => (
|
|
6
|
+
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
|
|
7
|
+
<ActivityIndicator size="large" color="#0000ff" />
|
|
8
|
+
</View>
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
export default LoadingIndicator;
|
package/src/Toast.tsx
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React, { useState, useEffect } from "react";
|
|
2
|
+
import { View, Text, StyleSheet, Animated } from "react-native";
|
|
3
|
+
|
|
4
|
+
const Toast = ({
|
|
5
|
+
message,
|
|
6
|
+
visible,
|
|
7
|
+
duration = 3000,
|
|
8
|
+
}: {
|
|
9
|
+
message: any;
|
|
10
|
+
visible: boolean;
|
|
11
|
+
duration?: number;
|
|
12
|
+
}) => {
|
|
13
|
+
const [fadeAnim] = useState(new Animated.Value(0));
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (visible) {
|
|
17
|
+
// Fade in the toast
|
|
18
|
+
Animated.timing(fadeAnim, {
|
|
19
|
+
toValue: 1,
|
|
20
|
+
duration: 300,
|
|
21
|
+
useNativeDriver: true,
|
|
22
|
+
}).start(() => {
|
|
23
|
+
// Auto-hide after duration
|
|
24
|
+
setTimeout(() => {
|
|
25
|
+
Animated.timing(fadeAnim, {
|
|
26
|
+
toValue: 0,
|
|
27
|
+
duration: 300,
|
|
28
|
+
useNativeDriver: true,
|
|
29
|
+
}).start();
|
|
30
|
+
}, duration);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}, [visible]);
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Animated.View
|
|
37
|
+
style={[
|
|
38
|
+
styles.toastContainer,
|
|
39
|
+
{ opacity: fadeAnim, display: visible ? "flex" : "none" },
|
|
40
|
+
]}
|
|
41
|
+
>
|
|
42
|
+
<Text style={styles.toastText}>{message}</Text>
|
|
43
|
+
</Animated.View>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const styles = StyleSheet.create({
|
|
48
|
+
toastContainer: {
|
|
49
|
+
position: "absolute",
|
|
50
|
+
bottom: 50,
|
|
51
|
+
left: "10%",
|
|
52
|
+
right: "10%",
|
|
53
|
+
backgroundColor: "rgba(0, 0, 0, 0.8)",
|
|
54
|
+
padding: 10,
|
|
55
|
+
borderRadius: 5,
|
|
56
|
+
alignItems: "center",
|
|
57
|
+
justifyContent: "center",
|
|
58
|
+
},
|
|
59
|
+
toastText: {
|
|
60
|
+
color: "#fff",
|
|
61
|
+
textAlign: "center",
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
export default Toast;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { TouchableOpacity, Text, StyleSheet, View } from "react-native";
|
|
3
|
+
import { openDebugMenu } from "../utils/debugMenu";
|
|
4
|
+
import { openDebuggerFromApp } from "../utils/debugMenu";
|
|
5
|
+
|
|
6
|
+
const DebugButton = () => {
|
|
7
|
+
if (!__DEV__) return null;
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<View style={styles.debugContainer}>
|
|
11
|
+
<TouchableOpacity style={styles.debugButton} onPress={openDebugMenu}>
|
|
12
|
+
<Text style={styles.debugText}>Menu</Text>
|
|
13
|
+
</TouchableOpacity>
|
|
14
|
+
<TouchableOpacity
|
|
15
|
+
style={[styles.debugButton, styles.debuggerButton]}
|
|
16
|
+
onPress={openDebuggerFromApp}
|
|
17
|
+
>
|
|
18
|
+
<Text style={styles.debugText}>Debug</Text>
|
|
19
|
+
</TouchableOpacity>
|
|
20
|
+
</View>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const styles = StyleSheet.create({
|
|
25
|
+
debugContainer: {
|
|
26
|
+
position: "absolute",
|
|
27
|
+
top: 40,
|
|
28
|
+
right: 10,
|
|
29
|
+
flexDirection: "row",
|
|
30
|
+
gap: 8,
|
|
31
|
+
},
|
|
32
|
+
debugButton: {
|
|
33
|
+
backgroundColor: "rgba(0,0,0,0.7)",
|
|
34
|
+
padding: 8,
|
|
35
|
+
borderRadius: 4,
|
|
36
|
+
},
|
|
37
|
+
debuggerButton: {
|
|
38
|
+
backgroundColor: "rgba(255,0,0,0.7)",
|
|
39
|
+
},
|
|
40
|
+
debugText: {
|
|
41
|
+
color: "white",
|
|
42
|
+
fontSize: 12,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
export default DebugButton;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React, { Component, ErrorInfo, ReactNode } from "react";
|
|
2
|
+
import { errorTracker } from "../services/ErrorTrackingService";
|
|
3
|
+
import { ErrorTypes } from "../constants/errorConstants";
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
componentName: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface State {
|
|
11
|
+
hasError: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class ErrorBoundary extends Component<Props, State> {
|
|
15
|
+
constructor(props: Props) {
|
|
16
|
+
super(props);
|
|
17
|
+
this.state = { hasError: false };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static getDerivedStateFromError(_: Error): State {
|
|
21
|
+
return { hasError: true };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
|
25
|
+
errorTracker.trackError(error, this.props.componentName, {
|
|
26
|
+
errorInfo,
|
|
27
|
+
type: ErrorTypes.RUNTIME_ERROR,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
render() {
|
|
32
|
+
if (this.state.hasError) {
|
|
33
|
+
return null; // Or a fallback UI if needed
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return this.props.children;
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export enum ErrorCodes {
|
|
2
|
+
MISSING_API_KEY = "MISSING_API_KEY",
|
|
3
|
+
ERROR_TRACKING_INIT_FAILED = "ERROR_TRACKING_INIT_FAILED",
|
|
4
|
+
WEBVIEW_LOAD_ERROR = "WEBVIEW_LOAD_ERROR",
|
|
5
|
+
CONFIG_FETCH_ERROR = "CONFIG_FETCH_ERROR",
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export enum ErrorTypes {
|
|
9
|
+
INITIALIZATION_ERROR = "INITIALIZATION_ERROR",
|
|
10
|
+
RUNTIME_ERROR = "RUNTIME_ERROR",
|
|
11
|
+
NETWORK_ERROR = "NETWORK_ERROR",
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const ErrorMessages = {
|
|
15
|
+
[ErrorCodes.MISSING_API_KEY]: (apiKey: any) =>
|
|
16
|
+
`Invalid API key: ${apiKey}. Please provide a valid API key.`,
|
|
17
|
+
[ErrorCodes.ERROR_TRACKING_INIT_FAILED]:
|
|
18
|
+
"Failed to initialize error tracking service.",
|
|
19
|
+
[ErrorCodes.WEBVIEW_LOAD_ERROR]: "Failed to load chatbot interface.",
|
|
20
|
+
[ErrorCodes.CONFIG_FETCH_ERROR]: "Failed to fetch chatbot configuration.",
|
|
21
|
+
};
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
const BASE_CHATBOT_DOMAIN = process.env.CHATBOT_DOMAIN || "";
|
|
2
|
+
const CHATBOT_PATH = process.env.CHATBOT_PATH || "";
|
|
3
|
+
export const BASE_CHATBOT_URL = `${BASE_CHATBOT_DOMAIN}/${CHATBOT_PATH}`;
|
|
4
|
+
export const API_URL = process.env.API_URL;
|
|
5
|
+
export const DEFAULT_LAUNCHER_IMAGE = `${BASE_CHATBOT_DOMAIN}/chatbubble.png`;
|
package/src/global.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// declare module "react-native" {
|
|
2
|
+
// export const View: any;
|
|
3
|
+
// export const ActivityIndicator: any;
|
|
4
|
+
// export const Linking: any;
|
|
5
|
+
// // Add other React Native exports as needed
|
|
6
|
+
// }
|
|
7
|
+
|
|
8
|
+
declare module "*.json" {
|
|
9
|
+
const value: any;
|
|
10
|
+
export default value;
|
|
11
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { getSystemInfo } from "../utils/systemInfo";
|
|
3
|
+
import { logger } from "../utils/logger";
|
|
4
|
+
import {
|
|
5
|
+
ChatbotEventType,
|
|
6
|
+
AllEventTypes,
|
|
7
|
+
ChatbotEventHandler,
|
|
8
|
+
} from "../types/events";
|
|
9
|
+
import { API_URL } from "../constants";
|
|
10
|
+
|
|
11
|
+
interface EventHookProps {
|
|
12
|
+
api_key: string;
|
|
13
|
+
chatbotConfig: any;
|
|
14
|
+
user_profile?: Record<string, any>;
|
|
15
|
+
onEvent?: ChatbotEventHandler;
|
|
16
|
+
systemInfo: { os: string; browser: string }; // Add this to get system info from WebView
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const useChatbotEvents = ({
|
|
20
|
+
api_key,
|
|
21
|
+
chatbotConfig,
|
|
22
|
+
user_profile,
|
|
23
|
+
onEvent,
|
|
24
|
+
systemInfo,
|
|
25
|
+
}: EventHookProps) => {
|
|
26
|
+
const onInternalEvent = useCallback(
|
|
27
|
+
async (
|
|
28
|
+
eventType: AllEventTypes,
|
|
29
|
+
additionalData: Record<string, any> = {}
|
|
30
|
+
) => {
|
|
31
|
+
if (!api_key) return;
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const apiUrl = API_URL;
|
|
35
|
+
const sysInfo = getSystemInfo(systemInfo);
|
|
36
|
+
|
|
37
|
+
const eventData = {
|
|
38
|
+
trigger_time: Date.now(),
|
|
39
|
+
channel: "CHATBOT",
|
|
40
|
+
org_id: api_key,
|
|
41
|
+
client_user_id: chatbotConfig?.isAnonymous
|
|
42
|
+
? "Anonymous"
|
|
43
|
+
: String(chatbotConfig?.userId || ""),
|
|
44
|
+
event_type: eventType,
|
|
45
|
+
user_profile: { ...(user_profile || {}) },
|
|
46
|
+
...additionalData,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const payload = {
|
|
50
|
+
org_id: api_key,
|
|
51
|
+
event_data: eventData,
|
|
52
|
+
metadata: {
|
|
53
|
+
timestamp: Date.now(),
|
|
54
|
+
...sysInfo,
|
|
55
|
+
},
|
|
56
|
+
event_type: "INFO",
|
|
57
|
+
user_id: chatbotConfig?.userId,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
logger.debug("Sending internal event:", payload);
|
|
61
|
+
|
|
62
|
+
const response = await fetch(`${apiUrl}/users/sdk/record-logs/`, {
|
|
63
|
+
method: "POST",
|
|
64
|
+
headers: { "Content-Type": "application/json" },
|
|
65
|
+
body: JSON.stringify(payload),
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
`Failed to send internal event: ${response.statusText}`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
} catch (error) {
|
|
74
|
+
logger.error("Failed to send internal event:", error);
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
[api_key, chatbotConfig, user_profile, systemInfo]
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const emitEvent = useCallback(
|
|
81
|
+
(type: ChatbotEventType, data?: any) => {
|
|
82
|
+
const timestamp = Date.now();
|
|
83
|
+
onEvent?.({ type, timestamp, data });
|
|
84
|
+
onInternalEvent(type, { ...data, timestamp });
|
|
85
|
+
},
|
|
86
|
+
[onEvent, onInternalEvent]
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
emitEvent,
|
|
91
|
+
onInternalEvent,
|
|
92
|
+
};
|
|
93
|
+
};
|