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.
Files changed (67) hide show
  1. package/dist/chat-widget.js +1 -1
  2. package/lib/commonjs/api.js +37 -5
  3. package/lib/commonjs/api.js.map +1 -1
  4. package/lib/commonjs/components/BetaNotice.js +5 -2
  5. package/lib/commonjs/components/BetaNotice.js.map +1 -1
  6. package/lib/commonjs/components/ChatHeader.js +20 -5
  7. package/lib/commonjs/components/ChatHeader.js.map +1 -1
  8. package/lib/commonjs/components/ChatWidget.js +58 -23
  9. package/lib/commonjs/components/ChatWidget.js.map +1 -1
  10. package/lib/commonjs/components/LazyProductsFetcher.js +4 -3
  11. package/lib/commonjs/components/LazyProductsFetcher.js.map +1 -1
  12. package/lib/commonjs/components/MessageMetaRow.js +18 -8
  13. package/lib/commonjs/components/MessageMetaRow.js.map +1 -1
  14. package/lib/commonjs/components/ProductsList.js +12 -5
  15. package/lib/commonjs/components/ProductsList.js.map +1 -1
  16. package/lib/commonjs/components/ProductsListView.js +142 -29
  17. package/lib/commonjs/components/ProductsListView.js.map +1 -1
  18. package/lib/commonjs/components/utils.js +44 -2
  19. package/lib/commonjs/components/utils.js.map +1 -1
  20. package/lib/commonjs/storage.js +13 -0
  21. package/lib/commonjs/storage.js.map +1 -1
  22. package/lib/module/api.js +37 -5
  23. package/lib/module/api.js.map +1 -1
  24. package/lib/module/components/BetaNotice.js +5 -2
  25. package/lib/module/components/BetaNotice.js.map +1 -1
  26. package/lib/module/components/ChatHeader.js +20 -5
  27. package/lib/module/components/ChatHeader.js.map +1 -1
  28. package/lib/module/components/ChatWidget.js +59 -24
  29. package/lib/module/components/ChatWidget.js.map +1 -1
  30. package/lib/module/components/LazyProductsFetcher.js +4 -3
  31. package/lib/module/components/LazyProductsFetcher.js.map +1 -1
  32. package/lib/module/components/MessageMetaRow.js +18 -8
  33. package/lib/module/components/MessageMetaRow.js.map +1 -1
  34. package/lib/module/components/ProductsList.js +12 -5
  35. package/lib/module/components/ProductsList.js.map +1 -1
  36. package/lib/module/components/ProductsListView.js +144 -31
  37. package/lib/module/components/ProductsListView.js.map +1 -1
  38. package/lib/module/components/utils.js +39 -0
  39. package/lib/module/components/utils.js.map +1 -1
  40. package/lib/module/storage.js +13 -0
  41. package/lib/module/storage.js.map +1 -1
  42. package/lib/typescript/api.d.ts +1 -1
  43. package/lib/typescript/api.d.ts.map +1 -1
  44. package/lib/typescript/components/BetaNotice.d.ts.map +1 -1
  45. package/lib/typescript/components/ChatHeader.d.ts.map +1 -1
  46. package/lib/typescript/components/ChatWidget.d.ts.map +1 -1
  47. package/lib/typescript/components/LazyProductsFetcher.d.ts +1 -0
  48. package/lib/typescript/components/LazyProductsFetcher.d.ts.map +1 -1
  49. package/lib/typescript/components/MessageMetaRow.d.ts.map +1 -1
  50. package/lib/typescript/components/ProductsList.d.ts +2 -1
  51. package/lib/typescript/components/ProductsList.d.ts.map +1 -1
  52. package/lib/typescript/components/ProductsListView.d.ts +2 -1
  53. package/lib/typescript/components/ProductsListView.d.ts.map +1 -1
  54. package/lib/typescript/components/utils.d.ts +11 -0
  55. package/lib/typescript/components/utils.d.ts.map +1 -1
  56. package/lib/typescript/storage.d.ts.map +1 -1
  57. package/package.json +7 -6
  58. package/src/api.ts +57 -6
  59. package/src/components/BetaNotice.tsx +3 -0
  60. package/src/components/ChatHeader.tsx +18 -3
  61. package/src/components/ChatWidget.tsx +52 -19
  62. package/src/components/LazyProductsFetcher.tsx +4 -2
  63. package/src/components/MessageMetaRow.tsx +14 -14
  64. package/src/components/ProductsList.tsx +14 -3
  65. package/src/components/ProductsListView.tsx +288 -134
  66. package/src/components/utils.ts +44 -4
  67. 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.4",
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 token = (await Storage.getItem("token")) || "";
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(params: ChatApiParams): Promise<any> {
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: 1,
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}>{isBetaMode ? "AI Search Beta" : "AI Search"}</Text>
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: "500",
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 token = await Storage.getItem("token");
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 = useCallback(() => {
108
- const searchPayload = JSON.stringify(assistantResponse?.search_payload);
109
- const payload = assistantResponse?.search_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
- }, [assistantResponse, apiUrl, priceMode, onViewAllPress]);
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) => setScrollY(e.nativeEvent.contentOffset.y)}
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
- scrollRef.current?.scrollToEnd({ animated: false });
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.bottomContainer}>
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: 16, height: 16 }}
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: 16, height: 16 }}
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 data={data} onViewAll={onViewAll} onItemPress={onItemPress} totalResults={totalResults} />
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