@robylon/react-native-sdk 2.0.21-staging.2 → 2.0.21-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.
- package/lib/commonjs/Chatbotsdk.js +2 -2
- package/lib/commonjs/Chatbotsdk.js.map +1 -1
- package/lib/commonjs/FloatingButton/launchers/ImageLauncher.js +2 -0
- package/lib/commonjs/FloatingButton/launchers/ImageLauncher.js.map +1 -0
- package/lib/commonjs/FloatingButton/launchers/TextLauncher.js +2 -0
- package/lib/commonjs/FloatingButton/launchers/TextLauncher.js.map +1 -0
- package/lib/commonjs/FloatingButton/launchers/TextualImageLauncher.js +2 -0
- package/lib/commonjs/FloatingButton/launchers/TextualImageLauncher.js.map +1 -0
- package/lib/commonjs/FloatingButton.js +1 -1
- package/lib/commonjs/FloatingButton.js.map +1 -1
- package/lib/commonjs/constants/fontStyles.js +2 -0
- package/lib/commonjs/constants/fontStyles.js.map +1 -0
- package/lib/commonjs/openChatbot.js.map +1 -1
- package/lib/commonjs/utils/animations.js +2 -0
- package/lib/commonjs/utils/animations.js.map +1 -0
- package/lib/commonjs/utils/colorUtils.js +2 -0
- package/lib/commonjs/utils/colorUtils.js.map +1 -0
- 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 +2 -0
- package/lib/module/FloatingButton/launchers/ImageLauncher.js.map +1 -0
- package/lib/module/FloatingButton/launchers/TextLauncher.js +2 -0
- package/lib/module/FloatingButton/launchers/TextLauncher.js.map +1 -0
- package/lib/module/FloatingButton/launchers/TextualImageLauncher.js +2 -0
- package/lib/module/FloatingButton/launchers/TextualImageLauncher.js.map +1 -0
- package/lib/module/FloatingButton.js +1 -1
- package/lib/module/FloatingButton.js.map +1 -1
- package/lib/module/constants/fontStyles.js +2 -0
- package/lib/module/constants/fontStyles.js.map +1 -0
- package/lib/module/openChatbot.js +2 -1
- package/lib/module/utils/animations.js +2 -0
- package/lib/module/utils/animations.js.map +1 -0
- package/lib/module/utils/colorUtils.js +2 -0
- package/lib/module/utils/colorUtils.js.map +1 -0
- package/lib/module/versions/version.staging.js +1 -1
- package/lib/typescript/Chatbotsdk.d.ts.map +1 -1
- package/lib/typescript/FloatingButton/launchers/ImageLauncher.d.ts +10 -0
- package/lib/typescript/FloatingButton/launchers/ImageLauncher.d.ts.map +1 -0
- package/lib/typescript/FloatingButton/launchers/TextLauncher.d.ts +10 -0
- package/lib/typescript/FloatingButton/launchers/TextLauncher.d.ts.map +1 -0
- package/lib/typescript/FloatingButton/launchers/TextualImageLauncher.d.ts +10 -0
- package/lib/typescript/FloatingButton/launchers/TextualImageLauncher.d.ts.map +1 -0
- package/lib/typescript/FloatingButton.d.ts +2 -0
- package/lib/typescript/FloatingButton.d.ts.map +1 -1
- package/lib/typescript/constants/fontStyles.d.ts +3 -0
- package/lib/typescript/constants/fontStyles.d.ts.map +1 -0
- package/lib/typescript/utils/animations.d.ts +38 -0
- package/lib/typescript/utils/animations.d.ts.map +1 -0
- package/lib/typescript/utils/colorUtils.d.ts +2 -0
- package/lib/typescript/utils/colorUtils.d.ts.map +1 -0
- package/lib/typescript/versions/version.staging.d.ts +1 -1
- package/package.json +1 -1
- package/src/Chatbotsdk.tsx +42 -38
- 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 +71 -62
- package/src/constants/fontStyles.ts +3 -0
- package/src/utils/animations.ts +120 -0
- package/src/utils/colorUtils.ts +35 -0
- package/src/versions/version.staging.ts +1 -1
- package/lib/commonjs/ChatbotWebview.js +0 -2
- package/lib/commonjs/ChatbotWebview.js.map +0 -1
- package/lib/module/ChatbotWebview.js +0 -2
- package/lib/module/ChatbotWebview.js.map +0 -1
- package/lib/typescript/ChatbotWebview.d.ts +0 -8
- package/lib/typescript/ChatbotWebview.d.ts.map +0 -1
- package/src/ChatbotWebview.tsx +0 -44
package/src/Chatbotsdk.tsx
CHANGED
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
getStorageScript,
|
|
40
40
|
createStorageMessage,
|
|
41
41
|
} from "./utils/webViewStorage";
|
|
42
|
+
import { animateWebViewOpen, animateWebViewClose } from "./utils/animations";
|
|
42
43
|
|
|
43
44
|
export interface ChatbotProps {
|
|
44
45
|
api_key: string;
|
|
@@ -132,7 +133,6 @@ const Chatbot: React.FC<InternalChatbotProps> = ({
|
|
|
132
133
|
);
|
|
133
134
|
const [loading, setLoading] = useState(true);
|
|
134
135
|
const webViewRef = useRef<WebView>(null);
|
|
135
|
-
const fadeAnim = useRef(new Animated.Value(0)).current;
|
|
136
136
|
const isFirstLoadRef = useRef(true);
|
|
137
137
|
const [toastVisible, setToastVisible] = useState(false);
|
|
138
138
|
const [toastMessage, setToastMessage] = useState("");
|
|
@@ -140,9 +140,15 @@ const Chatbot: React.FC<InternalChatbotProps> = ({
|
|
|
140
140
|
const [isStorageReady, setIsStorageReady] = useState(false);
|
|
141
141
|
const pendingStorageOps = useRef<Array<() => void>>([]);
|
|
142
142
|
const memoryCache = useRef<Record<string, string>>({});
|
|
143
|
-
|
|
144
143
|
const [systemInfo, setSystemInfo] = useState({ os: "", browser: "" });
|
|
145
144
|
|
|
145
|
+
// Initialize animated values
|
|
146
|
+
const animatedValues = useRef({
|
|
147
|
+
scale: new Animated.Value(1),
|
|
148
|
+
translateY: new Animated.Value(0),
|
|
149
|
+
opacity: new Animated.Value(1),
|
|
150
|
+
}).current;
|
|
151
|
+
|
|
146
152
|
// Create chatbotConfig object for events
|
|
147
153
|
const chatbotConfigForEvents = useMemo(
|
|
148
154
|
() => ({
|
|
@@ -462,11 +468,7 @@ const Chatbot: React.FC<InternalChatbotProps> = ({
|
|
|
462
468
|
const openWebView = useCallback(() => {
|
|
463
469
|
setIsWebViewVisible(true);
|
|
464
470
|
if (enableAnimation) {
|
|
465
|
-
|
|
466
|
-
toValue: 1,
|
|
467
|
-
duration: 250,
|
|
468
|
-
useNativeDriver: true,
|
|
469
|
-
}).start(() => {
|
|
471
|
+
animateWebViewOpen(animatedValues, () => {
|
|
470
472
|
onOpen?.();
|
|
471
473
|
emitEvent(ChatbotEventType.CHATBOT_OPENED);
|
|
472
474
|
if (!isFirstLoadRef.current) {
|
|
@@ -474,7 +476,6 @@ const Chatbot: React.FC<InternalChatbotProps> = ({
|
|
|
474
476
|
}
|
|
475
477
|
});
|
|
476
478
|
} else {
|
|
477
|
-
fadeAnim.setValue(1);
|
|
478
479
|
requestAnimationFrame(() => {
|
|
479
480
|
onOpen?.();
|
|
480
481
|
emitEvent(ChatbotEventType.CHATBOT_OPENED);
|
|
@@ -483,7 +484,7 @@ const Chatbot: React.FC<InternalChatbotProps> = ({
|
|
|
483
484
|
}
|
|
484
485
|
});
|
|
485
486
|
}
|
|
486
|
-
}, [enableAnimation,
|
|
487
|
+
}, [enableAnimation, animatedValues, onOpen, emitEvent]);
|
|
487
488
|
|
|
488
489
|
// Handle closing the chatbot
|
|
489
490
|
const closeWebView = useCallback(() => {
|
|
@@ -496,23 +497,18 @@ const Chatbot: React.FC<InternalChatbotProps> = ({
|
|
|
496
497
|
);
|
|
497
498
|
}
|
|
498
499
|
|
|
499
|
-
|
|
500
|
+
if (enableAnimation) {
|
|
501
|
+
animateWebViewClose(animatedValues, () => {
|
|
502
|
+
setIsWebViewVisible(false);
|
|
503
|
+
onClose?.();
|
|
504
|
+
emitEvent(ChatbotEventType.CHATBOT_CLOSED);
|
|
505
|
+
});
|
|
506
|
+
} else {
|
|
500
507
|
setIsWebViewVisible(false);
|
|
501
508
|
onClose?.();
|
|
502
509
|
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
510
|
}
|
|
515
|
-
}, [enableAnimation,
|
|
511
|
+
}, [enableAnimation, animatedValues, onClose, emitEvent]);
|
|
516
512
|
|
|
517
513
|
// Emit button loaded event when component mounts
|
|
518
514
|
useEffect(() => {
|
|
@@ -564,29 +560,28 @@ const Chatbot: React.FC<InternalChatbotProps> = ({
|
|
|
564
560
|
const bottomSpacing =
|
|
565
561
|
chatbotConfig?.interface_properties?.bottom_spacing ?? 20;
|
|
566
562
|
|
|
567
|
-
const mainContainerStyle = useMemo(() => {
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
}, [position, sideSpacing, styles.mainContainer]);
|
|
579
|
-
|
|
580
|
-
console.log("MAINCONTAINERSTYLE", mainContainerStyle, position, sideSpacing);
|
|
563
|
+
// const mainContainerStyle = useMemo(() => {
|
|
564
|
+
// return {
|
|
565
|
+
// ...styles.mainContainer,
|
|
566
|
+
// [position.toLowerCase()]: `${sideSpacing ?? 20}px`,
|
|
567
|
+
// alignItems:
|
|
568
|
+
// position === "Right" ? "flex-end" : ("flex-start" as FlexAlignType),
|
|
569
|
+
// // Explicitly clear mobile-specific styles
|
|
570
|
+
// top: undefined,
|
|
571
|
+
// [position === "Right" ? "left" : "right"]: undefined,
|
|
572
|
+
// backgroundColor: "green",
|
|
573
|
+
// };
|
|
574
|
+
// }, [position, sideSpacing, styles.mainContainer]);
|
|
581
575
|
|
|
582
576
|
return (
|
|
583
577
|
<ErrorBoundary componentName="ChatbotSDK">
|
|
584
|
-
<View style={
|
|
578
|
+
<View style={styles?.mainContainer}>
|
|
585
579
|
{!loading && (
|
|
586
580
|
<>
|
|
587
581
|
<ErrorBoundary componentName="FloatingButton">
|
|
588
582
|
{show_floating_button && api_key && chatbotConfig && (
|
|
589
583
|
<FloatingButton
|
|
584
|
+
chatbotConfig={chatbotConfig}
|
|
590
585
|
isWebViewVisible={isWebViewVisible}
|
|
591
586
|
onPress={() => {
|
|
592
587
|
emitEvent(ChatbotEventType.CHATBOT_BUTTON_CLICKED);
|
|
@@ -605,7 +600,16 @@ const Chatbot: React.FC<InternalChatbotProps> = ({
|
|
|
605
600
|
pointerEvents={isWebViewVisible ? "auto" : "none"}
|
|
606
601
|
>
|
|
607
602
|
<Animated.View
|
|
608
|
-
style={[
|
|
603
|
+
style={[
|
|
604
|
+
styles.fullScreenContainer,
|
|
605
|
+
{
|
|
606
|
+
opacity: animatedValues.opacity,
|
|
607
|
+
transform: [
|
|
608
|
+
{ scale: animatedValues.scale },
|
|
609
|
+
{ translateY: animatedValues.translateY },
|
|
610
|
+
],
|
|
611
|
+
},
|
|
612
|
+
]}
|
|
609
613
|
>
|
|
610
614
|
<ErrorBoundary componentName="ChatbotWebView">
|
|
611
615
|
<WebView
|
|
@@ -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;
|
package/src/FloatingButton.tsx
CHANGED
|
@@ -1,88 +1,97 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
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";
|
|
5
8
|
|
|
6
9
|
interface FloatingButtonProps {
|
|
7
10
|
onPress: () => void;
|
|
8
|
-
brandColor?: string;
|
|
9
|
-
imageUrl?: string;
|
|
11
|
+
brandColor?: string;
|
|
12
|
+
imageUrl?: string;
|
|
10
13
|
isWebViewVisible: boolean;
|
|
14
|
+
chatbotConfig: ChatbotConfig;
|
|
11
15
|
}
|
|
12
16
|
|
|
13
17
|
const FloatingButton: React.FC<FloatingButtonProps> = ({
|
|
14
18
|
onPress,
|
|
15
|
-
brandColor = "#007AFF", // Default color
|
|
16
|
-
imageUrl,
|
|
17
19
|
isWebViewVisible,
|
|
20
|
+
chatbotConfig,
|
|
18
21
|
}) => {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
+
}
|
|
28
78
|
};
|
|
29
79
|
|
|
30
80
|
return (
|
|
31
|
-
<View
|
|
32
|
-
style={
|
|
81
|
+
<Animated.View
|
|
82
|
+
style={buttonContainerStyle}
|
|
33
83
|
pointerEvents={isWebViewVisible ? "none" : "auto"}
|
|
34
84
|
>
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
styles.button,
|
|
38
|
-
{ backgroundColor: isWebViewVisible ? "transparent" : brandColor }, // Set transparent to visually disable
|
|
39
|
-
isWebViewVisible && styles.disabledButton, // Add a disabled style
|
|
40
|
-
]}
|
|
41
|
-
onPress={isWebViewVisible ? undefined : onPress} // Disable interaction
|
|
42
|
-
activeOpacity={isWebViewVisible ? 1 : 0.7} // Prevent touch feedback when disabled
|
|
43
|
-
>
|
|
44
|
-
{imageUrl && !isWebViewVisible ? (
|
|
45
|
-
<Image source={{ uri: imageUrl }} style={styles.buttonImage} />
|
|
46
|
-
) : (
|
|
47
|
-
<Text style={styles.buttonText}>Chat</Text>
|
|
48
|
-
)}
|
|
49
|
-
</TouchableOpacity>
|
|
50
|
-
</View>
|
|
85
|
+
{renderLauncher()}
|
|
86
|
+
</Animated.View>
|
|
51
87
|
);
|
|
52
88
|
};
|
|
53
89
|
|
|
54
90
|
const styles = StyleSheet.create({
|
|
55
91
|
container: {
|
|
56
92
|
position: "absolute",
|
|
57
|
-
bottom: 20,
|
|
58
|
-
right: 20,
|
|
59
93
|
zIndex: 998,
|
|
60
94
|
},
|
|
61
|
-
button: {
|
|
62
|
-
borderRadius: 24,
|
|
63
|
-
width: 48,
|
|
64
|
-
height: 48,
|
|
65
|
-
justifyContent: "center",
|
|
66
|
-
alignItems: "center",
|
|
67
|
-
elevation: 5,
|
|
68
|
-
shadowColor: "#000",
|
|
69
|
-
shadowOffset: { width: 0, height: 2 },
|
|
70
|
-
shadowOpacity: 0.25,
|
|
71
|
-
shadowRadius: 3.84,
|
|
72
|
-
},
|
|
73
|
-
buttonText: {
|
|
74
|
-
color: "white",
|
|
75
|
-
fontSize: 16,
|
|
76
|
-
fontWeight: "bold",
|
|
77
|
-
},
|
|
78
|
-
buttonImage: {
|
|
79
|
-
width: 48,
|
|
80
|
-
height: 48,
|
|
81
|
-
borderRadius: 24,
|
|
82
|
-
},
|
|
83
|
-
disabledButton: {
|
|
84
|
-
backgroundColor: "rgba(0, 0, 0, 0)", // Fully transparent to disable visuals
|
|
85
|
-
},
|
|
86
95
|
});
|
|
87
96
|
|
|
88
97
|
export default FloatingButton;
|