vdb-ai-chat 1.0.6 → 1.0.8

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 (71) hide show
  1. package/dist/chat-widget.js +1 -1
  2. package/lib/commonjs/api.js +51 -12
  3. package/lib/commonjs/api.js.map +1 -1
  4. package/lib/commonjs/components/BetaNotice.js +13 -12
  5. package/lib/commonjs/components/BetaNotice.js.map +1 -1
  6. package/lib/commonjs/components/ChatInput.js +59 -49
  7. package/lib/commonjs/components/ChatInput.js.map +1 -1
  8. package/lib/commonjs/components/ChatWidget.js +74 -61
  9. package/lib/commonjs/components/ChatWidget.js.map +1 -1
  10. package/lib/commonjs/components/MessageBubble.js +67 -52
  11. package/lib/commonjs/components/MessageBubble.js.map +1 -1
  12. package/lib/commonjs/components/MessageMetaRow.js +50 -31
  13. package/lib/commonjs/components/MessageMetaRow.js.map +1 -1
  14. package/lib/commonjs/components/ProductsListView.js +232 -153
  15. package/lib/commonjs/components/ProductsListView.js.map +1 -1
  16. package/lib/commonjs/components/SuggestionsRow.js +27 -24
  17. package/lib/commonjs/components/SuggestionsRow.js.map +1 -1
  18. package/lib/commonjs/components/utils.js +15 -4
  19. package/lib/commonjs/components/utils.js.map +1 -1
  20. package/lib/commonjs/contexts/ThemeProvider.js +80 -0
  21. package/lib/commonjs/contexts/ThemeProvider.js.map +1 -0
  22. package/lib/commonjs/contexts/utils.js +142 -0
  23. package/lib/commonjs/contexts/utils.js.map +1 -0
  24. package/lib/module/api.js +51 -12
  25. package/lib/module/api.js.map +1 -1
  26. package/lib/module/components/BetaNotice.js +14 -13
  27. package/lib/module/components/BetaNotice.js.map +1 -1
  28. package/lib/module/components/ChatInput.js +61 -50
  29. package/lib/module/components/ChatInput.js.map +1 -1
  30. package/lib/module/components/ChatWidget.js +78 -63
  31. package/lib/module/components/ChatWidget.js.map +1 -1
  32. package/lib/module/components/MessageBubble.js +69 -52
  33. package/lib/module/components/MessageBubble.js.map +1 -1
  34. package/lib/module/components/MessageMetaRow.js +52 -32
  35. package/lib/module/components/MessageMetaRow.js.map +1 -1
  36. package/lib/module/components/ProductsListView.js +234 -154
  37. package/lib/module/components/ProductsListView.js.map +1 -1
  38. package/lib/module/components/SuggestionsRow.js +29 -25
  39. package/lib/module/components/SuggestionsRow.js.map +1 -1
  40. package/lib/module/components/utils.js +17 -3
  41. package/lib/module/components/utils.js.map +1 -1
  42. package/lib/module/contexts/ThemeProvider.js +75 -0
  43. package/lib/module/contexts/ThemeProvider.js.map +1 -0
  44. package/lib/module/contexts/utils.js +136 -0
  45. package/lib/module/contexts/utils.js.map +1 -0
  46. package/lib/typescript/api.d.ts.map +1 -1
  47. package/lib/typescript/components/BetaNotice.d.ts.map +1 -1
  48. package/lib/typescript/components/ChatInput.d.ts.map +1 -1
  49. package/lib/typescript/components/ChatWidget.d.ts.map +1 -1
  50. package/lib/typescript/components/MessageBubble.d.ts +1 -1
  51. package/lib/typescript/components/MessageBubble.d.ts.map +1 -1
  52. package/lib/typescript/components/MessageMetaRow.d.ts.map +1 -1
  53. package/lib/typescript/components/ProductsListView.d.ts.map +1 -1
  54. package/lib/typescript/components/SuggestionsRow.d.ts.map +1 -1
  55. package/lib/typescript/components/utils.d.ts.map +1 -1
  56. package/lib/typescript/contexts/ThemeProvider.d.ts +7 -0
  57. package/lib/typescript/contexts/ThemeProvider.d.ts.map +1 -0
  58. package/lib/typescript/contexts/utils.d.ts +136 -0
  59. package/lib/typescript/contexts/utils.d.ts.map +1 -0
  60. package/package.json +6 -2
  61. package/src/api.ts +58 -21
  62. package/src/components/BetaNotice.tsx +15 -13
  63. package/src/components/ChatInput.tsx +63 -62
  64. package/src/components/ChatWidget.tsx +238 -206
  65. package/src/components/MessageBubble.tsx +113 -74
  66. package/src/components/MessageMetaRow.tsx +80 -50
  67. package/src/components/ProductsListView.tsx +243 -184
  68. package/src/components/SuggestionsRow.tsx +40 -25
  69. package/src/components/utils.ts +24 -8
  70. package/src/contexts/ThemeProvider.tsx +87 -0
  71. package/src/contexts/utils.ts +135 -0
@@ -10,6 +10,9 @@ import {
10
10
  Pressable,
11
11
  } from "react-native";
12
12
  import { getUserCurrencySymbol } from "./utils";
13
+ import { useTheme } from "styled-components/native";
14
+ import { DefaultTheme } from "styled-components/native";
15
+ import styled from "styled-components/native";
13
16
 
14
17
  let ImageComponent: typeof Image = Image;
15
18
  if (Platform.OS !== "web") {
@@ -21,6 +24,93 @@ if (Platform.OS !== "web") {
21
24
  }
22
25
  }
23
26
 
27
+ // Token configuration for diamond details display
28
+ type TokenStyle = "primary" | "secondary";
29
+ type TokenType =
30
+ | "field"
31
+ | "separator"
32
+ | "lineBreak"
33
+ | "group"
34
+ | "fieldWithSuffix"
35
+ | "priceField";
36
+
37
+ interface TokenConfig {
38
+ type: TokenType;
39
+ field?: string;
40
+ style?: TokenStyle;
41
+ suffix?: string;
42
+ prefix?: string;
43
+ format?: "percent";
44
+ showCurrencyIf?: string;
45
+ items?: TokenConfig[];
46
+ }
47
+
48
+ const DIAMOND_TOKENS: TokenConfig[] = [
49
+ // Row 1: Basic specs
50
+ { type: "field", field: "shape_long", style: "primary" },
51
+ {
52
+ type: "fieldWithSuffix",
53
+ field: "size",
54
+ style: "secondary",
55
+ suffix: "ct",
56
+ showCurrencyIf: "price_per_carat",
57
+ },
58
+ { type: "field", field: "color", style: "primary" },
59
+ { type: "field", field: "clarity_short", style: "primary" },
60
+ { type: "separator" },
61
+ {
62
+ type: "group",
63
+ items: [
64
+ { type: "field", field: "cut_short", style: "primary" },
65
+ { type: "field", field: "polish_short", style: "primary", prefix: "/" },
66
+ { type: "field", field: "symmetry_short", style: "primary", prefix: "/" },
67
+ ],
68
+ },
69
+ { type: "separator" },
70
+ { type: "field", field: "fluorescence_intensity_short", style: "primary" },
71
+ { type: "separator" },
72
+ { type: "field", field: "lab_short", style: "primary" },
73
+ { type: "separator" },
74
+ {
75
+ type: "field",
76
+ field: "discount_percent",
77
+ style: "secondary",
78
+ format: "percent",
79
+ },
80
+ // Row 2: Measurements & Pricing
81
+ { type: "lineBreak" },
82
+ {
83
+ type: "field",
84
+ field: "depth_percent",
85
+ style: "secondary",
86
+ prefix: "D",
87
+ format: "percent",
88
+ },
89
+ { type: "separator" },
90
+ {
91
+ type: "field",
92
+ field: "table_percent",
93
+ style: "secondary",
94
+ prefix: "T",
95
+ format: "percent",
96
+ },
97
+ { type: "separator" },
98
+ { type: "field", field: "measurement", style: "secondary" },
99
+ { type: "separator" },
100
+ {
101
+ type: "priceField",
102
+ field: "price_per_carat",
103
+ style: "secondary",
104
+ suffix: "PC",
105
+ },
106
+ {
107
+ type: "priceField",
108
+ field: "total_sales_price",
109
+ style: "secondary",
110
+ prefix: " =",
111
+ },
112
+ ];
113
+
24
114
  interface ProductsListViewProps {
25
115
  data: any[];
26
116
  totalResults?: number;
@@ -29,6 +119,80 @@ interface ProductsListViewProps {
29
119
  item: any;
30
120
  }
31
121
 
122
+ const renderToken = (
123
+ token: TokenConfig,
124
+ item: any,
125
+ userCurrency: string | null,
126
+ index: number
127
+ ): React.ReactNode => {
128
+ const TextView = token.style === "secondary" ? TextStyleTwo : TextStyleOne;
129
+ const currency = userCurrency || "$";
130
+ const theme = useTheme();
131
+ switch (token.type) {
132
+ case "separator":
133
+ return (
134
+ <TextView key={index} theme={theme}>
135
+ {"·"}
136
+ </TextView>
137
+ );
138
+
139
+ case "lineBreak":
140
+ return <View key={index} style={styles.lineBreak} />;
141
+
142
+ case "field": {
143
+ let value = item[token.field!] ?? "-";
144
+ if (token.format === "percent" && value !== "-") {
145
+ value = `${value}%`;
146
+ }
147
+ return (
148
+ <React.Fragment key={index}>
149
+ {token.prefix && <TextView theme={theme}>{token.prefix}</TextView>}
150
+ <TextView theme={theme}>{value}</TextView>
151
+ </React.Fragment>
152
+ );
153
+ }
154
+
155
+ case "fieldWithSuffix": {
156
+ const value = item[token.field!] ?? "-";
157
+ const showSuffix = token.showCurrencyIf
158
+ ? item[token.showCurrencyIf]
159
+ : true;
160
+ return (
161
+ <View key={index} style={styles.rowCenter}>
162
+ <TextView theme={theme}>{value}</TextView>
163
+ {showSuffix && token.suffix && (
164
+ <TextView theme={theme}>{token.suffix}</TextView>
165
+ )}
166
+ </View>
167
+ );
168
+ }
169
+
170
+ case "priceField": {
171
+ const value = item[token.field!];
172
+ return (
173
+ <View key={index} style={styles.rowCenter}>
174
+ {token.prefix && <TextView theme={theme}>{token.prefix}</TextView>}
175
+ {value && <TextView theme={theme}>{currency}</TextView>}
176
+ <TextView theme={theme}>{value ?? "-"}</TextView>
177
+ {token.suffix && <TextView theme={theme}>{token.suffix}</TextView>}
178
+ </View>
179
+ );
180
+ }
181
+
182
+ case "group":
183
+ return (
184
+ <View key={index} style={styles.rowCenter}>
185
+ {token.items?.map((subToken, subIndex) =>
186
+ renderToken(subToken, item, userCurrency, subIndex)
187
+ )}
188
+ </View>
189
+ );
190
+
191
+ default:
192
+ return null;
193
+ }
194
+ };
195
+
32
196
  const ProductsListViewComponent: React.FC<ProductsListViewProps> = ({
33
197
  data,
34
198
  totalResults,
@@ -37,7 +201,7 @@ const ProductsListViewComponent: React.FC<ProductsListViewProps> = ({
37
201
  item,
38
202
  }) => {
39
203
  if (!data || !data.length) return null;
40
-
204
+ const theme = useTheme();
41
205
  const [userCurrency, setUserCurrency] = useState<string | null>(null);
42
206
 
43
207
  useEffect(() => {
@@ -49,168 +213,110 @@ const ProductsListViewComponent: React.FC<ProductsListViewProps> = ({
49
213
  }, []);
50
214
 
51
215
  return (
52
- <View style={styles.wrapper}>
216
+ <Wrapper theme={theme}>
53
217
  <ScrollView
54
218
  showsVerticalScrollIndicator={false}
55
219
  contentContainerStyle={styles.listContent}
56
220
  >
57
- {data.map((item: any, index: number) => (
221
+ {data.map((dataItem: any, index: number) => (
58
222
  <Pressable
59
- key={item.id}
60
- onPress={() => onItemPress?.(item)}
223
+ key={dataItem.id}
224
+ onPress={() => onItemPress?.(dataItem)}
61
225
  // @ts-ignore - hovered is available on web
62
226
  style={({ hovered }) => [
63
227
  styles.itemContainer,
64
228
  hovered && styles.itemHover,
65
229
  ]}
66
230
  >
67
- <View>
68
- <View style={styles.left}>
69
- <View style={styles.serialContainer}>
70
- <Text style={styles.serial}>{index + 1}.</Text>
71
- </View>
72
- <View style={styles.tokens}>
73
- {/* Shape */}
74
- <Text style={styles.textStyleOne}>
75
- {item.shape_long ?? "-"}
76
- </Text>
77
- {/* Carat size */}
78
- <View style={{ flexDirection: "row", alignItems: "center" }}>
79
- <Text style={styles.textStyleTwo}>{item.size ?? "-"}</Text>
80
- {item.price_per_carat ? (
81
- <Text style={styles.textStyleOne}>ct</Text>
82
- ) : (
83
- <></>
84
- )}
85
- </View>
86
- {/* Color */}
87
- <Text style={styles.textStyleOne}>{item.color ?? "-"}</Text>
88
- {/* Clarity */}
89
- <Text style={styles.textStyleOne}>
90
- {item.clarity_short ?? "-"}
91
- </Text>
92
- <Text style={styles.textStyleOne}>{"·"}</Text>
93
- <View style={{ flexDirection: "row", alignItems: "center" }}>
94
- {/* Cut */}
95
- <Text style={styles.textStyleOne}>
96
- {item.cut_short ?? "-"}
97
- </Text>
98
- <Text style={styles.textStyleOne}>{"/"}</Text>
99
- {/* Polish */}
100
- <Text style={styles.textStyleOne}>
101
- {item.polish_short ?? "-"}
102
- </Text>
103
- <Text style={styles.textStyleOne}>{"/"}</Text>
104
- {/* Symmetry */}
105
- <Text style={styles.textStyleOne}>
106
- {item.symmetry_short ?? "-"}
107
- </Text>
108
- </View>
109
- <Text style={styles.textStyleOne}>{"·"}</Text>
110
- {/* Fluorescence */}
111
- <Text style={styles.textStyleOne}>
112
- {item.fluorescence_intensity_short ?? "-"}
113
- </Text>
114
- <Text style={styles.textStyleOne}>{"·"}</Text>
115
- {/* LAB */}
116
- <Text style={styles.textStyleOne}>
117
- {item.lab_short ?? "-"}
118
- </Text>
119
- <Text style={styles.textStyleOne}>{"·"}</Text>
120
- {/* Discount */}
121
- <Text style={styles.textStyleTwo}>
122
- {item.discount_percent ? item.discount_percent + "%" : "-"}
123
- </Text>
124
- <View style={styles.lineBreak} />
125
- {/* Depth */}
126
- <Text style={styles.textStyleOne}>{"D"}</Text>
127
- <Text style={styles.textStyleTwo}>
128
- {item.depth_percent ? item.depth_percent + "%" : "-"}
129
- </Text>
130
- <Text style={styles.textStyleOne}>{"·"}</Text>
131
- {/* Table */}
132
- <Text style={styles.textStyleOne}>{"T"}</Text>
133
- <Text style={styles.textStyleTwo}>
134
- {item.table_percent ? item.table_percent + "%" : "-"}
135
- </Text>
136
- <Text style={styles.textStyleOne}>{"·"}</Text>
137
- {/* Meas */}
138
- <Text style={styles.textStyleTwo}>
139
- {item.measurement ?? "-"}
140
- </Text>
141
- <Text style={styles.textStyleOne}>{"·"}</Text>
142
- {/* Price Per Carat */}
143
- <View style={{ flexDirection: "row", alignItems: "center" }}>
144
- {item.price_per_carat ? (
145
- <Text style={styles.textStyleTwo}>
146
- {userCurrency ? userCurrency : "$"}
147
- </Text>
148
- ) : (
149
- <></>
150
- )}
151
- <Text style={styles.textStyleTwo}>
152
- {item.price_per_carat ?? "-"}
153
- </Text>
154
- <Text style={styles.textStyleOne}>{"PC"}</Text>
155
- </View>
156
- {/* Total Sales Price */}
157
- <View style={{ flexDirection: "row", alignItems: "center" }}>
158
- <Text style={styles.textStyleOne}>{" ="}</Text>
159
- {item.total_sales_price ? (
160
- <Text style={styles.textStyleTwo}>
161
- {userCurrency ? userCurrency : "$"}
162
- </Text>
163
- ) : (
164
- <></>
165
- )}
166
- <Text style={styles.textStyleTwo}>
167
- {item.total_sales_price ?? "-"}
168
- </Text>
169
- </View>
170
- </View>
231
+ <View style={styles.left}>
232
+ <View style={styles.serialContainer}>
233
+ <Serial>{index + 1}.</Serial>
234
+ </View>
235
+ <View style={styles.tokens}>
236
+ {DIAMOND_TOKENS.map((token, tokenIndex) =>
237
+ renderToken(token, dataItem, userCurrency, tokenIndex)
238
+ )}
171
239
  </View>
172
240
  </View>
173
241
  </Pressable>
174
242
  ))}
175
243
  </ScrollView>
176
244
 
177
- <TouchableOpacity
178
- style={styles.button}
179
- activeOpacity={0.8}
180
- onPress={() => onViewAll(item)}
181
- >
182
- <Text
183
- style={styles.buttonText}
184
- >{`View All ${totalResults} Results`}</Text>
185
- <Image
245
+ <ButtonView activeOpacity={0.8} onPress={() => onViewAll(item)}>
246
+ <ButtonText>{`View All ${totalResults} Results`}</ButtonText>
247
+ <ImageComponent
186
248
  source={{
187
249
  uri: "https://cdn.vdbapp.com/ai/chat-widget/assets/img/right.svg",
188
250
  }}
189
251
  resizeMode="contain"
190
- style={{ width: 20, height: 20, tintColor: "#fff" }}
252
+ style={{
253
+ width: 20,
254
+ height: 20,
255
+ tintColor: theme["primary-cont"] || "#ffffff",
256
+ }}
191
257
  />
192
- </TouchableOpacity>
193
- </View>
258
+ </ButtonView>
259
+ </Wrapper>
194
260
  );
195
261
  };
196
262
 
197
- const styles = StyleSheet.create({
198
- wrapper: {
199
- paddingHorizontal: 8,
200
- },
263
+ const Wrapper = styled.View<{ theme: DefaultTheme }>`
264
+ padding-horizontal: 8px;
265
+ `;
266
+
267
+ const Serial = styled.Text<{ theme: DefaultTheme }>`
268
+ font-size: 13px;
269
+ color: ${({ theme }: { theme: DefaultTheme }) =>
270
+ theme["core-05"] || "#020001"};
271
+ font-weight: 500;
272
+ `;
201
273
 
274
+ const TextStyleOne = styled.Text<{ theme: DefaultTheme }>`
275
+ font-size: 13px;
276
+ color: ${({ theme }: { theme: DefaultTheme }) =>
277
+ theme["core-06"] || "#4F4E57"};
278
+ font-weight: 400;
279
+ font-family: "Roboto";
280
+ font-style: normal;
281
+ `;
282
+
283
+ const TextStyleTwo = styled.Text<{ theme: DefaultTheme }>`
284
+ font-size: 13px;
285
+ color: ${({ theme }: { theme: DefaultTheme }) =>
286
+ theme["core-05"] || "#020001"};
287
+ font-weight: 500;
288
+ font-family: "Roboto";
289
+ font-style: normal;
290
+ `;
291
+
292
+ const ButtonView = styled.TouchableOpacity<{ theme: DefaultTheme }>`
293
+ margin-top: 12px;
294
+ margin-horizontal: 12px;
295
+ align-self: center;
296
+ padding-horizontal: 16px;
297
+ padding-vertical: 6px;
298
+ background-color: ${({ theme }: { theme: DefaultTheme }) =>
299
+ theme["primary-bg-static"] || "#292735"};
300
+ border-radius: 8px;
301
+ align-items: center;
302
+ gap: 16px;
303
+ width: 100%;
304
+ flex-direction: row;
305
+ justify-content: center;
306
+ align-content: center;
307
+ `;
308
+
309
+ const ButtonText = styled.Text<{ theme: DefaultTheme }>`
310
+ color: ${({ theme }: { theme: DefaultTheme }) =>
311
+ theme["primary-cont"] || "#ffffff"};
312
+ font-size: 14px;
313
+ font-weight: 600;
314
+ `;
315
+
316
+ const styles = StyleSheet.create({
202
317
  listContent: {
203
318
  gap: 8,
204
319
  },
205
-
206
- row: {
207
- flexDirection: "row",
208
- justifyContent: "space-between",
209
- alignItems: "center",
210
- paddingVertical: 6,
211
- backgroundColor: "#fff",
212
- },
213
-
214
320
  left: {
215
321
  flexDirection: "row",
216
322
  alignItems: "flex-start",
@@ -234,34 +340,9 @@ const styles = StyleSheet.create({
234
340
  width: "100%",
235
341
  height: 0,
236
342
  },
237
-
238
- serial: {
239
- fontSize: 13,
240
- color: "#020001",
241
- fontWeight: "500",
242
- },
243
-
244
- textStyleOne: {
245
- fontFamily: "Roboto",
246
- fontSize: 13,
247
- fontStyle: "normal",
248
- fontWeight: "400",
249
- color: "#4F4E57",
250
- },
251
-
252
- textStyleTwo: {
253
- fontFamily: "Roboto",
254
- fontSize: 13,
255
- fontStyle: "normal",
256
- fontWeight: "500",
257
- color: "#020001",
258
- },
259
-
260
- price: {
261
- fontSize: 13,
262
- fontWeight: "500",
263
- color: "#020001",
264
- textAlign: "left",
343
+ rowCenter: {
344
+ flexDirection: "row",
345
+ alignItems: "center",
265
346
  },
266
347
  priceContainer: {
267
348
  alignItems: "flex-start",
@@ -275,28 +356,6 @@ const styles = StyleSheet.create({
275
356
  itemHover: {
276
357
  backgroundColor: "#EDEDF2",
277
358
  },
278
-
279
- button: {
280
- marginTop: 12,
281
- marginHorizontal: 12,
282
- alignSelf: "center",
283
- paddingHorizontal: 16,
284
- paddingVertical: 6,
285
- backgroundColor: "#292735",
286
- borderRadius: 8,
287
- alignItems: "center",
288
- gap: 16,
289
- width: "100%",
290
- flexDirection: "row",
291
- justifyContent: "center",
292
- alignContent: "center",
293
- },
294
-
295
- buttonText: {
296
- color: "#fff",
297
- fontSize: 14,
298
- fontWeight: "600",
299
- },
300
359
  });
301
360
 
302
361
  const ProductsListView = memo(ProductsListViewComponent);
@@ -6,6 +6,9 @@ import {
6
6
  StyleSheet,
7
7
  ScrollView,
8
8
  } from "react-native";
9
+ import styled from "styled-components/native";
10
+ import { useTheme } from "styled-components/native";
11
+ import { DefaultTheme } from "styled-components/native";
9
12
 
10
13
  interface SuggestionsRowProps {
11
14
  suggestions: string[];
@@ -21,6 +24,7 @@ const SuggestionsRowComponent: React.FC<SuggestionsRowProps> = ({
21
24
  }) => {
22
25
  if (!suggestions.length) return null;
23
26
  const ScrollViewComponent = variant === "inline" ? View : ScrollView;
27
+ const theme = useTheme();
24
28
  return (
25
29
  <ScrollViewComponent
26
30
  showsVerticalScrollIndicator={false}
@@ -32,14 +36,15 @@ const SuggestionsRowComponent: React.FC<SuggestionsRowProps> = ({
32
36
  >
33
37
  {suggestions.map((s, index) => (
34
38
  <>
35
- <TouchableOpacity
39
+ <ChipButton
36
40
  key={`${s}-${index}`}
37
- style={[styles.chip, variant === "inline" && styles.chipInline]}
41
+ theme={theme}
42
+ variant={variant}
38
43
  onPress={() => onSelect(s)}
39
44
  activeOpacity={0.75}
40
45
  >
41
- <Text style={styles.chipText}>{s}</Text>
42
- </TouchableOpacity>
46
+ <ChipText>{s}</ChipText>
47
+ </ChipButton>
43
48
  {index < suggestions.length - 1 && <View style={styles.spacer} />}
44
49
  </>
45
50
  ))}
@@ -47,6 +52,37 @@ const SuggestionsRowComponent: React.FC<SuggestionsRowProps> = ({
47
52
  );
48
53
  };
49
54
 
55
+ const ChipButton = styled.TouchableOpacity<{
56
+ theme: DefaultTheme;
57
+ variant: "inline" | "default";
58
+ }>`
59
+ background-color: ${({
60
+ theme,
61
+ variant,
62
+ }: {
63
+ theme: DefaultTheme;
64
+ variant: "inline" | "default";
65
+ }) =>
66
+ variant === "inline"
67
+ ? theme["core-01"] || "#FFFFFF"
68
+ : theme["core-02"] || "#EDEDF2"};
69
+ border-radius: 6px;
70
+ padding-horizontal: 8px;
71
+ padding-vertical: 6px;
72
+ width: 100%;
73
+ align-self: stretch;
74
+ justify-content: center;
75
+ align-items: flex-start;
76
+ `;
77
+
78
+ const ChipText = styled.Text<{ theme: DefaultTheme }>`
79
+ color: ${({ theme }: { theme: DefaultTheme }) =>
80
+ theme["core-05"] || "#1A1A1A"};
81
+ font-size: 14px;
82
+ font-weight: 500;
83
+ text-align: left;
84
+ `;
85
+
50
86
  const styles = StyleSheet.create({
51
87
  scroll: {
52
88
  maxWidth: "80%",
@@ -71,27 +107,6 @@ const styles = StyleSheet.create({
71
107
  // inside bubble, rely on bubble background
72
108
  gap: 6,
73
109
  },
74
-
75
- chip: {
76
- backgroundColor: "#EDEDF2",
77
- borderRadius: 6,
78
- paddingHorizontal: 8,
79
- paddingVertical: 6,
80
- width: "100%",
81
- alignSelf: "stretch",
82
- justifyContent: "center",
83
- alignItems: "flex-start",
84
- },
85
- chipInline: {
86
- backgroundColor: "#FFFFFF",
87
- },
88
-
89
- chipText: {
90
- fontSize: 14,
91
- color: "#1A1A1A",
92
- fontWeight: "500",
93
- textAlign: "left",
94
- },
95
110
  spacer: {
96
111
  width: 4,
97
112
  height: 4,
@@ -25,14 +25,33 @@ export enum FeedbackAction {
25
25
  UNSET = "0",
26
26
  }
27
27
 
28
+ // Structure for storing conversations with session token
29
+ interface StoredConversations {
30
+ token: string;
31
+ conversations: Record<string, { conversation_id: string | number }>;
32
+ }
33
+
34
+ async function getUserToken(): Promise<string> {
35
+ const userInfo = await Storage.getJSON<UserDetails>("persist:userInfo", {});
36
+ const userData = JSON.parse((userInfo as any)?.user || "{}");
37
+ return userData?.token || "";
38
+ }
39
+
28
40
  export const fetchConversationId = async (
29
41
  _priceMode: string
30
42
  ): Promise<string | null> => {
31
- const conversations = await Storage.getJSON<Record<string, any>>(
43
+ const currentToken = await getUserToken();
44
+ const stored = await Storage.getJSON<StoredConversations>(
32
45
  "vdbchat_conversations",
33
- {}
46
+ null
34
47
  );
35
- if (!conversations) return null;
48
+
49
+ // If no stored data or token doesn't match, return null (session changed)
50
+ if (!stored || stored.token !== currentToken) {
51
+ return null;
52
+ }
53
+
54
+ const conversations = stored.conversations || {};
36
55
 
37
56
  let priceMode = _priceMode;
38
57
  if (priceMode === undefined) {
@@ -40,14 +59,11 @@ export const fetchConversationId = async (
40
59
  const userData = userInfo?.user as UserDetails;
41
60
  priceMode = String(userData?.price_mode) || "";
42
61
  }
43
- return conversations[priceMode]?.conversation_id || null;
62
+ return String(conversations[priceMode]?.conversation_id) || null;
44
63
  };
45
64
 
46
65
  export const getUserCurrencySymbol = async (): Promise<string | null> => {
47
- const userInfo = await Storage.getJSON<UserDetails>(
48
- "persist:userInfo",
49
- {}
50
- );
66
+ const userInfo = await Storage.getJSON<UserDetails>("persist:userInfo", {});
51
67
  const userData = userInfo?.user as UserDetails;
52
68
  return userData.currency_symbol || "$";
53
69
  };