react-native-chatbot-ai 0.1.47 → 0.1.49
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/lib/module/components/chat/ChatEmpty.js +7 -13
- package/lib/module/components/chat/ChatEmpty.js.map +1 -1
- package/lib/module/components/chat/ChatMessageList.js +27 -12
- package/lib/module/components/chat/ChatMessageList.js.map +1 -1
- package/lib/module/components/chat/SuggestionItem.js +16 -8
- package/lib/module/components/chat/SuggestionItem.js.map +1 -1
- package/lib/module/components/chat/footer/SuggestionsBar.js +1 -1
- package/lib/module/components/chat/footer/SuggestionsBar.js.map +1 -1
- package/lib/module/components/chat/footer/item/UploadFileItem.js +21 -1
- package/lib/module/components/chat/footer/item/UploadFileItem.js.map +1 -1
- package/lib/module/components/chat/item/ChatAIAnswerMessageItem.js +11 -138
- package/lib/module/components/chat/item/ChatAIAnswerMessageItem.js.map +1 -1
- package/lib/module/components/chat/item/ChatAIThinkingMessageItem.js +2 -1
- package/lib/module/components/chat/item/ChatAIThinkingMessageItem.js.map +1 -1
- package/lib/module/components/chat/item/ShimmerBlock.js +7 -1
- package/lib/module/components/chat/item/ShimmerBlock.js.map +1 -1
- package/lib/module/components/chat/item/index.js +3 -2
- package/lib/module/components/chat/item/index.js.map +1 -1
- package/lib/module/hooks/message/useMessage.js +2 -0
- package/lib/module/hooks/message/useMessage.js.map +1 -1
- package/lib/module/hooks/upload/useFileUpload.js +2 -1
- package/lib/module/hooks/upload/useFileUpload.js.map +1 -1
- package/lib/module/store/streamMessage.js +11 -0
- package/lib/module/store/streamMessage.js.map +1 -1
- package/lib/typescript/src/components/chat/ChatEmpty.d.ts +5 -1
- package/lib/typescript/src/components/chat/ChatEmpty.d.ts.map +1 -1
- package/lib/typescript/src/components/chat/ChatMessageList.d.ts.map +1 -1
- package/lib/typescript/src/components/chat/SuggestionItem.d.ts.map +1 -1
- package/lib/typescript/src/components/chat/footer/item/UploadFileItem.d.ts.map +1 -1
- package/lib/typescript/src/components/chat/item/ChatAIAnswerMessageItem.d.ts +0 -2
- package/lib/typescript/src/components/chat/item/ChatAIAnswerMessageItem.d.ts.map +1 -1
- package/lib/typescript/src/components/chat/item/ChatAIThinkingMessageItem.d.ts.map +1 -1
- package/lib/typescript/src/components/chat/item/ShimmerBlock.d.ts.map +1 -1
- package/lib/typescript/src/components/chat/item/index.d.ts +1 -1
- package/lib/typescript/src/components/chat/item/index.d.ts.map +1 -1
- package/lib/typescript/src/hooks/message/useMessage.d.ts.map +1 -1
- package/lib/typescript/src/hooks/upload/useFileUpload.d.ts +1 -1
- package/lib/typescript/src/hooks/upload/useFileUpload.d.ts.map +1 -1
- package/lib/typescript/src/store/streamMessage.d.ts.map +1 -1
- package/lib/typescript/src/types/chat.d.ts +1 -0
- package/lib/typescript/src/types/chat.d.ts.map +1 -1
- package/package.json +3 -1
- package/src/components/chat/ChatEmpty.tsx +9 -13
- package/src/components/chat/ChatMessageList.tsx +24 -8
- package/src/components/chat/SuggestionItem.tsx +15 -8
- package/src/components/chat/footer/SuggestionsBar.tsx +1 -1
- package/src/components/chat/footer/item/UploadFileItem.tsx +21 -1
- package/src/components/chat/item/ChatAIAnswerMessageItem.tsx +15 -168
- package/src/components/chat/item/ChatAIThinkingMessageItem.tsx +1 -0
- package/src/components/chat/item/ShimmerBlock.tsx +4 -1
- package/src/components/chat/item/index.tsx +20 -12
- package/src/hooks/message/useMessage.ts +4 -0
- package/src/hooks/upload/useFileUpload.ts +6 -2
- package/src/store/streamMessage.ts +7 -0
- package/src/types/chat.ts +1 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FileUpload } from '../../hooks/upload/useUploadItem';
|
|
2
2
|
interface UseFileUploadProps {
|
|
3
3
|
file: FileUpload;
|
|
4
|
-
onSuccess?: (data: any) => void;
|
|
4
|
+
onSuccess?: (data: any, duration?: number) => void;
|
|
5
5
|
onError?: (err: any) => void;
|
|
6
6
|
}
|
|
7
7
|
export declare const useFileUpload: ({ file, onSuccess, onError, }: UseFileUploadProps) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFileUpload.d.ts","sourceRoot":"","sources":["../../../../../src/hooks/upload/useFileUpload.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AAc9D,UAAU,kBAAkB;IAC1B,IAAI,EAAE,UAAU,CAAC;IACjB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"useFileUpload.d.ts","sourceRoot":"","sources":["../../../../../src/hooks/upload/useFileUpload.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AAc9D,UAAU,kBAAkB;IAC1B,IAAI,EAAE,UAAU,CAAC;IACjB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAC;CAC9B;AAED,eAAO,MAAM,aAAa,GAAI,+BAI3B,kBAAkB;;;;;;CAmGpB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streamMessage.d.ts","sourceRoot":"","sources":["../../../../src/store/streamMessage.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAKnD,QAAA,MAAM,qBAAqB,
|
|
1
|
+
{"version":3,"file":"streamMessage.d.ts","sourceRoot":"","sources":["../../../../src/store/streamMessage.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAKnD,QAAA,MAAM,qBAAqB,iFAyCxB,CAAC;AAEJ,eAAe,qBAAqB,CAAC"}
|
|
@@ -65,6 +65,7 @@ export interface StreamMessageStore {
|
|
|
65
65
|
eventSource?: EventSource;
|
|
66
66
|
setEventSource: (es?: EventSource) => void;
|
|
67
67
|
stopStream: () => void;
|
|
68
|
+
clearStreamMessageRecordById: (ids: string[]) => void;
|
|
68
69
|
}
|
|
69
70
|
export interface ProductsStore {
|
|
70
71
|
products: Record<string, IProductItem | undefined>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../../../src/types/chat.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../../../src/types/chat.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-chatbot-ai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.49",
|
|
4
4
|
"description": "React Native library for Chatbot AI",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
@@ -96,6 +96,7 @@
|
|
|
96
96
|
"@d11/react-native-fast-image": "*",
|
|
97
97
|
"@droppii/libs": "*",
|
|
98
98
|
"@react-native-documents/picker": "*",
|
|
99
|
+
"@shopify/flash-list": "*",
|
|
99
100
|
"@tanstack/react-query": "*",
|
|
100
101
|
"react": "*",
|
|
101
102
|
"react-native": "*",
|
|
@@ -179,6 +180,7 @@
|
|
|
179
180
|
"axios": "^1.12.2",
|
|
180
181
|
"dayjs": "^1.11.18",
|
|
181
182
|
"lodash": "^4.17.21",
|
|
183
|
+
"react-native-animatable": "^1.4.0",
|
|
182
184
|
"react-native-marked": "^7.0.2",
|
|
183
185
|
"react-native-skeleton-placeholder": "^5.2.4",
|
|
184
186
|
"react-native-sse": "^1.2.1",
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { KColors, KContainer, KLabel, KSpacingValue } from '@droppii/libs';
|
|
2
2
|
import { StyleSheet } from 'react-native';
|
|
3
|
-
import { useFetchSuggestions } from '../../hooks/suggestions/useFetchSuggestions';
|
|
4
3
|
import SuggestionItem from './SuggestionItem';
|
|
5
4
|
import { useSendMessage } from '../../hooks/message/useSendMessage';
|
|
6
5
|
import debounce from 'lodash/debounce';
|
|
@@ -9,15 +8,18 @@ import { trans } from '../../translation';
|
|
|
9
8
|
import { useChatContext } from '../../context/ChatContext';
|
|
10
9
|
import { GAEvents } from '../../constants/events';
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
interface IChatEmptyProps {
|
|
12
|
+
suggestions?: ISuggestionItem[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const ChatEmpty = ({ suggestions }: IChatEmptyProps) => {
|
|
14
16
|
const { onSendMessage } = useSendMessage();
|
|
15
17
|
const logGA = useChatContext().logGA;
|
|
16
18
|
|
|
17
19
|
const onPress = debounce((item: ISuggestionItem, index: number) => {
|
|
18
20
|
logGA(GAEvents.newChatNewChatPrmSuggTap, {
|
|
19
21
|
suggestion_id: item.suggestion_id,
|
|
20
|
-
suggestion_content: item
|
|
22
|
+
suggestion_content: item?.content?.slice?.(0, 100) || '',
|
|
21
23
|
suggestion_cate: item.category,
|
|
22
24
|
order: index,
|
|
23
25
|
});
|
|
@@ -25,10 +27,7 @@ const ChatEmpty = () => {
|
|
|
25
27
|
}, 200);
|
|
26
28
|
|
|
27
29
|
return (
|
|
28
|
-
<KContainer.
|
|
29
|
-
contentContainerStyle={styles.container}
|
|
30
|
-
style={styles.transformStyle}
|
|
31
|
-
>
|
|
30
|
+
<KContainer.View style={styles.container}>
|
|
32
31
|
<KLabel.Text typo="TextXLgMedium" center>
|
|
33
32
|
{trans('chat_empty_title')}
|
|
34
33
|
</KLabel.Text>
|
|
@@ -40,7 +39,7 @@ const ChatEmpty = () => {
|
|
|
40
39
|
{trans('chat_empty_description_2')}
|
|
41
40
|
</KLabel.Text>
|
|
42
41
|
</KContainer.View>
|
|
43
|
-
{
|
|
42
|
+
{suggestions?.map((item: ISuggestionItem, index: number) => (
|
|
44
43
|
<SuggestionItem
|
|
45
44
|
key={item.suggestion_id}
|
|
46
45
|
item={item}
|
|
@@ -57,7 +56,7 @@ const ChatEmpty = () => {
|
|
|
57
56
|
>
|
|
58
57
|
{trans('ai_answer_note')}
|
|
59
58
|
</KLabel.Text>
|
|
60
|
-
</KContainer.
|
|
59
|
+
</KContainer.View>
|
|
61
60
|
);
|
|
62
61
|
};
|
|
63
62
|
|
|
@@ -71,9 +70,6 @@ const styles = StyleSheet.create({
|
|
|
71
70
|
padding: KSpacingValue['0.75rem'],
|
|
72
71
|
gap: KSpacingValue['0.5rem'],
|
|
73
72
|
},
|
|
74
|
-
transformStyle: {
|
|
75
|
-
// transform: [Platform.OS === 'android' ? { scale: -1 } : { scaleY: -1 }],
|
|
76
|
-
},
|
|
77
73
|
disclaimer: {
|
|
78
74
|
fontSize: 11,
|
|
79
75
|
paddingHorizontal: KSpacingValue['0.5rem'],
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StyleSheet, View } from 'react-native';
|
|
2
2
|
import type { ComponentType } from 'react';
|
|
3
3
|
import { useCallback, useRef, useEffect, useState } from 'react';
|
|
4
4
|
import ChatEmpty from './ChatEmpty';
|
|
@@ -8,8 +8,10 @@ import type { IMessageItem } from '../../types';
|
|
|
8
8
|
import { GAEvents } from '../../constants/events';
|
|
9
9
|
import { useChatContext } from '../../context/ChatContext';
|
|
10
10
|
import useSessionStore from '../../store/session';
|
|
11
|
+
import { FlashList } from '@shopify/flash-list';
|
|
12
|
+
import { useFetchSuggestions } from '../../hooks/suggestions/useFetchSuggestions';
|
|
11
13
|
|
|
12
|
-
const
|
|
14
|
+
const FlashListComponent = FlashList as unknown as ComponentType<any>;
|
|
13
15
|
|
|
14
16
|
interface IChatMessageListProps {
|
|
15
17
|
messageList?: IMessageItem[];
|
|
@@ -18,9 +20,12 @@ interface IChatMessageListProps {
|
|
|
18
20
|
const ChatMessageList = ({ messageList = [] }: IChatMessageListProps) => {
|
|
19
21
|
const logGA = useChatContext().logGA;
|
|
20
22
|
const sessionId = useSessionStore((state) => state.sessionId);
|
|
23
|
+
const { data: suggestions } = useFetchSuggestions();
|
|
21
24
|
|
|
22
25
|
const flatListRef = useRef<any>(null);
|
|
23
26
|
const scrollOffsetRef = useRef(0);
|
|
27
|
+
const contentHeightRef = useRef(0);
|
|
28
|
+
const viewportHeightRef = useRef(0);
|
|
24
29
|
const [showScrollToBottom, setShowScrollToBottom] = useState(false);
|
|
25
30
|
|
|
26
31
|
const onScroll = useCallback((event: any) => {
|
|
@@ -36,7 +41,11 @@ const ChatMessageList = ({ messageList = [] }: IChatMessageListProps) => {
|
|
|
36
41
|
|
|
37
42
|
const scrollToBottom = useCallback(async () => {
|
|
38
43
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
39
|
-
|
|
44
|
+
const offset = Math.max(
|
|
45
|
+
0,
|
|
46
|
+
contentHeightRef.current - viewportHeightRef.current
|
|
47
|
+
);
|
|
48
|
+
flatListRef.current?.scrollToOffset({ animated: true, offset });
|
|
40
49
|
}, []);
|
|
41
50
|
|
|
42
51
|
useEffect(() => {
|
|
@@ -53,11 +62,11 @@ const ChatMessageList = ({ messageList = [] }: IChatMessageListProps) => {
|
|
|
53
62
|
if (messageList.length > 0) {
|
|
54
63
|
scrollToBottom();
|
|
55
64
|
}
|
|
56
|
-
}, [messageList, scrollToBottom]);
|
|
65
|
+
}, [messageList.length, scrollToBottom]);
|
|
57
66
|
|
|
58
67
|
return (
|
|
59
68
|
<View style={{ flex: 1 }}>
|
|
60
|
-
<
|
|
69
|
+
<FlashListComponent
|
|
61
70
|
ref={flatListRef}
|
|
62
71
|
data={messageList}
|
|
63
72
|
renderItem={({
|
|
@@ -71,10 +80,18 @@ const ChatMessageList = ({ messageList = [] }: IChatMessageListProps) => {
|
|
|
71
80
|
)}
|
|
72
81
|
keyExtractor={(item: IMessageItem) => item.id}
|
|
73
82
|
contentContainerStyle={styles.container}
|
|
74
|
-
|
|
75
|
-
|
|
83
|
+
ListEmptyComponent={
|
|
84
|
+
<ChatEmpty suggestions={suggestions?.suggestions || []} />
|
|
85
|
+
}
|
|
76
86
|
onScroll={onScroll}
|
|
77
87
|
scrollEventThrottle={16}
|
|
88
|
+
estimatedItemSize={60}
|
|
89
|
+
onLayout={(e: any) => {
|
|
90
|
+
viewportHeightRef.current = e?.nativeEvent?.layout?.height || 0;
|
|
91
|
+
}}
|
|
92
|
+
onContentSizeChange={(_: any, height: number) => {
|
|
93
|
+
contentHeightRef.current = height;
|
|
94
|
+
}}
|
|
78
95
|
/>
|
|
79
96
|
|
|
80
97
|
{showScrollToBottom && (
|
|
@@ -100,7 +117,6 @@ const styles = StyleSheet.create({
|
|
|
100
117
|
flexGrow: 1,
|
|
101
118
|
gap: KSpacingValue['0.5rem'],
|
|
102
119
|
paddingVertical: KSpacingValue['1rem'],
|
|
103
|
-
// justifyContent: 'flex-end',
|
|
104
120
|
},
|
|
105
121
|
scrollButton: {
|
|
106
122
|
position: 'absolute',
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from '@droppii/libs';
|
|
11
11
|
import { SuggestionCategory } from '../../types/common';
|
|
12
12
|
import { StyleSheet } from 'react-native';
|
|
13
|
+
import * as Animatable from 'react-native-animatable';
|
|
13
14
|
|
|
14
15
|
interface SuggestionItemProps {
|
|
15
16
|
item: ISuggestionItem;
|
|
@@ -80,15 +81,21 @@ const SuggestionItem = ({ item, index, onPressItem }: SuggestionItemProps) => {
|
|
|
80
81
|
}, [item.category]);
|
|
81
82
|
|
|
82
83
|
return (
|
|
83
|
-
<
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
<Animatable.View
|
|
85
|
+
animation="fadeInUp"
|
|
86
|
+
duration={500}
|
|
87
|
+
style={{ width: '100%' }}
|
|
86
88
|
>
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
{item
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
<KContainer.Touchable
|
|
90
|
+
style={styles.container}
|
|
91
|
+
onPress={() => onPressItem(item, index)}
|
|
92
|
+
>
|
|
93
|
+
{icon}
|
|
94
|
+
<KLabel.Text flex typo="TextNmNormal">
|
|
95
|
+
{item?.content || ''}
|
|
96
|
+
</KLabel.Text>
|
|
97
|
+
</KContainer.Touchable>
|
|
98
|
+
</Animatable.View>
|
|
92
99
|
);
|
|
93
100
|
};
|
|
94
101
|
|
|
@@ -77,7 +77,7 @@ const SuggestionsBar = ({
|
|
|
77
77
|
conversation_id: useSessionStore.getState().sessionId,
|
|
78
78
|
group_id: groupSuggestionId,
|
|
79
79
|
suggestion_id: item.suggestion_id,
|
|
80
|
-
suggestion_content: item
|
|
80
|
+
suggestion_content: item?.content?.slice?.(0, 100) || '',
|
|
81
81
|
suggestion_cate: item.category,
|
|
82
82
|
order: index,
|
|
83
83
|
});
|
|
@@ -13,6 +13,9 @@ import { formatFileSize, shortenFileName } from '../../../../utils/common';
|
|
|
13
13
|
import { useFileUpload } from '../../../../hooks/upload/useFileUpload';
|
|
14
14
|
import UIUtils from '../../../../utils/ui';
|
|
15
15
|
import { useEffect, useRef } from 'react';
|
|
16
|
+
import { useChatContext } from 'src/context/ChatContext';
|
|
17
|
+
import { GAEvents } from 'src/constants/events';
|
|
18
|
+
import useSessionStore from 'src/store/session';
|
|
16
19
|
|
|
17
20
|
interface Props {
|
|
18
21
|
item: FileUpload;
|
|
@@ -20,9 +23,10 @@ interface Props {
|
|
|
20
23
|
onSuccess?: (item: FileUpload) => void;
|
|
21
24
|
}
|
|
22
25
|
const UploadFileItem = ({ item, onRemove, onSuccess }: Props) => {
|
|
26
|
+
const logGA = useChatContext().logGA;
|
|
23
27
|
const { isUploading, cancel } = useFileUpload({
|
|
24
28
|
file: item,
|
|
25
|
-
onSuccess: (data: any) => {
|
|
29
|
+
onSuccess: (data: any, duration?: number) => {
|
|
26
30
|
const mItem = {
|
|
27
31
|
...item,
|
|
28
32
|
remoteUrl: data?.[0]?.publicUrl,
|
|
@@ -30,6 +34,14 @@ const UploadFileItem = ({ item, onRemove, onSuccess }: Props) => {
|
|
|
30
34
|
};
|
|
31
35
|
if (mItem.remoteUrl) {
|
|
32
36
|
onSuccess?.(mItem);
|
|
37
|
+
logGA(GAEvents.chatDetailMultimodalUpload, {
|
|
38
|
+
conversation_id: useSessionStore.getState().sessionId,
|
|
39
|
+
file_type: 'file',
|
|
40
|
+
duration: duration || 0,
|
|
41
|
+
size_mb: (item?.size || 0) / 1024 / 1024,
|
|
42
|
+
status: 'success',
|
|
43
|
+
format: item.type,
|
|
44
|
+
});
|
|
33
45
|
}
|
|
34
46
|
},
|
|
35
47
|
onError: () => {
|
|
@@ -38,6 +50,14 @@ const UploadFileItem = ({ item, onRemove, onSuccess }: Props) => {
|
|
|
38
50
|
theme: 'danger',
|
|
39
51
|
});
|
|
40
52
|
onRemove?.(item);
|
|
53
|
+
logGA(GAEvents.chatDetailMultimodalUpload, {
|
|
54
|
+
conversation_id: useSessionStore.getState().sessionId,
|
|
55
|
+
file_type: 'file',
|
|
56
|
+
duration: 0,
|
|
57
|
+
size_mb: (item.size || 0) / 1024 / 1024,
|
|
58
|
+
status: 'failed',
|
|
59
|
+
format: item.type,
|
|
60
|
+
});
|
|
41
61
|
},
|
|
42
62
|
});
|
|
43
63
|
|
|
@@ -9,12 +9,13 @@ import {
|
|
|
9
9
|
import { IMessageItem } from '../../../types';
|
|
10
10
|
import { StyleSheet, Linking } from 'react-native';
|
|
11
11
|
import useStreamMessageStore from '../../../store/streamMessage';
|
|
12
|
-
import React, {
|
|
13
|
-
import
|
|
12
|
+
import React, { Fragment, ReactNode, useMemo } from 'react';
|
|
13
|
+
import {
|
|
14
14
|
Renderer,
|
|
15
15
|
RendererInterface,
|
|
16
16
|
MarkedTokenizer,
|
|
17
17
|
MarkedStyles,
|
|
18
|
+
useMarkdown,
|
|
18
19
|
} from 'react-native-marked';
|
|
19
20
|
import { useSearchProduct } from '../../../hooks/product/useSearchProduct';
|
|
20
21
|
import ProductHorizontalCard from '../../product/CardHorizontal';
|
|
@@ -33,141 +34,6 @@ interface ChatAIAnswerMessageItemProps {
|
|
|
33
34
|
|
|
34
35
|
class CustomTokenizer extends MarkedTokenizer {}
|
|
35
36
|
|
|
36
|
-
export function splitMarkdownBlocks(text: string): string[] {
|
|
37
|
-
if (typeof text !== 'string' || !text.length) return [];
|
|
38
|
-
|
|
39
|
-
const lines = (text + '\n').split(/\r?\n/);
|
|
40
|
-
const blocks: string[] = [];
|
|
41
|
-
let buffer: string[] = [];
|
|
42
|
-
let insideCode = false;
|
|
43
|
-
let insideTable = false;
|
|
44
|
-
|
|
45
|
-
for (let i = 0; i < lines.length; i++) {
|
|
46
|
-
const rawLine = lines[i] ?? '';
|
|
47
|
-
const line = rawLine.trimEnd();
|
|
48
|
-
|
|
49
|
-
// --- code block ---
|
|
50
|
-
if (/^```|^~~~/.test(line)) {
|
|
51
|
-
insideCode = !insideCode;
|
|
52
|
-
buffer.push(rawLine);
|
|
53
|
-
if (!insideCode) {
|
|
54
|
-
blocks.push(buffer.join('\n'));
|
|
55
|
-
buffer = [];
|
|
56
|
-
}
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
if (insideCode) {
|
|
60
|
-
buffer.push(rawLine);
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// --- table block ---
|
|
65
|
-
if (/^\|.*\|/.test(line)) {
|
|
66
|
-
// nếu bắt đầu table mới
|
|
67
|
-
if (!insideTable && buffer.length > 0) {
|
|
68
|
-
blocks.push(buffer.join('\n'));
|
|
69
|
-
buffer = [];
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
insideTable = true;
|
|
73
|
-
buffer.push(rawLine);
|
|
74
|
-
|
|
75
|
-
// nếu dòng kế tiếp không phải bảng -> kết thúc bảng
|
|
76
|
-
const nextLine = lines[i + 1];
|
|
77
|
-
if (!nextLine || !/^\|.*\|/.test(nextLine)) {
|
|
78
|
-
insideTable = false;
|
|
79
|
-
blocks.push(buffer.join('\n'));
|
|
80
|
-
buffer = [];
|
|
81
|
-
}
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// --- component marker ---
|
|
86
|
-
// ❗ nếu đang trong bảng, giữ nguyên marker trong buffer, không cắt block
|
|
87
|
-
if (line.includes('<!-- @component:') && !insideTable) {
|
|
88
|
-
const parts = rawLine
|
|
89
|
-
.split(/(<!--\s*@component:[\w-]+\([^)]+\)\s*-->)/g)
|
|
90
|
-
.filter(Boolean);
|
|
91
|
-
|
|
92
|
-
for (const part of parts) {
|
|
93
|
-
if (/^<!--\s*@component:[\w-]+\(/.test(part.trim())) {
|
|
94
|
-
if (buffer.length > 0) {
|
|
95
|
-
blocks.push(buffer.join('\n'));
|
|
96
|
-
buffer = [];
|
|
97
|
-
}
|
|
98
|
-
blocks.push(part.trim());
|
|
99
|
-
} else if (part.trim().length > 0) {
|
|
100
|
-
buffer.push(part);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// --- block starter (heading, list, quote, v.v.) ---
|
|
107
|
-
if (
|
|
108
|
-
/^(\s*#{1,6}\s+|\s*>|\s*[-*+]\s+|\s*\d+\.\s+)/.test(line) &&
|
|
109
|
-
!insideTable
|
|
110
|
-
) {
|
|
111
|
-
if (buffer.length > 0) {
|
|
112
|
-
blocks.push(buffer.join('\n'));
|
|
113
|
-
buffer = [];
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
buffer.push(rawLine);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (buffer.length > 0) {
|
|
121
|
-
blocks.push(buffer.join('\n'));
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return blocks.map((b) => b.trim()).filter(Boolean);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export function useStreamingMarkdownBlocks(text: string) {
|
|
128
|
-
const [blocks, setBlocks] = useState<string[]>([]);
|
|
129
|
-
const prevBlocksRef = useRef<string[]>([]);
|
|
130
|
-
|
|
131
|
-
useEffect(() => {
|
|
132
|
-
if (!text) {
|
|
133
|
-
setBlocks([]);
|
|
134
|
-
prevBlocksRef.current = [];
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const newBlocks = splitMarkdownBlocks(text);
|
|
139
|
-
const prevBlocks = prevBlocksRef.current;
|
|
140
|
-
|
|
141
|
-
// ✅ Merge thông minh, không làm mất chữ
|
|
142
|
-
let merged = [...prevBlocks];
|
|
143
|
-
|
|
144
|
-
for (let i = 0; i < newBlocks.length; i++) {
|
|
145
|
-
const newBlock = newBlocks[i];
|
|
146
|
-
const oldBlock = prevBlocks[i];
|
|
147
|
-
|
|
148
|
-
// nếu block chưa có hoặc thay đổi nội dung → cập nhật
|
|
149
|
-
if (oldBlock === undefined || newBlock !== oldBlock) {
|
|
150
|
-
merged[i] = newBlock || '';
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// nếu có thêm block mới (VD: text dài hơn)
|
|
155
|
-
if (newBlocks.length > merged.length) {
|
|
156
|
-
merged = [...merged, ...newBlocks.slice(merged.length)];
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// nếu markdown bị rút ngắn (xóa bớt)
|
|
160
|
-
if (newBlocks.length < merged.length) {
|
|
161
|
-
merged = merged.slice(0, newBlocks.length);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
prevBlocksRef.current = merged;
|
|
165
|
-
setBlocks(merged);
|
|
166
|
-
}, [text]);
|
|
167
|
-
|
|
168
|
-
return blocks;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
37
|
class CustomRenderer extends Renderer implements RendererInterface {
|
|
172
38
|
constructor(
|
|
173
39
|
openImageViewer?: (images: { url: string }[], index: number) => void,
|
|
@@ -199,11 +65,7 @@ class CustomRenderer extends Renderer implements RendererInterface {
|
|
|
199
65
|
);
|
|
200
66
|
default:
|
|
201
67
|
return (
|
|
202
|
-
<KLabel.Text
|
|
203
|
-
typo="TextMdNormal"
|
|
204
|
-
key={this.getKey()}
|
|
205
|
-
selectable={true}
|
|
206
|
-
>
|
|
68
|
+
<KLabel.Text typo="TextMdNormal" key={this.getKey()}>
|
|
207
69
|
{match?.[2] || ''}
|
|
208
70
|
</KLabel.Text>
|
|
209
71
|
);
|
|
@@ -229,7 +91,6 @@ class CustomRenderer extends Renderer implements RendererInterface {
|
|
|
229
91
|
key={this.getKey()}
|
|
230
92
|
typo="TextMdNormal"
|
|
231
93
|
color={KColors.black}
|
|
232
|
-
selectable={true}
|
|
233
94
|
>
|
|
234
95
|
{text}
|
|
235
96
|
</KLabel.Text>
|
|
@@ -242,7 +103,6 @@ class CustomRenderer extends Renderer implements RendererInterface {
|
|
|
242
103
|
key={this.getKey()}
|
|
243
104
|
typo="TextMdNormal"
|
|
244
105
|
color={KColors.black}
|
|
245
|
-
selectable={true}
|
|
246
106
|
>
|
|
247
107
|
{text}
|
|
248
108
|
</KLabel.Text>
|
|
@@ -256,7 +116,6 @@ class CustomRenderer extends Renderer implements RendererInterface {
|
|
|
256
116
|
typo="TextMdBold"
|
|
257
117
|
style={styles.mdStrong}
|
|
258
118
|
color={KColors.black}
|
|
259
|
-
selectable={true}
|
|
260
119
|
>
|
|
261
120
|
{children}
|
|
262
121
|
</KLabel.Text>
|
|
@@ -269,7 +128,6 @@ class CustomRenderer extends Renderer implements RendererInterface {
|
|
|
269
128
|
typo="TextMdNormal"
|
|
270
129
|
style={styles.mdEm}
|
|
271
130
|
color={KColors.black}
|
|
272
|
-
selectable={true}
|
|
273
131
|
>
|
|
274
132
|
{children}
|
|
275
133
|
</KLabel.Text>
|
|
@@ -294,7 +152,6 @@ class CustomRenderer extends Renderer implements RendererInterface {
|
|
|
294
152
|
typo="TextMdNormal"
|
|
295
153
|
style={textStyle}
|
|
296
154
|
color={KColors.gray.dark}
|
|
297
|
-
selectable={true}
|
|
298
155
|
>
|
|
299
156
|
{text}
|
|
300
157
|
</KLabel.Text>
|
|
@@ -425,10 +282,6 @@ const ChatAIAnswerMessageItem = ({
|
|
|
425
282
|
);
|
|
426
283
|
const isStreaming = useStreamMessageStore((state) => state.isStreaming);
|
|
427
284
|
|
|
428
|
-
const blocks = useStreamingMarkdownBlocks(
|
|
429
|
-
streamMessage?.content || item.content
|
|
430
|
-
);
|
|
431
|
-
|
|
432
285
|
const openImageViewer = useChatContext()?.openImageViewer;
|
|
433
286
|
|
|
434
287
|
const productIds = useMemo(() => {
|
|
@@ -463,6 +316,12 @@ const ChatAIAnswerMessageItem = ({
|
|
|
463
316
|
);
|
|
464
317
|
const tokenizer = useMemo(() => new CustomTokenizer(), []);
|
|
465
318
|
|
|
319
|
+
const blocks = useMarkdown(streamMessage?.content || item.content, {
|
|
320
|
+
renderer: renderer,
|
|
321
|
+
tokenizer: tokenizer,
|
|
322
|
+
styles: markedStyles,
|
|
323
|
+
});
|
|
324
|
+
|
|
466
325
|
// Extract all product images for sharing
|
|
467
326
|
const productImages = useMemo(() => {
|
|
468
327
|
if (productIds.length === 0) {
|
|
@@ -489,23 +348,11 @@ const ChatAIAnswerMessageItem = ({
|
|
|
489
348
|
return (
|
|
490
349
|
<KContainer.View style={styles.container}>
|
|
491
350
|
{blocks.map((chunk, index) => (
|
|
492
|
-
<
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
renderer={renderer}
|
|
498
|
-
value={chunk}
|
|
499
|
-
tokenizer={tokenizer}
|
|
500
|
-
styles={markedStyles}
|
|
501
|
-
flatListProps={{
|
|
502
|
-
style: styles.flatList,
|
|
503
|
-
removeClippedSubviews: false,
|
|
504
|
-
windowSize: 50,
|
|
505
|
-
initialNumToRender: 999,
|
|
506
|
-
}}
|
|
507
|
-
/>
|
|
508
|
-
</ShimmerBlock>
|
|
351
|
+
<Fragment key={`chunk-${index}`}>
|
|
352
|
+
<ShimmerBlock enable={streamMessage && isStreaming}>
|
|
353
|
+
{chunk}
|
|
354
|
+
</ShimmerBlock>
|
|
355
|
+
</Fragment>
|
|
509
356
|
))}
|
|
510
357
|
|
|
511
358
|
{/* Message Actions Bar */}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { KColors } from '@droppii/libs';
|
|
2
2
|
import React, { useEffect, useRef } from 'react';
|
|
3
3
|
import { View, Animated, StyleSheet, useWindowDimensions } from 'react-native';
|
|
4
|
+
import * as Animatable from 'react-native-animatable';
|
|
4
5
|
|
|
5
6
|
type Props = {
|
|
6
7
|
children: React.ReactNode;
|
|
@@ -28,7 +29,9 @@ export default function ShimmerBlock({
|
|
|
28
29
|
|
|
29
30
|
return (
|
|
30
31
|
<View style={styles.container}>
|
|
31
|
-
{
|
|
32
|
+
<Animatable.View animation="fadeIn" duration={duration} easing="ease-in">
|
|
33
|
+
{children}
|
|
34
|
+
</Animatable.View>
|
|
32
35
|
|
|
33
36
|
{enable && (
|
|
34
37
|
<Animated.View
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { memo } from 'react';
|
|
1
2
|
import { IMessageItem, MessageType } from '../../../types';
|
|
2
3
|
import ChatAIAnswerMessageItem from './ChatAIAnswerMessageItem';
|
|
3
4
|
import ChatAIThinkingMessageItem from './ChatAIThinkingMessageItem';
|
|
@@ -7,17 +8,24 @@ interface ChatItemProps {
|
|
|
7
8
|
item: IMessageItem;
|
|
8
9
|
isLastItem: boolean;
|
|
9
10
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
11
|
+
|
|
12
|
+
const ChatItem = memo(
|
|
13
|
+
({ item, isLastItem = false }: ChatItemProps) => {
|
|
14
|
+
switch (item.type) {
|
|
15
|
+
case MessageType.user_message:
|
|
16
|
+
return <ChatUserMessageItem item={item} isLast={isLastItem} />;
|
|
17
|
+
case MessageType.ai_answer:
|
|
18
|
+
return <ChatAIAnswerMessageItem item={item} isLast={isLastItem} />;
|
|
19
|
+
case MessageType.ai_thinking:
|
|
20
|
+
return <ChatAIThinkingMessageItem item={item} isLast={isLastItem} />;
|
|
21
|
+
default:
|
|
22
|
+
return <ChatUserMessageItem item={item} isLast={isLastItem} />;
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
(prev, next) =>
|
|
26
|
+
prev.isLastItem === next.isLastItem &&
|
|
27
|
+
prev.item?.id === next.item?.id &&
|
|
28
|
+
prev.item?.content === next.item?.content
|
|
29
|
+
);
|
|
22
30
|
|
|
23
31
|
export default ChatItem;
|
|
@@ -4,6 +4,7 @@ import { useFetchSessionById } from '../session/useFetchSessionById';
|
|
|
4
4
|
import { IMessageItem, SessionDetailResponse } from '../../types/dto';
|
|
5
5
|
import { DeviceEventEmitter } from 'react-native';
|
|
6
6
|
import { events } from '../../constants/events';
|
|
7
|
+
import useStreamMessageStore from '../../store/streamMessage';
|
|
7
8
|
|
|
8
9
|
const initialState = {
|
|
9
10
|
id: '',
|
|
@@ -107,6 +108,9 @@ export const useMessage = () => {
|
|
|
107
108
|
messages: [...merged, ...newItems],
|
|
108
109
|
};
|
|
109
110
|
});
|
|
111
|
+
useStreamMessageStore
|
|
112
|
+
.getState()
|
|
113
|
+
.clearStreamMessageRecordById(messages.map((m) => m.id));
|
|
110
114
|
}
|
|
111
115
|
);
|
|
112
116
|
const subUpdateMessageError = DeviceEventEmitter.addListener(
|