vdb-ai-chat 1.0.4 → 1.0.6
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/chat-widget.js +1 -1
- package/lib/commonjs/api.js +37 -5
- package/lib/commonjs/api.js.map +1 -1
- package/lib/commonjs/components/BetaNotice.js +5 -2
- package/lib/commonjs/components/BetaNotice.js.map +1 -1
- package/lib/commonjs/components/ChatHeader.js +20 -5
- package/lib/commonjs/components/ChatHeader.js.map +1 -1
- package/lib/commonjs/components/ChatWidget.js +58 -23
- package/lib/commonjs/components/ChatWidget.js.map +1 -1
- package/lib/commonjs/components/LazyProductsFetcher.js +4 -3
- package/lib/commonjs/components/LazyProductsFetcher.js.map +1 -1
- package/lib/commonjs/components/MessageMetaRow.js +18 -8
- package/lib/commonjs/components/MessageMetaRow.js.map +1 -1
- package/lib/commonjs/components/ProductsList.js +12 -5
- package/lib/commonjs/components/ProductsList.js.map +1 -1
- package/lib/commonjs/components/ProductsListView.js +142 -29
- package/lib/commonjs/components/ProductsListView.js.map +1 -1
- package/lib/commonjs/components/utils.js +44 -2
- package/lib/commonjs/components/utils.js.map +1 -1
- package/lib/commonjs/storage.js +13 -0
- package/lib/commonjs/storage.js.map +1 -1
- package/lib/module/api.js +37 -5
- package/lib/module/api.js.map +1 -1
- package/lib/module/components/BetaNotice.js +5 -2
- package/lib/module/components/BetaNotice.js.map +1 -1
- package/lib/module/components/ChatHeader.js +20 -5
- package/lib/module/components/ChatHeader.js.map +1 -1
- package/lib/module/components/ChatWidget.js +59 -24
- package/lib/module/components/ChatWidget.js.map +1 -1
- package/lib/module/components/LazyProductsFetcher.js +4 -3
- package/lib/module/components/LazyProductsFetcher.js.map +1 -1
- package/lib/module/components/MessageMetaRow.js +18 -8
- package/lib/module/components/MessageMetaRow.js.map +1 -1
- package/lib/module/components/ProductsList.js +12 -5
- package/lib/module/components/ProductsList.js.map +1 -1
- package/lib/module/components/ProductsListView.js +144 -31
- package/lib/module/components/ProductsListView.js.map +1 -1
- package/lib/module/components/utils.js +39 -0
- package/lib/module/components/utils.js.map +1 -1
- package/lib/module/storage.js +13 -0
- package/lib/module/storage.js.map +1 -1
- package/lib/typescript/api.d.ts +1 -1
- package/lib/typescript/api.d.ts.map +1 -1
- package/lib/typescript/components/BetaNotice.d.ts.map +1 -1
- package/lib/typescript/components/ChatHeader.d.ts.map +1 -1
- package/lib/typescript/components/ChatWidget.d.ts.map +1 -1
- package/lib/typescript/components/LazyProductsFetcher.d.ts +1 -0
- package/lib/typescript/components/LazyProductsFetcher.d.ts.map +1 -1
- package/lib/typescript/components/MessageMetaRow.d.ts.map +1 -1
- package/lib/typescript/components/ProductsList.d.ts +2 -1
- package/lib/typescript/components/ProductsList.d.ts.map +1 -1
- package/lib/typescript/components/ProductsListView.d.ts +2 -1
- package/lib/typescript/components/ProductsListView.d.ts.map +1 -1
- package/lib/typescript/components/utils.d.ts +11 -0
- package/lib/typescript/components/utils.d.ts.map +1 -1
- package/lib/typescript/storage.d.ts.map +1 -1
- package/package.json +7 -6
- package/src/api.ts +57 -6
- package/src/components/BetaNotice.tsx +3 -0
- package/src/components/ChatHeader.tsx +18 -3
- package/src/components/ChatWidget.tsx +52 -19
- package/src/components/LazyProductsFetcher.tsx +4 -2
- package/src/components/MessageMetaRow.tsx +14 -14
- package/src/components/ProductsList.tsx +14 -3
- package/src/components/ProductsListView.tsx +288 -134
- package/src/components/utils.ts +44 -4
- package/src/storage.ts +13 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vdb-ai-chat",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Cross-platform AI chat widget for React Native and Web",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -40,11 +40,11 @@
|
|
|
40
40
|
"url": ""
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"react": ">=17.0.0",
|
|
44
|
-
"react-native": ">=0.64.0",
|
|
45
|
-
"expo-image": ">=1.0.0",
|
|
46
43
|
"@segment/analytics-next": ">=1.0.0",
|
|
47
|
-
"@segment/analytics-react-native": ">=2.0.0"
|
|
44
|
+
"@segment/analytics-react-native": ">=2.0.0",
|
|
45
|
+
"expo-image": ">=1.0.0",
|
|
46
|
+
"react": ">=17.0.0",
|
|
47
|
+
"react-native": ">=0.64.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependenciesMeta": {
|
|
50
50
|
"react-native": {
|
|
@@ -61,7 +61,8 @@
|
|
|
61
61
|
}
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"react-native-web": "^0.19.13"
|
|
64
|
+
"react-native-web": "^0.19.13",
|
|
65
|
+
"react-responsive": "^10.0.1"
|
|
65
66
|
},
|
|
66
67
|
"devDependencies": {
|
|
67
68
|
"@babel/core": "^7.26.0",
|
package/src/api.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FeedbackAction } from "./components/utils";
|
|
1
|
+
import { FeedbackAction, UserDetails } from "./components/utils";
|
|
2
2
|
import { Storage } from "./storage";
|
|
3
3
|
import type { ChatMessage } from "./types";
|
|
4
4
|
|
|
@@ -6,7 +6,9 @@ import type { ChatMessage } from "./types";
|
|
|
6
6
|
export type ChatApiParams = Record<string, any>;
|
|
7
7
|
|
|
8
8
|
async function buildHeaders() {
|
|
9
|
-
const
|
|
9
|
+
const userInfo = await Storage.getJSON<UserDetails>("persist:userInfo", {});
|
|
10
|
+
const userData = JSON.parse(userInfo?.user || "{}");
|
|
11
|
+
const token = userData?.token || "";
|
|
10
12
|
const iid = (await Storage.getItem("persist:appState")) || "{}";
|
|
11
13
|
let installationId = "";
|
|
12
14
|
try {
|
|
@@ -41,7 +43,7 @@ export function normaliseMessages(raw: any): ChatMessage[] {
|
|
|
41
43
|
: typeof m.createdAt === "number"
|
|
42
44
|
? m.createdAt
|
|
43
45
|
: Date.now(),
|
|
44
|
-
search_payload: m.search_payload,
|
|
46
|
+
search_payload: m.search_payload ?? {},
|
|
45
47
|
reaction: String(m.reaction ?? "0"),
|
|
46
48
|
suggestions: Array.isArray(m.suggestions) ? m.suggestions : undefined,
|
|
47
49
|
}));
|
|
@@ -168,7 +170,50 @@ export async function clearChatHistory(
|
|
|
168
170
|
await Storage.setJSON("vdbchat_conversations", updatedConversations);
|
|
169
171
|
}
|
|
170
172
|
|
|
171
|
-
export async function getProducts(
|
|
173
|
+
export async function getProducts(
|
|
174
|
+
params: ChatApiParams,
|
|
175
|
+
priceMode: string | null
|
|
176
|
+
): Promise<any> {
|
|
177
|
+
const userInfo = await Storage.getJSON<UserDetails>("persist:userInfo", {});
|
|
178
|
+
const { activeProduct } = JSON.parse(
|
|
179
|
+
(await Storage.getItem("persist:appState")) || "{}"
|
|
180
|
+
) as { activeProduct?: string };
|
|
181
|
+
const searchResultViewTypeMap = JSON.parse(
|
|
182
|
+
userInfo?.searchResultViewType || "{}"
|
|
183
|
+
);
|
|
184
|
+
const searchResultViewType =
|
|
185
|
+
searchResultViewTypeMap?.[activeProduct ?? "default"];
|
|
186
|
+
const rawState = JSON.parse(
|
|
187
|
+
(await Storage.getItem("persist:searchFilters")) || "{}"
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
const sectionName =
|
|
191
|
+
typeof rawState?.sectionName === "string"
|
|
192
|
+
? JSON.parse(rawState.sectionName)
|
|
193
|
+
: rawState?.sectionName;
|
|
194
|
+
|
|
195
|
+
const rootState = JSON.parse((await Storage.getItem("persist:root")) || "{}");
|
|
196
|
+
const selectedGroups =
|
|
197
|
+
typeof rootState?.selectedGroups === "string"
|
|
198
|
+
? JSON.parse(rootState.selectedGroups)
|
|
199
|
+
: rootState?.selectedGroups || {};
|
|
200
|
+
|
|
201
|
+
let mGroupIds = selectedGroups[activeProduct ?? ""];
|
|
202
|
+
let vdbSetting = false;
|
|
203
|
+
if (mGroupIds) {
|
|
204
|
+
if (mGroupIds.includes(0)) {
|
|
205
|
+
vdbSetting = true;
|
|
206
|
+
mGroupIds = mGroupIds.filter((groupId: number) => groupId !== 0);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
let queryParamList: Record<string, any> = {};
|
|
211
|
+
if (mGroupIds && mGroupIds.length > 0) {
|
|
212
|
+
queryParamList = Object.assign(queryParamList, {
|
|
213
|
+
group_ids: mGroupIds,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
172
217
|
const res = await fetch(
|
|
173
218
|
"https://pdpdemo1.demo.customvirtual.app/v3/vdb/search_diamonds",
|
|
174
219
|
{
|
|
@@ -177,9 +222,15 @@ export async function getProducts(params: ChatApiParams): Promise<any> {
|
|
|
177
222
|
body: JSON.stringify({
|
|
178
223
|
vdb: {
|
|
179
224
|
...params,
|
|
225
|
+
...queryParamList,
|
|
180
226
|
page_size: 5,
|
|
181
|
-
price_mode:
|
|
182
|
-
vdb_setting: "true",
|
|
227
|
+
price_mode: priceMode,
|
|
228
|
+
vdb_setting: vdbSetting ? "true" : "false",
|
|
229
|
+
results_view_type: searchResultViewType || "grid",
|
|
230
|
+
featured: "false",
|
|
231
|
+
pair: sectionName === "Single Stones" ? "other" : "pair",
|
|
232
|
+
with_available_items: false,
|
|
233
|
+
page_number: 1,
|
|
183
234
|
},
|
|
184
235
|
}),
|
|
185
236
|
}
|
|
@@ -18,6 +18,8 @@ const styles = StyleSheet.create({
|
|
|
18
18
|
paddingVertical: 6,
|
|
19
19
|
paddingHorizontal: 16,
|
|
20
20
|
backgroundColor: "transparent",
|
|
21
|
+
alignItems: "center",
|
|
22
|
+
justifyContent: "center",
|
|
21
23
|
},
|
|
22
24
|
text: {
|
|
23
25
|
color: "#4F4E57",
|
|
@@ -26,6 +28,7 @@ const styles = StyleSheet.create({
|
|
|
26
28
|
fontStyle: "normal",
|
|
27
29
|
fontWeight: "400",
|
|
28
30
|
lineHeight: 18,
|
|
31
|
+
textAlign: "center",
|
|
29
32
|
},
|
|
30
33
|
});
|
|
31
34
|
|
|
@@ -61,7 +61,10 @@ const ChatHeader: React.FC<ChatHeaderProps> = ({
|
|
|
61
61
|
<View style={styles.container}>
|
|
62
62
|
<View style={styles.logoContainer}>
|
|
63
63
|
<VDBLogo />
|
|
64
|
-
<Text style={styles.title}>
|
|
64
|
+
<Text style={styles.title}>
|
|
65
|
+
AI Search
|
|
66
|
+
{isBetaMode && <Text style={styles.betaSup}>Beta</Text>}
|
|
67
|
+
</Text>
|
|
65
68
|
</View>
|
|
66
69
|
<View style={styles.buttonContainer}>
|
|
67
70
|
<TouchableOpacity onPress={() => {
|
|
@@ -79,6 +82,7 @@ const ChatHeader: React.FC<ChatHeaderProps> = ({
|
|
|
79
82
|
onClose?.();
|
|
80
83
|
}}
|
|
81
84
|
>
|
|
85
|
+
<Text style={styles.clearChatText}>Minimize</Text>
|
|
82
86
|
<CloseIcon />
|
|
83
87
|
</TouchableOpacity>
|
|
84
88
|
</View>
|
|
@@ -105,13 +109,24 @@ const styles = StyleSheet.create({
|
|
|
105
109
|
fontWeight: "500",
|
|
106
110
|
color: "#020001",
|
|
107
111
|
},
|
|
112
|
+
betaSup: {
|
|
113
|
+
fontSize: 13,
|
|
114
|
+
fontWeight: "400",
|
|
115
|
+
color: "#4F4E57",
|
|
116
|
+
marginLeft: 4,
|
|
117
|
+
position: "relative",
|
|
118
|
+
top: -6,
|
|
119
|
+
},
|
|
108
120
|
closeButton: {
|
|
109
121
|
backgroundColor: "#FFF",
|
|
110
|
-
width: 24,
|
|
111
122
|
height: 24,
|
|
112
123
|
borderRadius: 12,
|
|
124
|
+
paddingHorizontal: 8,
|
|
113
125
|
justifyContent: "center",
|
|
114
126
|
alignItems: "center",
|
|
127
|
+
display: "flex",
|
|
128
|
+
flexDirection: "row",
|
|
129
|
+
gap: 4,
|
|
115
130
|
},
|
|
116
131
|
closeIconText: {
|
|
117
132
|
fontSize: 14,
|
|
@@ -126,7 +141,7 @@ const styles = StyleSheet.create({
|
|
|
126
141
|
clearChatText: {
|
|
127
142
|
color: "#4F4E57",
|
|
128
143
|
fontSize: 14,
|
|
129
|
-
fontWeight: "
|
|
144
|
+
fontWeight: "400",
|
|
130
145
|
textDecorationLine: "none",
|
|
131
146
|
},
|
|
132
147
|
buttonContainer: {
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
FeedbackAction,
|
|
39
39
|
formatToTime,
|
|
40
40
|
getUserDetails,
|
|
41
|
+
useDeviceType,
|
|
41
42
|
UserDetails,
|
|
42
43
|
} from "./utils";
|
|
43
44
|
import { useUserAnalytics } from "../hooks/useAnalytics";
|
|
@@ -69,10 +70,8 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
69
70
|
);
|
|
70
71
|
const [typingMessageId, setTypingMessageId] = useState<string | null>(null);
|
|
71
72
|
const [typingFullText, setTypingFullText] = useState<string>("");
|
|
72
|
-
const [assistantResponse, setAssistantResponse] = useState<
|
|
73
|
-
ChatMessage | undefined
|
|
74
|
-
>(undefined);
|
|
75
73
|
const [scrollY, setScrollY] = useState(0);
|
|
74
|
+
const [autoScroll, setAutoScroll] = useState(true);
|
|
76
75
|
const [productsByMsg, setProductsByMsg] = useState<Record<string, any>>({});
|
|
77
76
|
const [reloadLoadingIds, setReloadLoadingIds] = useState<Set<string>>(
|
|
78
77
|
new Set()
|
|
@@ -86,13 +85,19 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
86
85
|
const theme = useMemo(() => mergeTheme(themeOverrides), [themeOverrides]);
|
|
87
86
|
const { _identify } = useUserAnalytics();
|
|
88
87
|
const betaActive = Boolean(isBetaModeProp);
|
|
88
|
+
const { isTablet } = useDeviceType();
|
|
89
89
|
const noResultsText =
|
|
90
90
|
"No results found for your search. Try adjusting your filters or query.";
|
|
91
91
|
useEffect(() => {
|
|
92
92
|
const loadAuthData = async () => {
|
|
93
93
|
try {
|
|
94
94
|
if (!userTokenProp) {
|
|
95
|
-
const
|
|
95
|
+
const userInfo = await Storage.getJSON<UserDetails>(
|
|
96
|
+
"persist:userInfo",
|
|
97
|
+
{}
|
|
98
|
+
);
|
|
99
|
+
const userData = JSON.parse(userInfo?.user || "{}");
|
|
100
|
+
const token = userData?.token || "";
|
|
96
101
|
if (token) {
|
|
97
102
|
setUserToken(token);
|
|
98
103
|
}
|
|
@@ -104,11 +109,10 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
104
109
|
loadAuthData();
|
|
105
110
|
}, [userTokenProp]);
|
|
106
111
|
|
|
107
|
-
const onViewAll =
|
|
108
|
-
const searchPayload = JSON.stringify(
|
|
109
|
-
const payload =
|
|
112
|
+
const onViewAll = (item: any) => {
|
|
113
|
+
const searchPayload = JSON.stringify(item?.search_payload);
|
|
114
|
+
const payload = item?.search_payload;
|
|
110
115
|
if (!payload) return;
|
|
111
|
-
|
|
112
116
|
const domain = apiUrl.split("v3");
|
|
113
117
|
const deepLinkUrl = `${domain[0]}webapp/${
|
|
114
118
|
payload.lab_grown === "true" ? "lab-grown-diamonds" : "natural-diamonds"
|
|
@@ -121,7 +125,7 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
121
125
|
} else if (Platform.OS === "web") {
|
|
122
126
|
window.open(deepLinkUrl, "_parent");
|
|
123
127
|
}
|
|
124
|
-
}
|
|
128
|
+
};
|
|
125
129
|
|
|
126
130
|
const onItemPress = useCallback(
|
|
127
131
|
(item: any) => {
|
|
@@ -213,6 +217,7 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
213
217
|
"Search Natural Diamonds",
|
|
214
218
|
"Search Lab-Grown Diamonds",
|
|
215
219
|
],
|
|
220
|
+
search_payload: {},
|
|
216
221
|
reaction: "0",
|
|
217
222
|
};
|
|
218
223
|
setMessages([initialAssistant]);
|
|
@@ -260,6 +265,7 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
260
265
|
createdAt: Date.now(),
|
|
261
266
|
reaction: "0",
|
|
262
267
|
isLoading: true,
|
|
268
|
+
search_payload: {},
|
|
263
269
|
};
|
|
264
270
|
|
|
265
271
|
setMessages((prev) => [...prev, userMessage, loadingMessage]);
|
|
@@ -296,8 +302,6 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
296
302
|
(m) => m.role === "assistant"
|
|
297
303
|
);
|
|
298
304
|
|
|
299
|
-
setAssistantResponse(latestAssistant);
|
|
300
|
-
|
|
301
305
|
if (latestAssistant?.text) {
|
|
302
306
|
if (
|
|
303
307
|
latestAssistant?.search_payload &&
|
|
@@ -305,7 +309,8 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
305
309
|
Object.keys(latestAssistant.search_payload).length > 0
|
|
306
310
|
) {
|
|
307
311
|
const productsResult = await getProducts(
|
|
308
|
-
latestAssistant.search_payload
|
|
312
|
+
latestAssistant.search_payload,
|
|
313
|
+
priceMode as string
|
|
309
314
|
);
|
|
310
315
|
|
|
311
316
|
const hasDiamonds =
|
|
@@ -328,6 +333,7 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
328
333
|
id: latestAssistant?.id ?? msg.id,
|
|
329
334
|
text: noResultsText,
|
|
330
335
|
isLoading: false,
|
|
336
|
+
search_payload: latestAssistant.search_payload ?? {},
|
|
331
337
|
}
|
|
332
338
|
: msg
|
|
333
339
|
)
|
|
@@ -351,6 +357,7 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
351
357
|
text: "",
|
|
352
358
|
isLoading: false,
|
|
353
359
|
suggestions: latestAssistant.suggestions,
|
|
360
|
+
search_payload: latestAssistant.search_payload ?? {},
|
|
354
361
|
}
|
|
355
362
|
: msg
|
|
356
363
|
)
|
|
@@ -425,7 +432,7 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
425
432
|
setReloadLoadingIds((prev) => new Set(prev).add(id));
|
|
426
433
|
const payload = msg?.search_payload;
|
|
427
434
|
if (!payload || Object.keys(payload).length === 0) return;
|
|
428
|
-
const productsResult = await getProducts(payload);
|
|
435
|
+
const productsResult = await getProducts(payload, priceMode as string);
|
|
429
436
|
setProductsByMsg((prev) => ({ ...prev, [id]: productsResult }));
|
|
430
437
|
} catch (e) {
|
|
431
438
|
console.error("Reload results failed", e);
|
|
@@ -482,7 +489,6 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
482
489
|
await Storage.setJSON("vdbchat_conversations", updatedConversations);
|
|
483
490
|
}
|
|
484
491
|
setMessages([]);
|
|
485
|
-
setAssistantResponse(undefined);
|
|
486
492
|
setProductsByMsg({});
|
|
487
493
|
setReloadLoadingIds(new Set());
|
|
488
494
|
setLoading(false);
|
|
@@ -504,6 +510,7 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
504
510
|
"Search Lab-Grown Diamonds",
|
|
505
511
|
],
|
|
506
512
|
reaction: "0",
|
|
513
|
+
search_payload: {},
|
|
507
514
|
};
|
|
508
515
|
setMessages([initialAssistant]);
|
|
509
516
|
} else {
|
|
@@ -613,7 +620,16 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
613
620
|
<ScrollView
|
|
614
621
|
ref={scrollRef}
|
|
615
622
|
keyboardShouldPersistTaps="handled"
|
|
616
|
-
onScroll={(e) =>
|
|
623
|
+
onScroll={(e) => {
|
|
624
|
+
const { contentOffset, contentSize, layoutMeasurement } =
|
|
625
|
+
e.nativeEvent;
|
|
626
|
+
setScrollY(contentOffset.y);
|
|
627
|
+
const distanceFromBottom =
|
|
628
|
+
contentSize.height -
|
|
629
|
+
(layoutMeasurement.height + contentOffset.y);
|
|
630
|
+
// Enable auto-scroll when user is near the bottom; disable when scrolled up
|
|
631
|
+
setAutoScroll(distanceFromBottom < 48);
|
|
632
|
+
}}
|
|
617
633
|
scrollEventThrottle={16}
|
|
618
634
|
style={
|
|
619
635
|
modalHeight
|
|
@@ -625,9 +641,14 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
625
641
|
...styles.listContent,
|
|
626
642
|
justifyContent: messages.length === 0 ? "center" : "flex-end",
|
|
627
643
|
minHeight: modalHeight ? modalHeight : undefined,
|
|
644
|
+
...(isTablet
|
|
645
|
+
? { maxWidth: 608, alignSelf: "center", width: "100%" }
|
|
646
|
+
: {}),
|
|
628
647
|
}}
|
|
629
648
|
onContentSizeChange={() => {
|
|
630
|
-
|
|
649
|
+
if (autoScroll) {
|
|
650
|
+
scrollRef.current?.scrollToEnd({ animated: false });
|
|
651
|
+
}
|
|
631
652
|
}}
|
|
632
653
|
>
|
|
633
654
|
<View style={styles.emptyContainer}>
|
|
@@ -673,6 +694,7 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
673
694
|
Object.keys(item.search_payload).length > 0 &&
|
|
674
695
|
!productsByMsg[item.id] && (
|
|
675
696
|
<LazyProductsFetcher
|
|
697
|
+
priceMode={priceMode as string}
|
|
676
698
|
messageId={item.id}
|
|
677
699
|
payload={item.search_payload}
|
|
678
700
|
onFetched={(id, result) => {
|
|
@@ -719,6 +741,7 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
719
741
|
}
|
|
720
742
|
onViewAll={onViewAll}
|
|
721
743
|
onItemPress={onItemPress}
|
|
744
|
+
item={item}
|
|
722
745
|
/>
|
|
723
746
|
)}
|
|
724
747
|
{/* Suggestions are now rendered inside MessageBubble */}
|
|
@@ -735,7 +758,15 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
735
758
|
});
|
|
736
759
|
})()}
|
|
737
760
|
</ScrollView>
|
|
738
|
-
<View style={styles.
|
|
761
|
+
<View style={styles.borderTop} />
|
|
762
|
+
<View
|
|
763
|
+
style={[
|
|
764
|
+
styles.bottomContainer,
|
|
765
|
+
isTablet
|
|
766
|
+
? { maxWidth: 608, alignSelf: "center", width: "100%" }
|
|
767
|
+
: {},
|
|
768
|
+
]}
|
|
769
|
+
>
|
|
739
770
|
<ChatInput
|
|
740
771
|
value={input}
|
|
741
772
|
onChangeText={setInput}
|
|
@@ -783,12 +814,14 @@ const styles = StyleSheet.create({
|
|
|
783
814
|
bottomContainer: {
|
|
784
815
|
paddingVertical: 12,
|
|
785
816
|
paddingHorizontal: 16,
|
|
786
|
-
borderTopColor: "#E0E0E0",
|
|
787
|
-
borderTopWidth: 1,
|
|
788
817
|
flexDirection: "column",
|
|
789
818
|
justifyContent: "space-between",
|
|
790
819
|
alignItems: "center",
|
|
791
820
|
alignSelf: "stretch",
|
|
792
821
|
gap: 12,
|
|
793
822
|
},
|
|
823
|
+
borderTop: {
|
|
824
|
+
borderTopColor: "#E0E0E0",
|
|
825
|
+
borderTopWidth: 1,
|
|
826
|
+
},
|
|
794
827
|
});
|
|
@@ -7,12 +7,14 @@ interface LazyProductsFetcherProps {
|
|
|
7
7
|
messageId: string;
|
|
8
8
|
payload: Record<string, any> | undefined | null;
|
|
9
9
|
onFetched: (messageId: string, data: any) => void;
|
|
10
|
+
priceMode: string | null;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
const LazyProductsFetcher: React.FC<LazyProductsFetcherProps> = ({
|
|
13
14
|
messageId,
|
|
14
15
|
payload,
|
|
15
16
|
onFetched,
|
|
17
|
+
priceMode,
|
|
16
18
|
}) => {
|
|
17
19
|
const { ref, inView } = useInViewport<HTMLDivElement>();
|
|
18
20
|
const fetchedRef = useRef(false);
|
|
@@ -25,14 +27,14 @@ const LazyProductsFetcher: React.FC<LazyProductsFetcherProps> = ({
|
|
|
25
27
|
fetchedRef.current = true;
|
|
26
28
|
(async () => {
|
|
27
29
|
try {
|
|
28
|
-
const result = await getProducts(payload);
|
|
30
|
+
const result = await getProducts(payload, priceMode as string);
|
|
29
31
|
onFetched(messageId, result);
|
|
30
32
|
} catch (e) {
|
|
31
33
|
// eslint-disable-next-line no-console
|
|
32
34
|
console.error("Lazy fetch products failed", e);
|
|
33
35
|
}
|
|
34
36
|
})();
|
|
35
|
-
}, [inView, messageId, onFetched, payload]);
|
|
37
|
+
}, [inView, messageId, onFetched, payload, priceMode]);
|
|
36
38
|
|
|
37
39
|
// Sentinel element; no visible UI needed
|
|
38
40
|
return <View ref={ref as any} style={{ height: 1 }} />;
|
|
@@ -79,6 +79,7 @@ export const MessageMetaRow: React.FC<Props> = ({
|
|
|
79
79
|
style={(state: any) => [
|
|
80
80
|
styles.borderButton,
|
|
81
81
|
state?.hovered && styles.borderButtonHover,
|
|
82
|
+
message.reaction === FeedbackAction.LIKE && styles.borderButtonLike,
|
|
82
83
|
]}
|
|
83
84
|
onPress={() =>
|
|
84
85
|
handleFeedbackAction(
|
|
@@ -90,20 +91,16 @@ export const MessageMetaRow: React.FC<Props> = ({
|
|
|
90
91
|
disabled={!canFeedback}
|
|
91
92
|
>
|
|
92
93
|
<ImageComponent
|
|
93
|
-
source={{
|
|
94
|
-
uri:
|
|
95
|
-
message.reaction === FeedbackAction.LIKE
|
|
96
|
-
? "https://cdn.vdbapp.com/ai/chat-widget/assets/img/like-filled.svg"
|
|
97
|
-
: "https://cdn.vdbapp.com/ai/chat-widget/assets/img/like.svg",
|
|
98
|
-
}}
|
|
94
|
+
source={{ uri: "https://cdn.vdbapp.com/ai/chat-widget/assets/img/like.svg" }}
|
|
99
95
|
resizeMode="contain"
|
|
100
|
-
style={{ width:
|
|
96
|
+
style={{ width: 20, height: 20, tintColor: message.reaction === FeedbackAction.LIKE ?"#00B140" : "#020001" }}
|
|
101
97
|
/>
|
|
102
98
|
</Pressable>
|
|
103
99
|
<Pressable
|
|
104
100
|
style={(state: any) => [
|
|
105
101
|
styles.borderButton,
|
|
106
102
|
state?.hovered && styles.borderButtonHover,
|
|
103
|
+
message.reaction === FeedbackAction.DISLIKE && styles.borderButtonDislike,
|
|
107
104
|
]}
|
|
108
105
|
onPress={() =>
|
|
109
106
|
handleFeedbackAction(
|
|
@@ -115,14 +112,9 @@ export const MessageMetaRow: React.FC<Props> = ({
|
|
|
115
112
|
disabled={!canFeedback}
|
|
116
113
|
>
|
|
117
114
|
<ImageComponent
|
|
118
|
-
source={{
|
|
119
|
-
uri:
|
|
120
|
-
message.reaction === FeedbackAction.DISLIKE
|
|
121
|
-
? "https://cdn.vdbapp.com/ai/chat-widget/assets/img/dislike-filled.svg"
|
|
122
|
-
: "https://cdn.vdbapp.com/ai/chat-widget/assets/img/dislike.svg",
|
|
123
|
-
}}
|
|
115
|
+
source={{ uri: "https://cdn.vdbapp.com/ai/chat-widget/assets/img/dislike.svg" }}
|
|
124
116
|
resizeMode="contain"
|
|
125
|
-
style={{ width:
|
|
117
|
+
style={{ width: 20, height: 20, tintColor: message.reaction === FeedbackAction.DISLIKE ? "#D0021B" : "#020001" }}
|
|
126
118
|
/>
|
|
127
119
|
</Pressable>
|
|
128
120
|
</View>
|
|
@@ -190,6 +182,14 @@ const styles = StyleSheet.create({
|
|
|
190
182
|
borderButtonHover: {
|
|
191
183
|
backgroundColor: "#EDEDF2",
|
|
192
184
|
},
|
|
185
|
+
borderButtonLike: {
|
|
186
|
+
backgroundColor: "#DBFFE4",
|
|
187
|
+
borderColor: "#00B140",
|
|
188
|
+
},
|
|
189
|
+
borderButtonDislike: {
|
|
190
|
+
backgroundColor: "#FFE2E0",
|
|
191
|
+
borderColor: "#D0021B",
|
|
192
|
+
},
|
|
193
193
|
timeText: {
|
|
194
194
|
fontSize: 12,
|
|
195
195
|
color: "#666",
|
|
@@ -4,10 +4,11 @@ import ProductsListView from "./ProductsListView";
|
|
|
4
4
|
|
|
5
5
|
interface ProductsListProps {
|
|
6
6
|
data: any[];
|
|
7
|
-
onViewAll?: () => void;
|
|
7
|
+
onViewAll?: (item: any) => void;
|
|
8
8
|
onItemPress?: (item: any) => void;
|
|
9
9
|
variant?: "grid" | "list";
|
|
10
|
-
totalResults: number
|
|
10
|
+
totalResults: number;
|
|
11
|
+
item: any;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
const ProductsList: React.FC<ProductsListProps> = ({
|
|
@@ -16,20 +17,30 @@ const ProductsList: React.FC<ProductsListProps> = ({
|
|
|
16
17
|
onItemPress,
|
|
17
18
|
variant = "list",
|
|
18
19
|
totalResults,
|
|
20
|
+
item,
|
|
19
21
|
}) => {
|
|
20
22
|
if (!data || !data.length) return null;
|
|
21
23
|
if (variant === "list") {
|
|
22
24
|
return (
|
|
23
25
|
<ProductsListView
|
|
24
26
|
data={data}
|
|
27
|
+
// @ts-ignore
|
|
25
28
|
onViewAll={onViewAll}
|
|
26
29
|
onItemPress={onItemPress}
|
|
27
30
|
totalResults={totalResults}
|
|
31
|
+
item={item}
|
|
28
32
|
/>
|
|
29
33
|
);
|
|
30
34
|
}
|
|
31
35
|
return (
|
|
32
|
-
<ProductsGrid
|
|
36
|
+
<ProductsGrid
|
|
37
|
+
data={data}
|
|
38
|
+
// @ts-ignore
|
|
39
|
+
onViewAll={onViewAll}
|
|
40
|
+
onItemPress={onItemPress}
|
|
41
|
+
totalResults={totalResults}
|
|
42
|
+
item={item}
|
|
43
|
+
/>
|
|
33
44
|
);
|
|
34
45
|
};
|
|
35
46
|
|