vdb-ai-chat 1.0.1 → 1.0.2
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/dist/10.chat-widget.js +2 -0
- package/dist/10.chat-widget.js.LICENSE.txt +1 -0
- package/dist/104.chat-widget.js +1 -0
- package/dist/50.chat-widget.js +1 -0
- package/dist/521.chat-widget.js +1 -0
- package/dist/538.chat-widget.js +1 -1
- package/dist/572.chat-widget.js +1 -0
- package/dist/694.chat-widget.js +1 -0
- package/dist/chat-widget.js +1 -1
- package/lib/commonjs/api.js +4 -3
- package/lib/commonjs/api.js.map +1 -1
- package/lib/commonjs/components/BetaNotice.js +38 -0
- package/lib/commonjs/components/BetaNotice.js.map +1 -0
- package/lib/commonjs/components/ChatHeader.js +27 -20
- package/lib/commonjs/components/ChatHeader.js.map +1 -1
- package/lib/commonjs/components/ChatInput.js +21 -21
- package/lib/commonjs/components/ChatInput.js.map +1 -1
- package/lib/commonjs/components/ChatWidget.js +119 -72
- package/lib/commonjs/components/ChatWidget.js.map +1 -1
- package/lib/commonjs/components/MessageBubble.js +26 -90
- package/lib/commonjs/components/MessageBubble.js.map +1 -1
- package/lib/commonjs/components/MessageMetaRow.js +135 -0
- package/lib/commonjs/components/MessageMetaRow.js.map +1 -0
- package/lib/commonjs/components/ProductsGrid.js +139 -0
- package/lib/commonjs/components/ProductsGrid.js.map +1 -0
- package/lib/commonjs/components/ProductsList.js +22 -126
- package/lib/commonjs/components/ProductsList.js.map +1 -1
- package/lib/commonjs/components/ProductsListView.js +139 -0
- package/lib/commonjs/components/ProductsListView.js.map +1 -0
- package/lib/commonjs/components/SuggestionsRow.js +41 -23
- package/lib/commonjs/components/SuggestionsRow.js.map +1 -1
- package/lib/commonjs/components/utils.js +4 -3
- package/lib/commonjs/components/utils.js.map +1 -1
- package/lib/commonjs/index.web.js +86 -29
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/theme.js +4 -4
- package/lib/commonjs/theme.js.map +1 -1
- package/lib/module/api.js +4 -3
- package/lib/module/api.js.map +1 -1
- package/lib/module/components/BetaNotice.js +30 -0
- package/lib/module/components/BetaNotice.js.map +1 -0
- package/lib/module/components/ChatHeader.js +27 -20
- package/lib/module/components/ChatHeader.js.map +1 -1
- package/lib/module/components/ChatInput.js +21 -21
- package/lib/module/components/ChatInput.js.map +1 -1
- package/lib/module/components/ChatWidget.js +120 -73
- package/lib/module/components/ChatWidget.js.map +1 -1
- package/lib/module/components/MessageBubble.js +26 -92
- package/lib/module/components/MessageBubble.js.map +1 -1
- package/lib/module/components/MessageMetaRow.js +127 -0
- package/lib/module/components/MessageMetaRow.js.map +1 -0
- package/lib/module/components/ProductsGrid.js +133 -0
- package/lib/module/components/ProductsGrid.js.map +1 -0
- package/lib/module/components/ProductsList.js +21 -126
- package/lib/module/components/ProductsList.js.map +1 -1
- package/lib/module/components/ProductsListView.js +132 -0
- package/lib/module/components/ProductsListView.js.map +1 -0
- package/lib/module/components/SuggestionsRow.js +41 -23
- package/lib/module/components/SuggestionsRow.js.map +1 -1
- package/lib/module/components/utils.js +4 -3
- package/lib/module/components/utils.js.map +1 -1
- package/lib/module/index.web.js +86 -29
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/theme.js +4 -4
- package/lib/module/theme.js.map +1 -1
- package/lib/typescript/api.d.ts.map +1 -1
- package/lib/typescript/components/BetaNotice.d.ts +5 -0
- package/lib/typescript/components/BetaNotice.d.ts.map +1 -0
- package/lib/typescript/components/ChatHeader.d.ts +5 -2
- package/lib/typescript/components/ChatHeader.d.ts.map +1 -1
- package/lib/typescript/components/ChatInput.d.ts.map +1 -1
- package/lib/typescript/components/ChatWidget.d.ts.map +1 -1
- package/lib/typescript/components/MessageBubble.d.ts +7 -3
- package/lib/typescript/components/MessageBubble.d.ts.map +1 -1
- package/lib/typescript/components/MessageMetaRow.d.ts +14 -0
- package/lib/typescript/components/MessageMetaRow.d.ts.map +1 -0
- package/lib/typescript/components/ProductsGrid.d.ts +10 -0
- package/lib/typescript/components/ProductsGrid.d.ts.map +1 -0
- package/lib/typescript/components/ProductsList.d.ts +4 -2
- package/lib/typescript/components/ProductsList.d.ts.map +1 -1
- package/lib/typescript/components/ProductsListView.d.ts +10 -0
- package/lib/typescript/components/ProductsListView.d.ts.map +1 -0
- package/lib/typescript/components/SuggestionsRow.d.ts +2 -0
- package/lib/typescript/components/SuggestionsRow.d.ts.map +1 -1
- package/lib/typescript/components/utils.d.ts +1 -0
- package/lib/typescript/components/utils.d.ts.map +1 -1
- package/lib/typescript/index.web.d.ts +1 -1
- package/lib/typescript/index.web.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +3 -1
- package/lib/typescript/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/api.ts +4 -3
- package/src/components/BetaNotice.tsx +32 -0
- package/src/components/ChatHeader.tsx +32 -18
- package/src/components/ChatInput.tsx +20 -21
- package/src/components/ChatWidget.tsx +258 -220
- package/src/components/MessageBubble.tsx +44 -149
- package/src/components/MessageMetaRow.tsx +199 -0
- package/src/components/ProductsGrid.tsx +163 -0
- package/src/components/ProductsList.tsx +20 -146
- package/src/components/ProductsListView.tsx +149 -0
- package/src/components/SuggestionsRow.tsx +45 -21
- package/src/components/utils.ts +6 -4
- package/src/index.web.tsx +87 -32
- package/src/theme.ts +4 -4
- package/src/types.ts +3 -2
- package/dist/751.chat-widget.js +0 -1
- package/lib/commonjs/contexts/SegmentClientContext.js +0 -19
- package/lib/commonjs/contexts/SegmentClientContext.js.map +0 -1
- package/lib/module/contexts/SegmentClientContext.js +0 -10
- package/lib/module/contexts/SegmentClientContext.js.map +0 -1
- package/lib/typescript/contexts/SegmentClientContext.d.ts +0 -9
- package/lib/typescript/contexts/SegmentClientContext.d.ts.map +0 -1
- package/src/contexts/SegmentClientContext.tsx +0 -20
|
@@ -1,162 +1,36 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
StyleSheet,
|
|
5
|
-
Text,
|
|
6
|
-
ScrollView,
|
|
7
|
-
TouchableOpacity,
|
|
8
|
-
Platform,
|
|
9
|
-
} from "react-native";
|
|
10
|
-
import React, { memo } from "react";
|
|
11
|
-
|
|
12
|
-
// Use expo-image on native if available, fallback to RN Image
|
|
13
|
-
let ImageComponent: typeof Image = Image;
|
|
14
|
-
if (Platform.OS !== "web") {
|
|
15
|
-
try {
|
|
16
|
-
const ExpoImage = require("expo-image").Image;
|
|
17
|
-
if (ExpoImage) ImageComponent = ExpoImage;
|
|
18
|
-
} catch {
|
|
19
|
-
// expo-image not installed, use React Native Image
|
|
20
|
-
}
|
|
21
|
-
}
|
|
1
|
+
import React from "react";
|
|
2
|
+
import ProductsGrid from "./ProductsGrid";
|
|
3
|
+
import ProductsListView from "./ProductsListView";
|
|
22
4
|
|
|
23
5
|
interface ProductsListProps {
|
|
24
|
-
data: any;
|
|
6
|
+
data: any[];
|
|
25
7
|
onViewAll?: () => void;
|
|
26
8
|
onItemPress?: (item: any) => void;
|
|
9
|
+
variant?: "grid" | "list";
|
|
10
|
+
totalResults: number
|
|
27
11
|
}
|
|
28
12
|
|
|
29
|
-
const
|
|
13
|
+
const ProductsList: React.FC<ProductsListProps> = ({
|
|
30
14
|
data,
|
|
31
15
|
onViewAll,
|
|
32
16
|
onItemPress,
|
|
17
|
+
variant = "list",
|
|
18
|
+
totalResults,
|
|
33
19
|
}) => {
|
|
34
20
|
if (!data || !data.length) return null;
|
|
35
|
-
|
|
21
|
+
if (variant === "list") {
|
|
22
|
+
return (
|
|
23
|
+
<ProductsListView
|
|
24
|
+
data={data}
|
|
25
|
+
onViewAll={onViewAll}
|
|
26
|
+
onItemPress={onItemPress}
|
|
27
|
+
totalResults={totalResults}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
36
31
|
return (
|
|
37
|
-
<
|
|
38
|
-
<ScrollView
|
|
39
|
-
horizontal
|
|
40
|
-
showsHorizontalScrollIndicator={false}
|
|
41
|
-
contentContainerStyle={styles.listContent}
|
|
42
|
-
>
|
|
43
|
-
{data.map((item: any) => (
|
|
44
|
-
<TouchableOpacity
|
|
45
|
-
key={item.id}
|
|
46
|
-
onPress={() => {
|
|
47
|
-
onItemPress?.(item);
|
|
48
|
-
}}
|
|
49
|
-
>
|
|
50
|
-
<View key={item.id} style={styles.card}>
|
|
51
|
-
{item.image_thumb_url ? (
|
|
52
|
-
<ImageComponent
|
|
53
|
-
style={styles.image}
|
|
54
|
-
source={{ uri: item.image_thumb_url }}
|
|
55
|
-
/>
|
|
56
|
-
) : null}
|
|
57
|
-
|
|
58
|
-
<View style={styles.content}>
|
|
59
|
-
<Text numberOfLines={2} style={styles.title}>
|
|
60
|
-
{item.short_title}
|
|
61
|
-
</Text>
|
|
62
|
-
<Text style={styles.price}>${item.total_sales_price}</Text>
|
|
63
|
-
</View>
|
|
64
|
-
</View>
|
|
65
|
-
</TouchableOpacity>
|
|
66
|
-
))}
|
|
67
|
-
</ScrollView>
|
|
68
|
-
|
|
69
|
-
{/* View All Button */}
|
|
70
|
-
<TouchableOpacity
|
|
71
|
-
style={styles.button}
|
|
72
|
-
activeOpacity={0.8}
|
|
73
|
-
onPress={onViewAll}
|
|
74
|
-
>
|
|
75
|
-
<Text style={styles.buttonText}>View All {">>"}</Text>
|
|
76
|
-
</TouchableOpacity>
|
|
77
|
-
</View>
|
|
32
|
+
<ProductsGrid data={data} onViewAll={onViewAll} onItemPress={onItemPress} totalResults={totalResults} />
|
|
78
33
|
);
|
|
79
34
|
};
|
|
80
35
|
|
|
81
|
-
const styles = StyleSheet.create({
|
|
82
|
-
wrapper: {
|
|
83
|
-
paddingHorizontal: 12,
|
|
84
|
-
marginHorizontal: 12,
|
|
85
|
-
marginBottom: 12,
|
|
86
|
-
paddingTop: 6,
|
|
87
|
-
paddingBottom: 14,
|
|
88
|
-
backgroundColor: "#fff",
|
|
89
|
-
borderRadius: 8,
|
|
90
|
-
borderWidth: 1,
|
|
91
|
-
borderColor: "#e8e8e8",
|
|
92
|
-
elevation: 3,
|
|
93
|
-
shadowColor: "#000",
|
|
94
|
-
shadowOpacity: 0.08,
|
|
95
|
-
shadowOffset: { width: 0, height: 2 },
|
|
96
|
-
shadowRadius: 6,
|
|
97
|
-
},
|
|
98
|
-
|
|
99
|
-
listContent: {
|
|
100
|
-
gap: 12,
|
|
101
|
-
paddingVertical: 6,
|
|
102
|
-
},
|
|
103
|
-
|
|
104
|
-
card: {
|
|
105
|
-
width: 150,
|
|
106
|
-
backgroundColor: "#fff",
|
|
107
|
-
borderRadius: 14,
|
|
108
|
-
overflow: "hidden",
|
|
109
|
-
borderColor: "#e8e8e8",
|
|
110
|
-
borderWidth: 1,
|
|
111
|
-
elevation: 3,
|
|
112
|
-
shadowColor: "#000",
|
|
113
|
-
shadowOpacity: 0.08,
|
|
114
|
-
shadowOffset: { width: 0, height: 2 },
|
|
115
|
-
shadowRadius: 6,
|
|
116
|
-
},
|
|
117
|
-
|
|
118
|
-
image: {
|
|
119
|
-
width: "100%",
|
|
120
|
-
height: 120,
|
|
121
|
-
borderBottomWidth: 1,
|
|
122
|
-
borderBottomColor: "#e8e8e8",
|
|
123
|
-
},
|
|
124
|
-
|
|
125
|
-
content: {
|
|
126
|
-
padding: 10,
|
|
127
|
-
gap: 4,
|
|
128
|
-
},
|
|
129
|
-
|
|
130
|
-
title: {
|
|
131
|
-
fontSize: 13,
|
|
132
|
-
color: "#222",
|
|
133
|
-
fontWeight: "500",
|
|
134
|
-
},
|
|
135
|
-
|
|
136
|
-
price: {
|
|
137
|
-
marginTop: 4,
|
|
138
|
-
fontSize: 14,
|
|
139
|
-
fontWeight: "700",
|
|
140
|
-
color: "#000",
|
|
141
|
-
},
|
|
142
|
-
|
|
143
|
-
button: {
|
|
144
|
-
marginTop: 12,
|
|
145
|
-
alignSelf: "center",
|
|
146
|
-
paddingHorizontal: 20,
|
|
147
|
-
paddingVertical: 8,
|
|
148
|
-
backgroundColor: "#804195",
|
|
149
|
-
borderRadius: 20,
|
|
150
|
-
width: 300,
|
|
151
|
-
alignItems: "center",
|
|
152
|
-
},
|
|
153
|
-
|
|
154
|
-
buttonText: {
|
|
155
|
-
color: "#fff",
|
|
156
|
-
fontSize: 14,
|
|
157
|
-
fontWeight: "600",
|
|
158
|
-
},
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
const ProductsList = memo(ProductsListComponent);
|
|
162
36
|
export default ProductsList;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import React, { memo, useState } from "react";
|
|
2
|
+
import { View, StyleSheet, Text, TouchableOpacity, ScrollView, Platform, Image } from "react-native";
|
|
3
|
+
|
|
4
|
+
let ImageComponent: typeof Image = Image;
|
|
5
|
+
if (Platform.OS !== "web") {
|
|
6
|
+
try {
|
|
7
|
+
const ExpoImage = require("expo-image").Image;
|
|
8
|
+
if (ExpoImage) ImageComponent = ExpoImage;
|
|
9
|
+
} catch {
|
|
10
|
+
// expo-image not installed, use React Native Image
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface ProductsListViewProps {
|
|
15
|
+
data: any[];
|
|
16
|
+
totalResults?: number;
|
|
17
|
+
onViewAll?: () => void;
|
|
18
|
+
onItemPress?: (item: any) => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const ProductsListViewComponent: React.FC<ProductsListViewProps> = ({ data, totalResults, onViewAll, onItemPress }) => {
|
|
22
|
+
if (!data || !data.length) return null;
|
|
23
|
+
|
|
24
|
+
const [priceWidth, setPriceWidth] = useState<number>(0);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<View style={styles.wrapper}>
|
|
28
|
+
<ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={styles.listContent}>
|
|
29
|
+
{data.map((item: any, index: number) => (
|
|
30
|
+
<TouchableOpacity key={item.id} onPress={() => onItemPress?.(item)} activeOpacity={0.8}>
|
|
31
|
+
<View style={styles.row}>
|
|
32
|
+
<View style={styles.left}>
|
|
33
|
+
<Text style={styles.serial}>{index + 1}.</Text>
|
|
34
|
+
<Text
|
|
35
|
+
numberOfLines={1}
|
|
36
|
+
ellipsizeMode="tail"
|
|
37
|
+
style={styles.title}
|
|
38
|
+
>
|
|
39
|
+
{item.short_title}
|
|
40
|
+
</Text>
|
|
41
|
+
</View>
|
|
42
|
+
{item.total_sales_price ? (
|
|
43
|
+
<View style={[styles.priceContainer, priceWidth ? { width: priceWidth } : null]}>
|
|
44
|
+
<Text
|
|
45
|
+
style={styles.price}
|
|
46
|
+
onLayout={(e) => {
|
|
47
|
+
const w = e.nativeEvent.layout.width;
|
|
48
|
+
if (w > priceWidth) setPriceWidth(w);
|
|
49
|
+
}}
|
|
50
|
+
>
|
|
51
|
+
${item.total_sales_price}
|
|
52
|
+
</Text>
|
|
53
|
+
</View>
|
|
54
|
+
) : (
|
|
55
|
+
<></>
|
|
56
|
+
)}
|
|
57
|
+
</View>
|
|
58
|
+
</TouchableOpacity>
|
|
59
|
+
))}
|
|
60
|
+
</ScrollView>
|
|
61
|
+
|
|
62
|
+
<TouchableOpacity style={styles.button} activeOpacity={0.8} onPress={onViewAll}>
|
|
63
|
+
<Text style={styles.buttonText}>{`View All ${totalResults} Results`}</Text>
|
|
64
|
+
<Image
|
|
65
|
+
source={{
|
|
66
|
+
uri: "https://cdn.vdbapp.com/ai/chat-widget/assets/img/right.svg",
|
|
67
|
+
}}
|
|
68
|
+
resizeMode="contain"
|
|
69
|
+
style={{ width: 20, height: 20, tintColor: "#fff" }}
|
|
70
|
+
/>
|
|
71
|
+
</TouchableOpacity>
|
|
72
|
+
</View>
|
|
73
|
+
);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const styles = StyleSheet.create({
|
|
77
|
+
wrapper: {
|
|
78
|
+
paddingHorizontal: 8,
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
listContent: {
|
|
82
|
+
// No extra padding/margins
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
row: {
|
|
86
|
+
flexDirection: "row",
|
|
87
|
+
justifyContent: "space-between",
|
|
88
|
+
alignItems: "center",
|
|
89
|
+
paddingVertical: 6,
|
|
90
|
+
backgroundColor: "#fff",
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
left: {
|
|
94
|
+
flexDirection: "row",
|
|
95
|
+
alignItems: "center",
|
|
96
|
+
gap: 4,
|
|
97
|
+
flex: 1,
|
|
98
|
+
minWidth: 0,
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
serial: {
|
|
102
|
+
fontSize: 13,
|
|
103
|
+
color: "#020001",
|
|
104
|
+
fontWeight: "500",
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
title: {
|
|
108
|
+
flexShrink: 1,
|
|
109
|
+
fontSize: 13,
|
|
110
|
+
color: "#3378F6",
|
|
111
|
+
fontWeight: "500",
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
price: {
|
|
115
|
+
fontSize: 13,
|
|
116
|
+
fontWeight: "500",
|
|
117
|
+
color: "#020001",
|
|
118
|
+
textAlign: "left",
|
|
119
|
+
},
|
|
120
|
+
priceContainer: {
|
|
121
|
+
alignItems: "flex-start",
|
|
122
|
+
justifyContent: "center",
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
button: {
|
|
126
|
+
marginTop: 12,
|
|
127
|
+
marginHorizontal: 12,
|
|
128
|
+
alignSelf: "center",
|
|
129
|
+
paddingHorizontal: 16,
|
|
130
|
+
paddingVertical: 6,
|
|
131
|
+
backgroundColor: "#292735",
|
|
132
|
+
borderRadius: 8,
|
|
133
|
+
alignItems: "center",
|
|
134
|
+
gap: 16,
|
|
135
|
+
width: "100%",
|
|
136
|
+
flexDirection: "row",
|
|
137
|
+
justifyContent: "center",
|
|
138
|
+
alignContent: "center",
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
buttonText: {
|
|
142
|
+
color: "#fff",
|
|
143
|
+
fontSize: 14,
|
|
144
|
+
fontWeight: "600",
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const ProductsListView = memo(ProductsListViewComponent);
|
|
149
|
+
export default ProductsListView;
|
|
@@ -10,24 +10,30 @@ import {
|
|
|
10
10
|
interface SuggestionsRowProps {
|
|
11
11
|
suggestions: string[];
|
|
12
12
|
onSelect: (value: string) => void;
|
|
13
|
+
/** Inline variant renders inside a message bubble, without outer padding/background */
|
|
14
|
+
variant?: "inline" | "default";
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
const SuggestionsRowComponent: React.FC<SuggestionsRowProps> = ({
|
|
16
18
|
suggestions,
|
|
17
19
|
onSelect,
|
|
20
|
+
variant = "default",
|
|
18
21
|
}) => {
|
|
19
22
|
if (!suggestions.length) return null;
|
|
20
23
|
|
|
21
24
|
return (
|
|
22
25
|
<ScrollView
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
showsVerticalScrollIndicator={false}
|
|
27
|
+
contentContainerStyle={[
|
|
28
|
+
styles.container,
|
|
29
|
+
variant === "inline" && styles.containerInline,
|
|
30
|
+
]}
|
|
31
|
+
style={[styles.scroll, variant === "inline" && styles.scrollInline]}
|
|
26
32
|
>
|
|
27
33
|
{suggestions.map((s) => (
|
|
28
34
|
<TouchableOpacity
|
|
29
35
|
key={s}
|
|
30
|
-
style={styles.chip}
|
|
36
|
+
style={[styles.chip, variant === "inline" && styles.chipInline]}
|
|
31
37
|
onPress={() => onSelect(s)}
|
|
32
38
|
activeOpacity={0.75}
|
|
33
39
|
>
|
|
@@ -39,33 +45,51 @@ const SuggestionsRowComponent: React.FC<SuggestionsRowProps> = ({
|
|
|
39
45
|
};
|
|
40
46
|
|
|
41
47
|
const styles = StyleSheet.create({
|
|
48
|
+
scroll: {
|
|
49
|
+
maxWidth: "80%",
|
|
50
|
+
alignSelf: "flex-start",
|
|
51
|
+
paddingHorizontal: 8,
|
|
52
|
+
},
|
|
53
|
+
scrollInline: {
|
|
54
|
+
maxWidth: "100%",
|
|
55
|
+
alignSelf: "stretch",
|
|
56
|
+
paddingHorizontal: 0,
|
|
57
|
+
},
|
|
42
58
|
container: {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
59
|
+
paddingVertical: 0,
|
|
60
|
+
flexDirection: "column",
|
|
61
|
+
justifyContent: "center",
|
|
62
|
+
alignItems: "center",
|
|
63
|
+
gap: 4,
|
|
64
|
+
alignSelf: "stretch",
|
|
65
|
+
// backgroundColor: "#EDEDF2",
|
|
66
|
+
},
|
|
67
|
+
containerInline: {
|
|
68
|
+
// inside bubble, rely on bubble background
|
|
69
|
+
gap: 6,
|
|
47
70
|
},
|
|
48
71
|
|
|
49
72
|
chip: {
|
|
50
|
-
backgroundColor: "#
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
73
|
+
backgroundColor: "#EDEDF2",
|
|
74
|
+
borderRadius: 4,
|
|
75
|
+
paddingHorizontal: 8,
|
|
76
|
+
paddingVertical: 6,
|
|
77
|
+
width: "100%",
|
|
78
|
+
alignSelf: "stretch",
|
|
79
|
+
justifyContent: "center",
|
|
80
|
+
alignItems: "flex-start",
|
|
81
|
+
borderWidth: 1,
|
|
82
|
+
borderColor: "#E0E0E0",
|
|
83
|
+
},
|
|
84
|
+
chipInline: {
|
|
85
|
+
backgroundColor: "#FFFFFF",
|
|
62
86
|
},
|
|
63
87
|
|
|
64
88
|
chipText: {
|
|
65
89
|
fontSize: 14,
|
|
66
90
|
color: "#1A1A1A",
|
|
67
91
|
fontWeight: "500",
|
|
68
|
-
|
|
92
|
+
textAlign: "left",
|
|
69
93
|
},
|
|
70
94
|
});
|
|
71
95
|
|
package/src/components/utils.ts
CHANGED
|
@@ -31,12 +31,13 @@ export const fetchConversationId = async (
|
|
|
31
31
|
if (!conversations) return null;
|
|
32
32
|
|
|
33
33
|
let priceMode = _priceMode;
|
|
34
|
-
if (
|
|
35
|
-
const
|
|
36
|
-
"
|
|
34
|
+
if (priceMode === undefined) {
|
|
35
|
+
const userInfo = await Storage.getJSON<UserDetails>(
|
|
36
|
+
"persist:userInfo",
|
|
37
37
|
{}
|
|
38
38
|
);
|
|
39
|
-
|
|
39
|
+
const userData = userInfo?.user as UserDetails;
|
|
40
|
+
priceMode = String(userData?.price_mode) || "";
|
|
40
41
|
}
|
|
41
42
|
return conversations[priceMode]?.conversation_id || null;
|
|
42
43
|
};
|
|
@@ -51,6 +52,7 @@ export interface UserDetails {
|
|
|
51
52
|
country?: string;
|
|
52
53
|
price_mode?: string | number;
|
|
53
54
|
plan_details?: any;
|
|
55
|
+
user?: any;
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
export const getUserDetails = async (): Promise<UserDetails | null> => {
|
package/src/index.web.tsx
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Platform, StyleSheet, View } from "react-native";
|
|
2
3
|
import { createRoot } from "react-dom/client";
|
|
3
|
-
import {
|
|
4
|
+
import { AnalyticsBrowser } from "@segment/analytics-next";
|
|
5
|
+
import { AnalyticsClientProvider } from "./contexts/AnalyticsClientContext";
|
|
4
6
|
import { ChatWidget } from "./components/ChatWidget";
|
|
5
7
|
import type { ChatTheme } from "./types";
|
|
6
|
-
import {
|
|
7
|
-
import { AnalyticsClientProvider } from "./contexts/AnalyticsClientContext";
|
|
8
|
+
import { getUserDetails } from "./components/utils";
|
|
8
9
|
|
|
9
10
|
export function renderChatApp(
|
|
10
11
|
domElement: HTMLElement,
|
|
@@ -13,31 +14,45 @@ export function renderChatApp(
|
|
|
13
14
|
onClose?: () => void,
|
|
14
15
|
onClearChat?: () => void,
|
|
15
16
|
segmentWriteKey?: string,
|
|
17
|
+
onViewAllPress?: (url: string, payload: any) => void,
|
|
18
|
+
onItemPress?: (url: string, item: any) => void,
|
|
19
|
+
isBetaMode?: boolean,
|
|
16
20
|
) {
|
|
17
21
|
const root = createRoot(domElement);
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
.
|
|
35
|
-
|
|
36
|
-
});
|
|
37
|
-
} catch (e) {
|
|
38
|
-
console.warn("Segment analytics load failed", e);
|
|
22
|
+
|
|
23
|
+
// Bridge to parent RN WebView / browser
|
|
24
|
+
const sendToParent = (type: string, payload: any) => {
|
|
25
|
+
const msg = { source: "vdb-ai-chat", type, payload };
|
|
26
|
+
const json = JSON.stringify(msg);
|
|
27
|
+
|
|
28
|
+
// RN WebView bridge
|
|
29
|
+
const rnwv = (window as any).ReactNativeWebView;
|
|
30
|
+
if (rnwv?.postMessage) {
|
|
31
|
+
rnwv.postMessage(json);
|
|
32
|
+
} else {
|
|
33
|
+
// Browser/iframe
|
|
34
|
+
try {
|
|
35
|
+
window.parent?.postMessage(msg, "*");
|
|
36
|
+
} catch {}
|
|
37
|
+
try {
|
|
38
|
+
window.dispatchEvent(new CustomEvent("vdb-ai-event", { detail: msg }));
|
|
39
|
+
} catch {}
|
|
39
40
|
}
|
|
40
|
-
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const handleViewAll = (url: string, payload: any) => {
|
|
44
|
+
onViewAllPress?.(url, payload);
|
|
45
|
+
sendToParent("ai_view_all", { url, payload });
|
|
46
|
+
onClose?.();
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const handleItemPress = (url: string, item: any) => {
|
|
50
|
+
onItemPress?.(url, item);
|
|
51
|
+
sendToParent("ai_item_click", { url, item });
|
|
52
|
+
onClose?.();
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Build base content and render immediately
|
|
41
56
|
const content = (
|
|
42
57
|
<View style={styles.root}>
|
|
43
58
|
<ChatWidget
|
|
@@ -45,16 +60,56 @@ export function renderChatApp(
|
|
|
45
60
|
theme={theme}
|
|
46
61
|
onClose={onClose}
|
|
47
62
|
onClearChat={onClearChat}
|
|
63
|
+
onViewAllPress={handleViewAll}
|
|
64
|
+
onItemPress={handleItemPress}
|
|
65
|
+
isBetaMode={isBetaMode}
|
|
48
66
|
/>
|
|
49
67
|
</View>
|
|
50
68
|
);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
|
|
69
|
+
root.render(content);
|
|
70
|
+
|
|
71
|
+
if (Platform.OS === "web" && segmentWriteKey) {
|
|
72
|
+
try {
|
|
73
|
+
const isEdge =
|
|
74
|
+
typeof navigator !== "undefined" &&
|
|
75
|
+
(navigator.userAgent.includes("Edg") || navigator.userAgent.includes("Edge"));
|
|
76
|
+
|
|
77
|
+
const clientPromise = AnalyticsBrowser.load({
|
|
78
|
+
writeKey: segmentWriteKey,
|
|
79
|
+
trackAppLifecycleEvents: false as any,
|
|
80
|
+
flushAt: (isEdge ? 1 : 10) as any,
|
|
81
|
+
flushInterval: (10000 as any),
|
|
82
|
+
maxBatchSize: (isEdge ? 1 : 10) as any,
|
|
83
|
+
} as any);
|
|
84
|
+
|
|
85
|
+
clientPromise
|
|
86
|
+
.then( async ([client]: any) => {
|
|
87
|
+
(client as any)?.debug?.(true);
|
|
88
|
+
(window as any).analytics = client;
|
|
56
89
|
|
|
57
|
-
|
|
90
|
+
client.page?.();
|
|
91
|
+
try {
|
|
92
|
+
const user = await getUserDetails();
|
|
93
|
+
const userId = user?.id;
|
|
94
|
+
if (userId) client.identify(userId);
|
|
95
|
+
} catch {}
|
|
96
|
+
|
|
97
|
+
const contentWithAnalytics = (
|
|
98
|
+
<AnalyticsClientProvider client={client}>
|
|
99
|
+
{content}
|
|
100
|
+
</AnalyticsClientProvider>
|
|
101
|
+
);
|
|
102
|
+
root.render(contentWithAnalytics);
|
|
103
|
+
})
|
|
104
|
+
.catch((e: any) => {
|
|
105
|
+
// eslint-disable-next-line no-console
|
|
106
|
+
console.error("[Segment] init failed", e);
|
|
107
|
+
});
|
|
108
|
+
} catch (e) {
|
|
109
|
+
// eslint-disable-next-line no-console
|
|
110
|
+
console.error("[Segment] init failed", e);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
58
113
|
}
|
|
59
114
|
|
|
60
115
|
const styles = StyleSheet.create({
|
package/src/theme.ts
CHANGED
|
@@ -4,14 +4,14 @@ export const defaultTheme: ChatTheme = {
|
|
|
4
4
|
primaryColor: "#0b93f6",
|
|
5
5
|
backgroundColor: "#ffffff",
|
|
6
6
|
inputColor: "#FFF",
|
|
7
|
-
inputBackgroundColor: "#
|
|
7
|
+
inputBackgroundColor: "#FFFFFF",
|
|
8
8
|
inputBorderRadius: 8,
|
|
9
9
|
inputTextColor: "#000000",
|
|
10
|
-
userBubbleColor: "#
|
|
10
|
+
userBubbleColor: "#4F4E57",
|
|
11
11
|
userTextColor: "#ffffff",
|
|
12
|
-
botBubbleColor: "#
|
|
12
|
+
botBubbleColor: "#EDEDF2",
|
|
13
13
|
botTextColor: "#000000",
|
|
14
|
-
borderRadius:
|
|
14
|
+
borderRadius: 8,
|
|
15
15
|
fontFamily: undefined,
|
|
16
16
|
fontSize: 16,
|
|
17
17
|
};
|
package/src/types.ts
CHANGED
|
@@ -7,8 +7,7 @@ export interface ChatMessage {
|
|
|
7
7
|
createdAt: number;
|
|
8
8
|
isLoading?: boolean;
|
|
9
9
|
suggestions?: string[];
|
|
10
|
-
|
|
11
|
-
reaction?: "0" | "1" | "2";
|
|
10
|
+
reaction?: string;
|
|
12
11
|
search_payload?: Record<string, any>;
|
|
13
12
|
}
|
|
14
13
|
|
|
@@ -44,6 +43,8 @@ export interface ChatWidgetProps {
|
|
|
44
43
|
onViewAllPress?: (deepLinkUrl: string, payload: any) => void;
|
|
45
44
|
/** Called when a product item is pressed. Receives the deep link URL and item data. */
|
|
46
45
|
onItemPress?: (deepLinkUrl: string, item: any) => void;
|
|
46
|
+
/** When true, shows beta labels and disclaimer. */
|
|
47
|
+
isBetaMode?: boolean;
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
export interface ChatWidgetRef {
|