vdb-ai-chat 1.0.21 → 1.0.23
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 +20 -1
- package/lib/commonjs/api.js.map +1 -1
- package/lib/commonjs/components/BetaNotice.js +0 -1
- package/lib/commonjs/components/BetaNotice.js.map +1 -1
- package/lib/commonjs/components/Button.js +95 -0
- package/lib/commonjs/components/Button.js.map +1 -0
- package/lib/commonjs/components/ChatWidget.js +82 -4
- package/lib/commonjs/components/ChatWidget.js.map +1 -1
- package/lib/commonjs/components/LabelContainer.js +50 -0
- package/lib/commonjs/components/LabelContainer.js.map +1 -0
- package/lib/commonjs/components/MessageBubble.js +50 -11
- package/lib/commonjs/components/MessageBubble.js.map +1 -1
- package/lib/commonjs/components/MessageMetaRow.js +22 -6
- package/lib/commonjs/components/MessageMetaRow.js.map +1 -1
- package/lib/commonjs/components/ProductsList.js +12 -2
- package/lib/commonjs/components/ProductsList.js.map +1 -1
- package/lib/commonjs/components/ProductsListView.js +123 -317
- package/lib/commonjs/components/ProductsListView.js.map +1 -1
- package/lib/commonjs/components/utils.js +189 -6
- package/lib/commonjs/components/utils.js.map +1 -1
- package/lib/commonjs/types.js +4 -0
- package/lib/module/api.js +20 -2
- package/lib/module/api.js.map +1 -1
- package/lib/module/components/BetaNotice.js +0 -1
- package/lib/module/components/BetaNotice.js.map +1 -1
- package/lib/module/components/Button.js +88 -0
- package/lib/module/components/Button.js.map +1 -0
- package/lib/module/components/ChatWidget.js +84 -6
- package/lib/module/components/ChatWidget.js.map +1 -1
- package/lib/module/components/LabelContainer.js +43 -0
- package/lib/module/components/LabelContainer.js.map +1 -0
- package/lib/module/components/MessageBubble.js +51 -12
- package/lib/module/components/MessageBubble.js.map +1 -1
- package/lib/module/components/MessageMetaRow.js +22 -6
- package/lib/module/components/MessageMetaRow.js.map +1 -1
- package/lib/module/components/ProductsList.js +12 -2
- package/lib/module/components/ProductsList.js.map +1 -1
- package/lib/module/components/ProductsListView.js +123 -318
- package/lib/module/components/ProductsListView.js.map +1 -1
- package/lib/module/components/utils.js +182 -4
- package/lib/module/components/utils.js.map +1 -1
- package/lib/module/types.js +1 -1
- package/lib/typescript/api.d.ts +1 -0
- package/lib/typescript/api.d.ts.map +1 -1
- package/lib/typescript/components/BetaNotice.d.ts.map +1 -1
- package/lib/typescript/components/Button.d.ts +5 -0
- package/lib/typescript/components/Button.d.ts.map +1 -0
- package/lib/typescript/components/ChatWidget.d.ts.map +1 -1
- package/lib/typescript/components/LabelContainer.d.ts +10 -0
- package/lib/typescript/components/LabelContainer.d.ts.map +1 -0
- package/lib/typescript/components/MessageBubble.d.ts.map +1 -1
- package/lib/typescript/components/MessageMetaRow.d.ts.map +1 -1
- package/lib/typescript/components/ProductsList.d.ts +5 -0
- package/lib/typescript/components/ProductsList.d.ts.map +1 -1
- package/lib/typescript/components/ProductsListView.d.ts +5 -0
- package/lib/typescript/components/ProductsListView.d.ts.map +1 -1
- package/lib/typescript/components/utils.d.ts +37 -4
- package/lib/typescript/components/utils.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +28 -0
- package/lib/typescript/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/api.ts +30 -2
- package/src/components/BetaNotice.tsx +0 -1
- package/src/components/Button.tsx +85 -0
- package/src/components/ChatWidget.tsx +90 -2
- package/src/components/LabelContainer.tsx +50 -0
- package/src/components/MessageBubble.tsx +50 -36
- package/src/components/MessageMetaRow.tsx +26 -9
- package/src/components/ProductsList.tsx +15 -0
- package/src/components/ProductsListView.tsx +238 -307
- package/src/components/utils.ts +242 -5
- package/src/types.ts +31 -0
package/src/api.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FeedbackAction, renderSearchParams, UserDetails } from "./components/utils";
|
|
1
|
+
import { FeedbackAction, ProductCategory, renderSearchParams, UserDetails } from "./components/utils";
|
|
2
2
|
import Routes from "./routes";
|
|
3
3
|
import { Storage } from "./storage";
|
|
4
4
|
import type { ChatMessage } from "./types";
|
|
@@ -94,7 +94,7 @@ export async function normaliseMessages(raw: any, priceMode: string | null): Pro
|
|
|
94
94
|
: typeof m.lab_grown === "boolean"
|
|
95
95
|
? m.lab_grown
|
|
96
96
|
: false;
|
|
97
|
-
const activeProduct = isLabgrown ?
|
|
97
|
+
const activeProduct = isLabgrown ? ProductCategory.LAB_GROWN_DIAMOND : ProductCategory.DIAMOND;
|
|
98
98
|
const searchResultViewType =
|
|
99
99
|
searchResultViewTypeMap?.[activeProduct ?? "default"];
|
|
100
100
|
let mGroupIds = selectedGroups[activeProduct ?? ""];
|
|
@@ -352,6 +352,34 @@ export async function getProducts(
|
|
|
352
352
|
return data;
|
|
353
353
|
}
|
|
354
354
|
|
|
355
|
+
export async function getProductsPaginated(
|
|
356
|
+
apiUrl: string,
|
|
357
|
+
params: ChatApiParams,
|
|
358
|
+
pageNumber: number = 1
|
|
359
|
+
): Promise<any> {
|
|
360
|
+
const res = await fetch(
|
|
361
|
+
Routes.SEARCH_DIAMONDS(apiUrl),
|
|
362
|
+
{
|
|
363
|
+
method: "POST",
|
|
364
|
+
headers: await buildHeaders(),
|
|
365
|
+
body: JSON.stringify({
|
|
366
|
+
vdb: {
|
|
367
|
+
...params,
|
|
368
|
+
page_size: 5,
|
|
369
|
+
page_number: pageNumber,
|
|
370
|
+
},
|
|
371
|
+
}),
|
|
372
|
+
}
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
if (!res.ok) {
|
|
376
|
+
throw new Error(`POST failed with ${res.status}`);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const data = await res.json();
|
|
380
|
+
return data;
|
|
381
|
+
}
|
|
382
|
+
|
|
355
383
|
export const handleFeedbackActionApi = async (
|
|
356
384
|
apiUrl: string,
|
|
357
385
|
action: FeedbackAction,
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { ActivityIndicator } from "react-native";
|
|
2
|
+
import styled, { DefaultTheme, useTheme } from "styled-components/native";
|
|
3
|
+
import React, { useState } from "react";
|
|
4
|
+
import { ButtonCategory, ButtonType, getStyleForCategory } from "./utils";
|
|
5
|
+
import { ButtonProps } from "src/types";
|
|
6
|
+
|
|
7
|
+
const Button: React.FC<ButtonProps> = ({
|
|
8
|
+
category = ButtonCategory.default,
|
|
9
|
+
type = ButtonType.primary,
|
|
10
|
+
disabled = false,
|
|
11
|
+
loading,
|
|
12
|
+
onPress,
|
|
13
|
+
height,
|
|
14
|
+
width,
|
|
15
|
+
borderRadius,
|
|
16
|
+
isDisableInteraction = false,
|
|
17
|
+
maxWidth,
|
|
18
|
+
style,
|
|
19
|
+
children,
|
|
20
|
+
}: ButtonProps) => {
|
|
21
|
+
const theme = useTheme();
|
|
22
|
+
const [hovered, setHovered] = useState<boolean | null>(false);
|
|
23
|
+
const { textColor, background, borderColor, hover } = getStyleForCategory(
|
|
24
|
+
category,
|
|
25
|
+
type,
|
|
26
|
+
disabled
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<CustomButton
|
|
31
|
+
onPress={onPress}
|
|
32
|
+
disabled={disabled || loading || isDisableInteraction}
|
|
33
|
+
textColor={textColor}
|
|
34
|
+
background={background}
|
|
35
|
+
borderColor={borderColor}
|
|
36
|
+
borderRadius={borderRadius}
|
|
37
|
+
onHoverIn={() => setHovered(true)}
|
|
38
|
+
onHoverOut={() => setHovered(false)}
|
|
39
|
+
height={height}
|
|
40
|
+
width={width}
|
|
41
|
+
maxWidth={maxWidth}
|
|
42
|
+
type={type}
|
|
43
|
+
style={({ pressed }: { pressed: boolean }) => [
|
|
44
|
+
{
|
|
45
|
+
...style,
|
|
46
|
+
backgroundColor:
|
|
47
|
+
pressed || hovered
|
|
48
|
+
? (theme[hover as keyof DefaultTheme] as string)
|
|
49
|
+
: (theme[background as keyof DefaultTheme] as string),
|
|
50
|
+
}
|
|
51
|
+
]}
|
|
52
|
+
>
|
|
53
|
+
{/*TODO: Replace with the custom text component*/}
|
|
54
|
+
{loading ? (
|
|
55
|
+
<ActivityIndicator
|
|
56
|
+
color={theme[textColor as keyof DefaultTheme] as string}
|
|
57
|
+
/>
|
|
58
|
+
) : (
|
|
59
|
+
children
|
|
60
|
+
)}
|
|
61
|
+
</CustomButton>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const CustomButton = styled.Pressable<any>`
|
|
66
|
+
justify-content: center;
|
|
67
|
+
align-items: center;
|
|
68
|
+
width: ${({ width }: any) => (width ? width : "100%")};
|
|
69
|
+
max-width: ${({ maxWidth }: any) => (maxWidth ? maxWidth : "100%")};
|
|
70
|
+
padding-horizontal: 16px;
|
|
71
|
+
height: ${({ height }: any) => (height > 0 ? height : 40)}px;
|
|
72
|
+
border-radius: ${({ borderRadius }: any) =>
|
|
73
|
+
borderRadius > 0 ? borderRadius : 8}px;
|
|
74
|
+
border-width: ${({ type }: any) => (type === ButtonType.secondary ? 2 : 0)}px;
|
|
75
|
+
border-color: ${({ borderColor, theme }: any) => theme[borderColor]};
|
|
76
|
+
`;
|
|
77
|
+
const StyledText = styled.Text<any>`
|
|
78
|
+
color: ${({ color }: any) => color || "#FFFFFF"};
|
|
79
|
+
font-family: ${({ isBold }: any) => (isBold ? "Roboto-Medium" : "Roboto-Regular")};
|
|
80
|
+
font-weight: ${({ isBold }: any) => (isBold ? "500" : "400")};
|
|
81
|
+
font-size: 14px;
|
|
82
|
+
font-style: normal;
|
|
83
|
+
`;
|
|
84
|
+
|
|
85
|
+
export default Button;
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
fetchInitialMessages,
|
|
30
30
|
fetchOlderMessages,
|
|
31
31
|
getProducts,
|
|
32
|
+
getProductsPaginated,
|
|
32
33
|
handleFeedbackActionApi,
|
|
33
34
|
normaliseMessages,
|
|
34
35
|
sendUserMessage,
|
|
@@ -40,6 +41,7 @@ import {
|
|
|
40
41
|
FeedbackAction,
|
|
41
42
|
formatToTime,
|
|
42
43
|
getUserDetails,
|
|
44
|
+
ProductCategory,
|
|
43
45
|
UserDetails,
|
|
44
46
|
} from "./utils";
|
|
45
47
|
import { useUserAnalytics } from "../hooks/useAnalytics";
|
|
@@ -66,6 +68,7 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
66
68
|
onItemPress: onItemPressProp,
|
|
67
69
|
isBetaMode: isBetaModeProp,
|
|
68
70
|
activeProductType,
|
|
71
|
+
trackAnalyticsEvent,
|
|
69
72
|
},
|
|
70
73
|
ref
|
|
71
74
|
) => {
|
|
@@ -92,6 +95,9 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
92
95
|
undefined
|
|
93
96
|
);
|
|
94
97
|
const [currentPage, setCurrentPage] = useState(1);
|
|
98
|
+
// Search results pagination state
|
|
99
|
+
const [searchResultsPages, setSearchResultsPages] = useState<Record<string, number>>({});
|
|
100
|
+
const [loadingMoreResults, setLoadingMoreResults] = useState<Set<string>>(new Set());
|
|
95
101
|
const scrollRef = useRef<ScrollView | null>(null);
|
|
96
102
|
const inputRef = useRef<TextInput | null>(null);
|
|
97
103
|
// Scroll management refs - using refs instead of state to avoid re-renders
|
|
@@ -154,7 +160,7 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
154
160
|
const deepLinkUrl = `${apiUrl}webapp/${
|
|
155
161
|
isLabgrown ? "lab-grown-diamonds" : "natural-diamonds"
|
|
156
162
|
}/search?priceMode=${priceMode}&productType=${
|
|
157
|
-
isLabgrown ?
|
|
163
|
+
isLabgrown ? ProductCategory.LAB_GROWN_DIAMOND : ProductCategory.DIAMOND
|
|
158
164
|
}&fromNewFilterScreen=false&filterSplitStep=1§ionName=${
|
|
159
165
|
searchPayload.pair === "pair" ? "Pairs" : "Single Stones"
|
|
160
166
|
}&breadCrumbLabel=Stone%20Search&enterSecondFlow=false&saved_search=${stringifiedSearchPayload}`;
|
|
@@ -169,7 +175,7 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
169
175
|
const onItemPress = useCallback(
|
|
170
176
|
(item: any) => {
|
|
171
177
|
const deepLinkUrl = `${apiUrl}webapp/${
|
|
172
|
-
item.type ===
|
|
178
|
+
item.type === ProductCategory.LAB_GROWN_DIAMOND
|
|
173
179
|
? "lab-grown-diamonds"
|
|
174
180
|
: "natural-diamonds"
|
|
175
181
|
}/item-detail/${item.id}?productType=${
|
|
@@ -489,6 +495,83 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
489
495
|
}
|
|
490
496
|
}, []);
|
|
491
497
|
|
|
498
|
+
// Handle show more results for search pagination
|
|
499
|
+
const handleShowMoreResults = useCallback(async (messageId: string, searchPayload: any) => {
|
|
500
|
+
if (loadingMoreResults.has(messageId)) return;
|
|
501
|
+
|
|
502
|
+
const currentPageForMessage = searchResultsPages[messageId] || 1;
|
|
503
|
+
const nextPage = currentPageForMessage + 1;
|
|
504
|
+
|
|
505
|
+
// Limit to maximum 3 pages
|
|
506
|
+
if (nextPage > 3) return;
|
|
507
|
+
|
|
508
|
+
try {
|
|
509
|
+
setLoadingMoreResults(prev => new Set(prev).add(messageId));
|
|
510
|
+
|
|
511
|
+
const newResults = await getProductsPaginated(apiUrl, searchPayload, nextPage);
|
|
512
|
+
|
|
513
|
+
if (newResults?.response?.body?.diamonds) {
|
|
514
|
+
setProductsByMsg(prev => {
|
|
515
|
+
const currentData = prev[messageId];
|
|
516
|
+
if (!currentData) return prev;
|
|
517
|
+
|
|
518
|
+
const existingDiamonds = currentData.response?.body?.diamonds || [];
|
|
519
|
+
const newDiamonds = newResults.response.body.diamonds;
|
|
520
|
+
|
|
521
|
+
return {
|
|
522
|
+
...prev,
|
|
523
|
+
[messageId]: {
|
|
524
|
+
...currentData,
|
|
525
|
+
response: {
|
|
526
|
+
...currentData.response,
|
|
527
|
+
body: {
|
|
528
|
+
...currentData.response.body,
|
|
529
|
+
diamonds: [...existingDiamonds, ...newDiamonds]
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
setSearchResultsPages(prev => ({
|
|
537
|
+
...prev,
|
|
538
|
+
[messageId]: nextPage
|
|
539
|
+
}));
|
|
540
|
+
}
|
|
541
|
+
} catch (error) {
|
|
542
|
+
console.error('Failed to load more results:', error);
|
|
543
|
+
} finally {
|
|
544
|
+
setLoadingMoreResults(prev => {
|
|
545
|
+
const next = new Set(prev);
|
|
546
|
+
next.delete(messageId);
|
|
547
|
+
return next;
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
}, [apiUrl, loadingMoreResults, searchResultsPages]);
|
|
551
|
+
|
|
552
|
+
// Handle show less results to revert to first 5 results
|
|
553
|
+
const handleShowLessResults = useCallback(async (messageId: string, searchPayload: any) => {
|
|
554
|
+
try {
|
|
555
|
+
// Reset to first page and fetch only initial 5 results
|
|
556
|
+
const initialResults = await getProducts(apiUrl, searchPayload);
|
|
557
|
+
|
|
558
|
+
if (initialResults?.response?.body?.diamonds) {
|
|
559
|
+
setProductsByMsg(prev => ({
|
|
560
|
+
...prev,
|
|
561
|
+
[messageId]: initialResults
|
|
562
|
+
}));
|
|
563
|
+
|
|
564
|
+
// Reset to page 1
|
|
565
|
+
setSearchResultsPages(prev => ({
|
|
566
|
+
...prev,
|
|
567
|
+
[messageId]: 1
|
|
568
|
+
}));
|
|
569
|
+
}
|
|
570
|
+
} catch (error) {
|
|
571
|
+
console.error('Failed to show less results:', error);
|
|
572
|
+
}
|
|
573
|
+
}, [apiUrl]);
|
|
574
|
+
|
|
492
575
|
// Load older messages when user scrolls to top
|
|
493
576
|
const loadOlderMessages = useCallback(async () => {
|
|
494
577
|
if (loadingOlder || !hasMoreMessages || !hasAuth) return;
|
|
@@ -921,6 +1004,11 @@ export const ChatWidget = forwardRef<ChatWidgetRef, ChatWidgetProps>(
|
|
|
921
1004
|
onViewAll={onViewAll}
|
|
922
1005
|
onItemPress={onItemPress}
|
|
923
1006
|
item={item}
|
|
1007
|
+
showMoreResults={() => handleShowMoreResults(item.id, item.search_payload)}
|
|
1008
|
+
showLessResults={() => handleShowLessResults(item.id, item.search_payload)}
|
|
1009
|
+
trackAnalyticsEvent={trackAnalyticsEvent}
|
|
1010
|
+
currentPage={searchResultsPages[item.id] || 1}
|
|
1011
|
+
isLoadingMore={loadingMoreResults.has(item.id)}
|
|
924
1012
|
/>
|
|
925
1013
|
)}
|
|
926
1014
|
{/* Suggestions are now rendered inside MessageBubble */}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import styled from "styled-components/native";
|
|
3
|
+
import { DefaultTheme } from "styled-components/native";
|
|
4
|
+
import { useTheme } from "styled-components/native";
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
backgroundColor: string;
|
|
8
|
+
productName: string;
|
|
9
|
+
productColor: string;
|
|
10
|
+
height?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const LabelContainer: React.FC<Props> = ({
|
|
14
|
+
backgroundColor,
|
|
15
|
+
productName,
|
|
16
|
+
productColor,
|
|
17
|
+
height = 24,
|
|
18
|
+
}: any) => {
|
|
19
|
+
const theme = useTheme();
|
|
20
|
+
return (
|
|
21
|
+
<Container backgroundColor={backgroundColor} height={height}>
|
|
22
|
+
<StyledText
|
|
23
|
+
color={theme[productColor as keyof DefaultTheme]}
|
|
24
|
+
selectable={true}
|
|
25
|
+
>
|
|
26
|
+
{productName ?? ""}
|
|
27
|
+
</StyledText>
|
|
28
|
+
</Container>
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const Container = styled.View<any>`
|
|
33
|
+
background-color: ${({ theme, backgroundColor }: any) => theme[backgroundColor]};
|
|
34
|
+
align-items: center;
|
|
35
|
+
justify-content: center;
|
|
36
|
+
padding-left: 6px;
|
|
37
|
+
padding-right: 6px;
|
|
38
|
+
border-radius: 7px;
|
|
39
|
+
height: ${({ height }: any) => height}px;
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
const StyledText = styled.Text<any>`
|
|
43
|
+
color: ${({ color }: any) => color || "#9138AE"};
|
|
44
|
+
font-family: Roboto;
|
|
45
|
+
font-size: 13px;
|
|
46
|
+
font-style: normal;
|
|
47
|
+
font-weight: 500;
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
export default LabelContainer;
|
|
@@ -6,7 +6,8 @@ import { useTheme } from "styled-components/native";
|
|
|
6
6
|
import styled from "styled-components/native";
|
|
7
7
|
import { DefaultTheme } from "styled-components/native";
|
|
8
8
|
import { css } from "styled-components/native";
|
|
9
|
-
import { generateSavedSearchName } from "./utils";
|
|
9
|
+
import { generateSavedSearchName, getProductAlphaColor, getProductColor, ProductCategory } from "./utils";
|
|
10
|
+
import LabelContainer from "./LabelContainer";
|
|
10
11
|
|
|
11
12
|
interface Props {
|
|
12
13
|
message: ChatMessage;
|
|
@@ -41,23 +42,45 @@ const MessageBubbleComponent: React.FC<Props> = ({
|
|
|
41
42
|
: typeof message?.search_payload?.lab_grown === "boolean"
|
|
42
43
|
? message?.search_payload?.lab_grown
|
|
43
44
|
: false;
|
|
45
|
+
const productType = isLabgrown ? ProductCategory.LAB_GROWN_DIAMOND : ProductCategory.DIAMOND;
|
|
44
46
|
|
|
45
47
|
return (
|
|
46
48
|
<Container theme={theme} userTheme={userTheme} isUser={isUser}>
|
|
47
49
|
<BubbleContainer theme={theme} userTheme={userTheme} isUser={isUser}>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
50
|
+
{message.role === "assistant" && hasResults && typeof totalResults === "number" ?
|
|
51
|
+
<>
|
|
52
|
+
<InlineContainer>
|
|
53
|
+
<MessageText isUser={isUser} isBold={false} theme={theme}>
|
|
54
|
+
{`Found `}
|
|
55
|
+
</MessageText>
|
|
56
|
+
<MessageText isUser={isUser} isBold={true} theme={theme}>
|
|
57
|
+
{`${Number(totalResults).toLocaleString()} `}
|
|
58
|
+
</MessageText>
|
|
59
|
+
<LabelContainer productName={isLabgrown ? "Lab-Grown Diamonds" : "Natural Diamonds"} productColor={getProductColor(productType)} backgroundColor={getProductAlphaColor(productType)} />
|
|
60
|
+
<MessageText isUser={isUser} isBold={false} theme={theme}>
|
|
61
|
+
{` that `}
|
|
62
|
+
</MessageText>
|
|
63
|
+
<MessageText isUser={isUser} isBold={false} theme={theme}>
|
|
64
|
+
{`match `}
|
|
65
|
+
</MessageText>
|
|
66
|
+
<MessageText isUser={isUser} isBold={false} theme={theme}>
|
|
67
|
+
{`your `}
|
|
68
|
+
</MessageText>
|
|
69
|
+
<MessageText isUser={isUser} isBold={false} theme={theme}>
|
|
70
|
+
{`preferences:`}
|
|
71
|
+
</MessageText>
|
|
72
|
+
</InlineContainer>
|
|
73
|
+
|
|
74
|
+
<MessageText isUser={isUser} isBold={true} theme={theme}>
|
|
75
|
+
{`${generateSavedSearchName(message.search_payload, true)?.title}`}
|
|
76
|
+
</MessageText>
|
|
77
|
+
</>
|
|
78
|
+
: null}
|
|
79
|
+
{!(message.role === "assistant" && hasResults) &&
|
|
80
|
+
<MessageText isUser={isUser} theme={theme}>
|
|
81
|
+
{(message.text || "")}
|
|
82
|
+
</MessageText>
|
|
83
|
+
}
|
|
61
84
|
{message.role === "assistant" &&
|
|
62
85
|
Array.isArray(message.suggestions) &&
|
|
63
86
|
message.suggestions.length > 0 &&
|
|
@@ -140,28 +163,12 @@ const BubbleContainer = styled.View<{
|
|
|
140
163
|
}) => (isUser ? userTheme.borderRadius : 0)}px;
|
|
141
164
|
`;
|
|
142
165
|
|
|
143
|
-
const MessageText = styled.Text
|
|
144
|
-
theme:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
userTheme,
|
|
150
|
-
theme,
|
|
151
|
-
isUser,
|
|
152
|
-
}: {
|
|
153
|
-
userTheme: ChatTheme;
|
|
154
|
-
theme: DefaultTheme;
|
|
155
|
-
isUser: Boolean;
|
|
156
|
-
}) =>
|
|
157
|
-
isUser
|
|
158
|
-
? theme["core-01"] || userTheme.userTextColor || "#FFFFFF"
|
|
159
|
-
: theme["core-05"] || userTheme.botTextColor || "#000000"};
|
|
160
|
-
font-family: ${({ userTheme }: { userTheme: ChatTheme }) =>
|
|
161
|
-
userTheme.fontFamily || "Roboto"};
|
|
162
|
-
font-size: ${({ userTheme }: { userTheme: ChatTheme }) =>
|
|
163
|
-
userTheme.fontSize || 16}px;
|
|
164
|
-
line-height: 20px;
|
|
166
|
+
const MessageText = styled.Text`
|
|
167
|
+
color: ${({ theme, isUser }: any) => isUser ? theme["core-01"] || "#FFFFFF" : theme["core-05"] || "#000000"};
|
|
168
|
+
font-family: ${({ isBold }: any) => (isBold ? "Roboto-Medium" : "Roboto")};
|
|
169
|
+
font-size: 14px;
|
|
170
|
+
font-style: normal;
|
|
171
|
+
font-weight: ${({ isBold }: any) => (isBold ? "500" : "400")};
|
|
165
172
|
`;
|
|
166
173
|
|
|
167
174
|
const SuggestionsWrapper = styled.View<{
|
|
@@ -169,3 +176,10 @@ const SuggestionsWrapper = styled.View<{
|
|
|
169
176
|
}>`
|
|
170
177
|
margin-top: 8px;
|
|
171
178
|
`;
|
|
179
|
+
|
|
180
|
+
const InlineContainer = styled.View`
|
|
181
|
+
flex-direction: row;
|
|
182
|
+
align-items: center;
|
|
183
|
+
flex-wrap: wrap;
|
|
184
|
+
margin-bottom: 4px;
|
|
185
|
+
`;
|
|
@@ -55,15 +55,11 @@ export const MessageMetaRow: React.FC<Props> = ({
|
|
|
55
55
|
message.role === "assistant" &&
|
|
56
56
|
!message.isLoading &&
|
|
57
57
|
isValidMessageId &&
|
|
58
|
+
Object.keys(message.search_payload || {}).length > 0 &&
|
|
58
59
|
!!conversationId;
|
|
59
60
|
|
|
60
61
|
return (
|
|
61
|
-
<
|
|
62
|
-
style={[
|
|
63
|
-
styles.rowContainer,
|
|
64
|
-
isUser ? styles.alignRight : styles.alignLeft,
|
|
65
|
-
]}
|
|
66
|
-
>
|
|
62
|
+
<RowContainer isUser={isUser}>
|
|
67
63
|
<View style={styles.timeContainer}>
|
|
68
64
|
<TimeText theme={theme}>{formatToTime(message.createdAt)}</TimeText>
|
|
69
65
|
</View>
|
|
@@ -153,7 +149,7 @@ export const MessageMetaRow: React.FC<Props> = ({
|
|
|
153
149
|
/>
|
|
154
150
|
</Pressable>
|
|
155
151
|
))} */}
|
|
156
|
-
</
|
|
152
|
+
</RowContainer>
|
|
157
153
|
);
|
|
158
154
|
};
|
|
159
155
|
|
|
@@ -198,15 +194,36 @@ const TimeText = styled.Text<{ theme: DefaultTheme }>`
|
|
|
198
194
|
color: ${({ theme }: { theme: DefaultTheme }) => theme["core-06"] || "#666"};
|
|
199
195
|
`;
|
|
200
196
|
|
|
197
|
+
const RowContainer = styled.View<{
|
|
198
|
+
theme: DefaultTheme;
|
|
199
|
+
isUser: Boolean;
|
|
200
|
+
}>`
|
|
201
|
+
flex-direction: row;
|
|
202
|
+
justify-content: space-between;
|
|
203
|
+
align-items: center;
|
|
204
|
+
padding-horizontal: 8px;
|
|
205
|
+
margin-top: 0;
|
|
206
|
+
margin-bottom: 12px;
|
|
207
|
+
${({ isUser }: { isUser: Boolean }) =>
|
|
208
|
+
isUser
|
|
209
|
+
? css`
|
|
210
|
+
align-self: flex-end;
|
|
211
|
+
`
|
|
212
|
+
: css`
|
|
213
|
+
align-self: flex-start;
|
|
214
|
+
width: 100%;
|
|
215
|
+
`};
|
|
216
|
+
`;
|
|
217
|
+
|
|
201
218
|
const styles = StyleSheet.create({
|
|
202
219
|
rowContainer: {
|
|
203
220
|
flexDirection: "row",
|
|
204
221
|
justifyContent: "space-between",
|
|
205
222
|
alignItems: "center",
|
|
206
|
-
|
|
207
|
-
marginHorizontal: 8,
|
|
223
|
+
paddingHorizontal: 8,
|
|
208
224
|
marginTop: 0,
|
|
209
225
|
marginBottom: 12,
|
|
226
|
+
width: "100%",
|
|
210
227
|
},
|
|
211
228
|
alignRight: {
|
|
212
229
|
alignSelf: "flex-end",
|
|
@@ -9,6 +9,11 @@ interface ProductsListProps {
|
|
|
9
9
|
variant?: "grid" | "list";
|
|
10
10
|
totalResults: number;
|
|
11
11
|
item: any;
|
|
12
|
+
showMoreResults?: () => void;
|
|
13
|
+
showLessResults?: () => void;
|
|
14
|
+
trackAnalyticsEvent?: (eventName: string, eventData: Record<string, any>) => void;
|
|
15
|
+
currentPage?: number;
|
|
16
|
+
isLoadingMore?: boolean;
|
|
12
17
|
}
|
|
13
18
|
|
|
14
19
|
const ProductsList: React.FC<ProductsListProps> = ({
|
|
@@ -18,6 +23,11 @@ const ProductsList: React.FC<ProductsListProps> = ({
|
|
|
18
23
|
variant = "list",
|
|
19
24
|
totalResults,
|
|
20
25
|
item,
|
|
26
|
+
showMoreResults,
|
|
27
|
+
showLessResults,
|
|
28
|
+
trackAnalyticsEvent,
|
|
29
|
+
currentPage = 1,
|
|
30
|
+
isLoadingMore = false,
|
|
21
31
|
}) => {
|
|
22
32
|
if (!data || !data.length) return null;
|
|
23
33
|
if (variant === "list") {
|
|
@@ -29,6 +39,11 @@ const ProductsList: React.FC<ProductsListProps> = ({
|
|
|
29
39
|
onItemPress={onItemPress}
|
|
30
40
|
totalResults={totalResults}
|
|
31
41
|
item={item}
|
|
42
|
+
showMoreResults={showMoreResults}
|
|
43
|
+
showLessResults={showLessResults}
|
|
44
|
+
trackAnalyticsEvent={trackAnalyticsEvent}
|
|
45
|
+
currentPage={currentPage}
|
|
46
|
+
isLoadingMore={isLoadingMore}
|
|
32
47
|
/>
|
|
33
48
|
);
|
|
34
49
|
}
|