react-native-chatbot-ai 0.1.27 → 0.1.29
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/footer/SuggestionsBar.js +208 -0
- package/lib/module/components/chat/footer/SuggestionsBar.js.map +1 -0
- package/lib/module/components/chat/footer/index.js +89 -153
- package/lib/module/components/chat/footer/index.js.map +1 -1
- package/lib/module/components/chat/item/ChatAIAnswerMessageItem.js +38 -19
- package/lib/module/components/chat/item/ChatAIAnswerMessageItem.js.map +1 -1
- package/lib/module/components/chat/item/ShimmerBlock.js +52 -0
- package/lib/module/components/chat/item/ShimmerBlock.js.map +1 -0
- package/lib/module/components/product/CardHorizontal.js +30 -9
- package/lib/module/components/product/CardHorizontal.js.map +1 -1
- package/lib/module/hooks/message/useSendMessage.js +0 -2
- package/lib/module/hooks/message/useSendMessage.js.map +1 -1
- package/lib/module/hooks/message/useStreamMessage.js +8 -21
- package/lib/module/hooks/message/useStreamMessage.js.map +1 -1
- package/lib/module/hooks/product/useSearchProduct.js +8 -5
- package/lib/module/hooks/product/useSearchProduct.js.map +1 -1
- package/lib/module/store/products.js +13 -4
- package/lib/module/store/products.js.map +1 -1
- package/lib/module/store/streamMessage.js +18 -0
- package/lib/module/store/streamMessage.js.map +1 -1
- package/lib/module/types/chat.js.map +1 -1
- package/lib/typescript/src/components/chat/footer/SuggestionsBar.d.ts +10 -0
- package/lib/typescript/src/components/chat/footer/SuggestionsBar.d.ts.map +1 -0
- package/lib/typescript/src/components/chat/footer/index.d.ts.map +1 -1
- package/lib/typescript/src/components/chat/item/ChatAIAnswerMessageItem.d.ts.map +1 -1
- package/lib/typescript/src/components/chat/item/ShimmerBlock.d.ts +9 -0
- package/lib/typescript/src/components/chat/item/ShimmerBlock.d.ts.map +1 -0
- package/lib/typescript/src/components/product/CardHorizontal.d.ts +2 -2
- package/lib/typescript/src/components/product/CardHorizontal.d.ts.map +1 -1
- package/lib/typescript/src/hooks/message/useSendMessage.d.ts +0 -1
- package/lib/typescript/src/hooks/message/useSendMessage.d.ts.map +1 -1
- package/lib/typescript/src/hooks/message/useStreamMessage.d.ts +0 -1
- package/lib/typescript/src/hooks/message/useStreamMessage.d.ts.map +1 -1
- package/lib/typescript/src/hooks/product/useSearchProduct.d.ts.map +1 -1
- package/lib/typescript/src/store/products.d.ts.map +1 -1
- package/lib/typescript/src/store/streamMessage.d.ts.map +1 -1
- package/lib/typescript/src/types/chat.d.ts +5 -1
- package/lib/typescript/src/types/chat.d.ts.map +1 -1
- package/package.json +3 -1
- package/src/components/chat/footer/SuggestionsBar.tsx +247 -0
- package/src/components/chat/footer/index.tsx +90 -168
- package/src/components/chat/item/ChatAIAnswerMessageItem.tsx +56 -20
- package/src/components/chat/item/ShimmerBlock.tsx +60 -0
- package/src/components/product/CardHorizontal.tsx +333 -305
- package/src/hooks/message/useSendMessage.ts +1 -2
- package/src/hooks/message/useStreamMessage.ts +9 -24
- package/src/hooks/product/useSearchProduct.ts +10 -3
- package/src/store/products.ts +8 -2
- package/src/store/streamMessage.ts +13 -0
- package/src/types/chat.ts +5 -1
|
@@ -8,7 +8,8 @@ import {
|
|
|
8
8
|
KRadiusValue,
|
|
9
9
|
KSpacingValue,
|
|
10
10
|
} from '@droppii/libs';
|
|
11
|
-
import
|
|
11
|
+
import SuggestionsBar from './SuggestionsBar';
|
|
12
|
+
import { useCallback, useState, useRef, useMemo, useEffect } from 'react';
|
|
12
13
|
import type { ComponentType } from 'react';
|
|
13
14
|
import {
|
|
14
15
|
FlatList,
|
|
@@ -42,7 +43,6 @@ import UploadFileItem from './item/UploadFileItem';
|
|
|
42
43
|
import {
|
|
43
44
|
IAttachment,
|
|
44
45
|
IMessageItem,
|
|
45
|
-
ISuggestionItem,
|
|
46
46
|
MessageType,
|
|
47
47
|
SendActionLogType,
|
|
48
48
|
} from '../../../types';
|
|
@@ -70,28 +70,22 @@ export type UploadItem = ImageUpload | FileUpload;
|
|
|
70
70
|
const MAX_FILE_UPLOAD = 3;
|
|
71
71
|
const MAX_FILE_SIZE = 30 * 1024 * 1024;
|
|
72
72
|
const MAX_IMAGE_SIZE = 7 * 1024 * 1024;
|
|
73
|
-
const COUNTDOWN_MS = 60000;
|
|
74
73
|
|
|
75
74
|
interface IChatFooterProps {
|
|
76
75
|
lastMessage?: IMessageItem;
|
|
77
76
|
}
|
|
78
77
|
|
|
79
78
|
const ChatFooter = ({ lastMessage }: IChatFooterProps) => {
|
|
80
|
-
const countdownRef = useRef<NodeJS.Timeout | null>(null);
|
|
81
|
-
const isInCountdownRef = useRef(false);
|
|
82
|
-
const firstVisibleItemRef = useRef<ISuggestionItem | null>(null);
|
|
83
|
-
|
|
84
|
-
const viewabilityConfig = useRef({ itemVisiblePercentThreshold: 10 }).current;
|
|
85
|
-
|
|
86
79
|
const menuRef = useRef(null);
|
|
87
80
|
const logGA = useChatContext().logGA;
|
|
88
|
-
const { onSendMessage
|
|
81
|
+
const { onSendMessage } = useSendMessage();
|
|
89
82
|
const [message, setMessage] = useState('');
|
|
90
83
|
const isStreaming = useStreamMessageStore((state) => state.isStreaming);
|
|
84
|
+
const stopStream = useStreamMessageStore((state) => state.stopStream);
|
|
91
85
|
const [fileUpload, setFileUpload] = useState<UploadItem[]>([]);
|
|
92
86
|
|
|
93
|
-
const debouncedMessage = debounce((
|
|
94
|
-
setMessage(
|
|
87
|
+
const debouncedMessage = debounce((m: string) => {
|
|
88
|
+
setMessage(m);
|
|
95
89
|
}, 200);
|
|
96
90
|
|
|
97
91
|
const isDisabledSend = useMemo(() => {
|
|
@@ -335,163 +329,101 @@ const ChatFooter = ({ lastMessage }: IChatFooterProps) => {
|
|
|
335
329
|
});
|
|
336
330
|
}, [fileUpload, logGA]);
|
|
337
331
|
|
|
338
|
-
|
|
339
|
-
(
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
SendActionLogType.promptSuggestion
|
|
344
|
-
);
|
|
345
|
-
logGA(GAEvents.chatDetailInConvPrmSuggTap, {
|
|
346
|
-
conversation_id: useSessionStore.getState().sessionId,
|
|
347
|
-
group_id: lastMessage?.group_suggestion_id,
|
|
348
|
-
suggestion_id: item.suggestion_id,
|
|
349
|
-
suggestion_content: item.content,
|
|
350
|
-
suggestion_cate: item.category,
|
|
351
|
-
order: index,
|
|
352
|
-
});
|
|
353
|
-
},
|
|
354
|
-
200
|
|
355
|
-
);
|
|
356
|
-
|
|
357
|
-
const renderSuggestionItem = useCallback(
|
|
358
|
-
(item: ISuggestionItem, index: number) => {
|
|
359
|
-
return (
|
|
360
|
-
<KContainer.Touchable
|
|
361
|
-
style={styles.suggestionItem}
|
|
362
|
-
onPress={() => onPressItemSuggestion(item, index)}
|
|
363
|
-
>
|
|
364
|
-
<KLabel.Text typo="TextSmNormal">{item?.content || ''}</KLabel.Text>
|
|
365
|
-
</KContainer.Touchable>
|
|
366
|
-
);
|
|
367
|
-
},
|
|
368
|
-
[onPressItemSuggestion]
|
|
369
|
-
);
|
|
370
|
-
|
|
371
|
-
const onViewableItemsChanged = useRef(({ viewableItems }: any) => {
|
|
372
|
-
if (viewableItems && viewableItems.length > 0) {
|
|
373
|
-
firstVisibleItemRef.current = viewableItems[0].item;
|
|
374
|
-
}
|
|
375
|
-
}).current;
|
|
376
|
-
|
|
377
|
-
const logFirstVisibleItem = useCallback(() => {
|
|
378
|
-
if (!firstVisibleItemRef.current) return;
|
|
379
|
-
|
|
380
|
-
logGA(GAEvents.chatDetailInConvPrmSuggScroll, {
|
|
381
|
-
group_id: lastMessage?.group_suggestion_id,
|
|
382
|
-
message_chat_id: lastMessage?.id,
|
|
383
|
-
suggestion_id: firstVisibleItemRef.current.suggestion_id,
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
// Bắt đầu countdown
|
|
387
|
-
isInCountdownRef.current = true;
|
|
388
|
-
countdownRef.current = setTimeout(() => {
|
|
389
|
-
isInCountdownRef.current = false;
|
|
390
|
-
countdownRef.current = null;
|
|
391
|
-
}, COUNTDOWN_MS);
|
|
392
|
-
}, [lastMessage, logGA]);
|
|
393
|
-
|
|
394
|
-
const handleMomentumEnd = useCallback(() => {
|
|
395
|
-
if (!isInCountdownRef.current) {
|
|
396
|
-
logFirstVisibleItem();
|
|
397
|
-
}
|
|
398
|
-
}, [logFirstVisibleItem]);
|
|
332
|
+
useEffect(() => {
|
|
333
|
+
return () => {
|
|
334
|
+
stopStream();
|
|
335
|
+
};
|
|
336
|
+
}, [stopStream]);
|
|
399
337
|
|
|
400
338
|
return (
|
|
401
339
|
<KContainer.View style={styles.container}>
|
|
402
340
|
<KContainer.VisibleView visible={isShowSuggestions}>
|
|
403
|
-
<
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
}: {
|
|
409
|
-
item: ISuggestionItem;
|
|
410
|
-
index: number;
|
|
411
|
-
}) => renderSuggestionItem(item, index)}
|
|
412
|
-
horizontal
|
|
413
|
-
keyExtractor={(item: ISuggestionItem) => item.suggestion_id}
|
|
414
|
-
contentContainerStyle={styles.suggessionContainer}
|
|
415
|
-
onViewableItemsChanged={onViewableItemsChanged}
|
|
416
|
-
viewabilityConfig={viewabilityConfig}
|
|
417
|
-
scrollEventThrottle={16}
|
|
418
|
-
onMomentumScrollEnd={handleMomentumEnd}
|
|
341
|
+
<SuggestionsBar
|
|
342
|
+
suggestions={lastMessage?.suggestions}
|
|
343
|
+
groupSuggestionId={lastMessage?.group_suggestion_id}
|
|
344
|
+
messageId={lastMessage?.id}
|
|
345
|
+
onSendMessage={onSendMessage}
|
|
419
346
|
/>
|
|
420
347
|
</KContainer.VisibleView>
|
|
421
|
-
<KContainer.
|
|
422
|
-
<
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
item
|
|
430
|
-
|
|
431
|
-
|
|
348
|
+
<KContainer.View style={styles.contentContainer}>
|
|
349
|
+
<KContainer.VisibleView visible={fileUpload.length > 0}>
|
|
350
|
+
<FlatListComponent
|
|
351
|
+
data={fileUpload}
|
|
352
|
+
renderItem={({ item }: { item: UploadItem }) =>
|
|
353
|
+
renderUploadItem(item)
|
|
354
|
+
}
|
|
355
|
+
horizontal
|
|
356
|
+
keyExtractor={(item: UploadItem) =>
|
|
357
|
+
item.uploadType === 'image' ? item.path : item.uri
|
|
358
|
+
}
|
|
359
|
+
contentContainerStyle={styles.listUploadItem}
|
|
360
|
+
/>
|
|
361
|
+
</KContainer.VisibleView>
|
|
362
|
+
<KInput.TextArea
|
|
363
|
+
paddingV={0}
|
|
364
|
+
paddingH={'0.25rem'}
|
|
365
|
+
placeholder={trans('input_placeholder')}
|
|
366
|
+
clearButtonMode="hidden"
|
|
367
|
+
onChangeText={debouncedMessage}
|
|
368
|
+
value={message}
|
|
369
|
+
multiline
|
|
370
|
+
style={styles.input}
|
|
371
|
+
blurOnSubmit={false}
|
|
372
|
+
textAlignVertical="top"
|
|
432
373
|
/>
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
value={message}
|
|
441
|
-
multiline
|
|
442
|
-
style={styles.input}
|
|
443
|
-
blurOnSubmit={false}
|
|
444
|
-
textAlignVertical="top"
|
|
445
|
-
/>
|
|
446
|
-
<KContainer.View style={styles.actions}>
|
|
447
|
-
<Menu
|
|
448
|
-
ref={menuRef}
|
|
449
|
-
renderer={Popover}
|
|
450
|
-
rendererProps={{ placement: 'top', anchorStyle: { display: 'none' } }}
|
|
451
|
-
>
|
|
452
|
-
<MenuTrigger
|
|
453
|
-
customStyles={{
|
|
454
|
-
TriggerTouchableComponent: TouchableWithoutFeedback,
|
|
374
|
+
<KContainer.View style={styles.actions}>
|
|
375
|
+
<Menu
|
|
376
|
+
ref={menuRef}
|
|
377
|
+
renderer={Popover}
|
|
378
|
+
rendererProps={{
|
|
379
|
+
placement: 'top',
|
|
380
|
+
anchorStyle: { display: 'none' },
|
|
455
381
|
}}
|
|
456
382
|
>
|
|
383
|
+
<MenuTrigger
|
|
384
|
+
customStyles={{
|
|
385
|
+
TriggerTouchableComponent: TouchableWithoutFeedback,
|
|
386
|
+
}}
|
|
387
|
+
>
|
|
388
|
+
<KImage.VectorIcons
|
|
389
|
+
name="image-o"
|
|
390
|
+
size={24}
|
|
391
|
+
color={KColors.gray.dark}
|
|
392
|
+
/>
|
|
393
|
+
</MenuTrigger>
|
|
394
|
+
<MenuOptions optionsContainerStyle={styles.popover}>
|
|
395
|
+
{menuItems.map((i) => (
|
|
396
|
+
<MenuOption
|
|
397
|
+
key={i.icon}
|
|
398
|
+
onSelect={i.onPress}
|
|
399
|
+
style={styles.menuItem}
|
|
400
|
+
>
|
|
401
|
+
<KLabel.Text typo="TextMdMedium">{i.label}</KLabel.Text>
|
|
402
|
+
<KImage.VectorIcons name={i.icon} />
|
|
403
|
+
</MenuOption>
|
|
404
|
+
))}
|
|
405
|
+
</MenuOptions>
|
|
406
|
+
</Menu>
|
|
407
|
+
<KContainer.Touchable onPress={handlePressDocumentPicker}>
|
|
457
408
|
<KImage.VectorIcons
|
|
458
|
-
name="
|
|
409
|
+
name="paperclip-o"
|
|
459
410
|
size={24}
|
|
460
411
|
color={KColors.gray.dark}
|
|
461
412
|
/>
|
|
462
|
-
</
|
|
463
|
-
<
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
</MenuOptions>
|
|
475
|
-
</Menu>
|
|
476
|
-
<KContainer.Touchable onPress={handlePressDocumentPicker}>
|
|
477
|
-
<KImage.VectorIcons
|
|
478
|
-
name="paperclip-o"
|
|
479
|
-
size={24}
|
|
480
|
-
color={KColors.gray.dark}
|
|
413
|
+
</KContainer.Touchable>
|
|
414
|
+
<KContainer.View flex />
|
|
415
|
+
<KButton.Solid
|
|
416
|
+
kind="primary"
|
|
417
|
+
icon={{
|
|
418
|
+
vectorName: isStreaming ? 'square-b' : 'send-b',
|
|
419
|
+
size: 20,
|
|
420
|
+
tintColor: KColors.white,
|
|
421
|
+
}}
|
|
422
|
+
onPress={onPressSend}
|
|
423
|
+
br="round"
|
|
424
|
+
disabled={isDisabledSend}
|
|
481
425
|
/>
|
|
482
|
-
</KContainer.
|
|
483
|
-
<KContainer.View flex />
|
|
484
|
-
<KButton.Solid
|
|
485
|
-
kind="primary"
|
|
486
|
-
icon={{
|
|
487
|
-
vectorName: isStreaming ? 'square-b' : 'send-b',
|
|
488
|
-
size: 20,
|
|
489
|
-
tintColor: KColors.white,
|
|
490
|
-
}}
|
|
491
|
-
onPress={onPressSend}
|
|
492
|
-
br="round"
|
|
493
|
-
disabled={isDisabledSend}
|
|
494
|
-
/>
|
|
426
|
+
</KContainer.View>
|
|
495
427
|
</KContainer.View>
|
|
496
428
|
</KContainer.View>
|
|
497
429
|
);
|
|
@@ -501,7 +433,6 @@ export default ChatFooter;
|
|
|
501
433
|
|
|
502
434
|
const styles = StyleSheet.create({
|
|
503
435
|
container: {
|
|
504
|
-
paddingHorizontal: KSpacingValue['0.75rem'],
|
|
505
436
|
paddingVertical: KSpacingValue['0.5rem'],
|
|
506
437
|
borderWidth: 1,
|
|
507
438
|
borderColor: KColors.hexToRgba(KColors.black, 0.15),
|
|
@@ -510,6 +441,9 @@ const styles = StyleSheet.create({
|
|
|
510
441
|
borderTopRightRadius: KSpacingValue['1.25rem'],
|
|
511
442
|
backgroundColor: KColors.white,
|
|
512
443
|
},
|
|
444
|
+
contentContainer: {
|
|
445
|
+
paddingHorizontal: KSpacingValue['0.75rem'],
|
|
446
|
+
},
|
|
513
447
|
actions: {
|
|
514
448
|
flexDirection: 'row',
|
|
515
449
|
alignItems: 'center',
|
|
@@ -542,16 +476,4 @@ const styles = StyleSheet.create({
|
|
|
542
476
|
paddingTop: 6,
|
|
543
477
|
paddingBottom: 8,
|
|
544
478
|
},
|
|
545
|
-
suggessionContainer: {
|
|
546
|
-
gap: KSpacingValue['0.25rem'],
|
|
547
|
-
paddingBottom: 8,
|
|
548
|
-
},
|
|
549
|
-
suggestionItem: {
|
|
550
|
-
backgroundColor: KColors.palette.gray.w25,
|
|
551
|
-
borderRadius: KRadiusValue['4x'],
|
|
552
|
-
height: 32,
|
|
553
|
-
justifyContent: 'center',
|
|
554
|
-
alignItems: 'center',
|
|
555
|
-
paddingHorizontal: KSpacingValue['0.75rem'],
|
|
556
|
-
},
|
|
557
479
|
});
|
|
@@ -24,6 +24,7 @@ import { useChatContext } from '../../../context/ChatContext';
|
|
|
24
24
|
import { trans } from '../../../translation';
|
|
25
25
|
import MessageActionsBar from './MessageActionsBar';
|
|
26
26
|
import useProductsStore from '../../../store/products';
|
|
27
|
+
import ShimmerBlock from './ShimmerBlock';
|
|
27
28
|
|
|
28
29
|
interface ChatAIAnswerMessageItemProps {
|
|
29
30
|
item: IMessageItem;
|
|
@@ -220,7 +221,11 @@ class CustomRenderer extends Renderer implements RendererInterface {
|
|
|
220
221
|
|
|
221
222
|
text(text: string | ReactNode[]): ReactNode {
|
|
222
223
|
return (
|
|
223
|
-
<KLabel.Text
|
|
224
|
+
<KLabel.Text
|
|
225
|
+
key={this.getKey()}
|
|
226
|
+
typo="TextMdNormal"
|
|
227
|
+
color={KColors.black}
|
|
228
|
+
>
|
|
224
229
|
{text}
|
|
225
230
|
</KLabel.Text>
|
|
226
231
|
);
|
|
@@ -228,7 +233,11 @@ class CustomRenderer extends Renderer implements RendererInterface {
|
|
|
228
233
|
|
|
229
234
|
codespan(text: string): ReactNode {
|
|
230
235
|
return (
|
|
231
|
-
<KLabel.Text
|
|
236
|
+
<KLabel.Text
|
|
237
|
+
key={this.getKey()}
|
|
238
|
+
typo="TextMdNormal"
|
|
239
|
+
color={KColors.black}
|
|
240
|
+
>
|
|
232
241
|
{text}
|
|
233
242
|
</KLabel.Text>
|
|
234
243
|
);
|
|
@@ -236,7 +245,12 @@ class CustomRenderer extends Renderer implements RendererInterface {
|
|
|
236
245
|
|
|
237
246
|
strong(children: string | ReactNode[]): ReactNode {
|
|
238
247
|
return (
|
|
239
|
-
<KLabel.Text
|
|
248
|
+
<KLabel.Text
|
|
249
|
+
key={this.getKey()}
|
|
250
|
+
typo="TextMdBold"
|
|
251
|
+
style={styles.mdStrong}
|
|
252
|
+
color={KColors.black}
|
|
253
|
+
>
|
|
240
254
|
{children}
|
|
241
255
|
</KLabel.Text>
|
|
242
256
|
);
|
|
@@ -246,7 +260,8 @@ class CustomRenderer extends Renderer implements RendererInterface {
|
|
|
246
260
|
<KLabel.Text
|
|
247
261
|
key={this.getKey()}
|
|
248
262
|
typo="TextMdNormal"
|
|
249
|
-
style={
|
|
263
|
+
style={styles.mdEm}
|
|
264
|
+
color={KColors.black}
|
|
250
265
|
>
|
|
251
266
|
{children}
|
|
252
267
|
</KLabel.Text>
|
|
@@ -337,7 +352,7 @@ class CustomRenderer extends Renderer implements RendererInterface {
|
|
|
337
352
|
<KLabel.Text
|
|
338
353
|
typo="TextMdNormal"
|
|
339
354
|
color={KColors.primary.normal}
|
|
340
|
-
style={
|
|
355
|
+
style={styles.mdLink}
|
|
341
356
|
>
|
|
342
357
|
{children}
|
|
343
358
|
</KLabel.Text>
|
|
@@ -366,7 +381,12 @@ class CustomRenderer extends Renderer implements RendererInterface {
|
|
|
366
381
|
|
|
367
382
|
heading(text: string | ReactNode[]): ReactNode {
|
|
368
383
|
return (
|
|
369
|
-
<KLabel.Text
|
|
384
|
+
<KLabel.Text
|
|
385
|
+
key={this.getKey()}
|
|
386
|
+
typo="TextMdBold"
|
|
387
|
+
style={styles.mdHeading}
|
|
388
|
+
color={KColors.black}
|
|
389
|
+
>
|
|
370
390
|
{text}
|
|
371
391
|
</KLabel.Text>
|
|
372
392
|
);
|
|
@@ -443,7 +463,7 @@ const ChatAIAnswerMessageItem = ({
|
|
|
443
463
|
const images: string[] = [];
|
|
444
464
|
|
|
445
465
|
productIds.forEach((productId) => {
|
|
446
|
-
const product = products
|
|
466
|
+
const product = products[productId];
|
|
447
467
|
|
|
448
468
|
if (!product) {
|
|
449
469
|
return;
|
|
@@ -460,19 +480,23 @@ const ChatAIAnswerMessageItem = ({
|
|
|
460
480
|
return (
|
|
461
481
|
<KContainer.View style={styles.container}>
|
|
462
482
|
{blocks.map((chunk, index) => (
|
|
463
|
-
<
|
|
464
|
-
key={index}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
483
|
+
<ShimmerBlock
|
|
484
|
+
key={`chunk-${index}`}
|
|
485
|
+
enable={streamMessage && isStreaming}
|
|
486
|
+
>
|
|
487
|
+
<Markdown
|
|
488
|
+
renderer={renderer}
|
|
489
|
+
value={chunk}
|
|
490
|
+
tokenizer={tokenizer}
|
|
491
|
+
styles={markedStyles}
|
|
492
|
+
flatListProps={{
|
|
493
|
+
style: styles.flatList,
|
|
494
|
+
removeClippedSubviews: false,
|
|
495
|
+
windowSize: 50,
|
|
496
|
+
initialNumToRender: 999,
|
|
497
|
+
}}
|
|
498
|
+
/>
|
|
499
|
+
</ShimmerBlock>
|
|
476
500
|
))}
|
|
477
501
|
|
|
478
502
|
{/* Message Actions Bar */}
|
|
@@ -539,4 +563,16 @@ const styles = StyleSheet.create({
|
|
|
539
563
|
borderColor: '#eee',
|
|
540
564
|
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
541
565
|
},
|
|
566
|
+
mdStrong: {
|
|
567
|
+
fontWeight: 'bold',
|
|
568
|
+
},
|
|
569
|
+
mdEm: {
|
|
570
|
+
fontStyle: 'italic',
|
|
571
|
+
},
|
|
572
|
+
mdHeading: {
|
|
573
|
+
fontWeight: 'bold',
|
|
574
|
+
},
|
|
575
|
+
mdLink: {
|
|
576
|
+
textDecorationLine: 'underline',
|
|
577
|
+
},
|
|
542
578
|
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { KColors } from '@droppii/libs';
|
|
2
|
+
import React, { useEffect, useRef } from 'react';
|
|
3
|
+
import { View, Animated, StyleSheet, useWindowDimensions } from 'react-native';
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
duration?: number;
|
|
8
|
+
enable?: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default function ShimmerBlock({
|
|
12
|
+
children,
|
|
13
|
+
duration = 1000,
|
|
14
|
+
enable = false,
|
|
15
|
+
}: Props) {
|
|
16
|
+
const translateX = useRef(new Animated.Value(0)).current;
|
|
17
|
+
const { width: screenWidth } = useWindowDimensions();
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (!enable) return;
|
|
21
|
+
Animated.timing(translateX, {
|
|
22
|
+
toValue: screenWidth,
|
|
23
|
+
duration,
|
|
24
|
+
useNativeDriver: true,
|
|
25
|
+
}).start();
|
|
26
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
27
|
+
}, [screenWidth, duration, enable]);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<View style={styles.container}>
|
|
31
|
+
{children}
|
|
32
|
+
|
|
33
|
+
<Animated.View
|
|
34
|
+
pointerEvents="none"
|
|
35
|
+
style={[
|
|
36
|
+
styles.shimmer,
|
|
37
|
+
{
|
|
38
|
+
transform: [{ translateX }],
|
|
39
|
+
},
|
|
40
|
+
]}
|
|
41
|
+
/>
|
|
42
|
+
</View>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const styles = StyleSheet.create({
|
|
47
|
+
container: {
|
|
48
|
+
position: 'relative',
|
|
49
|
+
overflow: 'hidden',
|
|
50
|
+
},
|
|
51
|
+
shimmer: {
|
|
52
|
+
position: 'absolute',
|
|
53
|
+
top: 0,
|
|
54
|
+
left: 0,
|
|
55
|
+
width: 24,
|
|
56
|
+
height: '100%',
|
|
57
|
+
backgroundColor: KColors.hexToRgba(KColors.white, 0.4),
|
|
58
|
+
borderRadius: 3,
|
|
59
|
+
},
|
|
60
|
+
});
|