react-native-chatbot-ai 0.1.19 → 0.1.22
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/Drawer/DeleteSessionPopup.js +113 -0
- package/lib/module/components/Drawer/DeleteSessionPopup.js.map +1 -0
- package/lib/module/components/Drawer/DrawerContent.js +21 -8
- package/lib/module/components/Drawer/DrawerContent.js.map +1 -1
- package/lib/module/components/Drawer/RenameSessionPopup.js +121 -0
- package/lib/module/components/Drawer/RenameSessionPopup.js.map +1 -0
- package/lib/module/components/Drawer/SearchInput.js +10 -3
- package/lib/module/components/Drawer/SearchInput.js.map +1 -1
- package/lib/module/components/Drawer/SessionItem.js +36 -20
- package/lib/module/components/Drawer/SessionItem.js.map +1 -1
- package/lib/module/components/Drawer/SessionList.js +2 -5
- package/lib/module/components/Drawer/SessionList.js.map +1 -1
- package/lib/module/components/Drawer/SessionOptionsBottomSheet.js +129 -0
- package/lib/module/components/Drawer/SessionOptionsBottomSheet.js.map +1 -0
- package/lib/module/components/Drawer/ShareSessionPopup.js +132 -0
- package/lib/module/components/Drawer/ShareSessionPopup.js.map +1 -0
- package/lib/module/components/chat/ChatEmpty.js +15 -4
- package/lib/module/components/chat/ChatEmpty.js.map +1 -1
- package/lib/module/components/chat/ChatHeader.js +10 -4
- package/lib/module/components/chat/ChatHeader.js.map +1 -1
- package/lib/module/components/chat/ChatMessageList.js +75 -24
- package/lib/module/components/chat/ChatMessageList.js.map +1 -1
- package/lib/module/components/chat/SuggestionItem.js +2 -1
- package/lib/module/components/chat/SuggestionItem.js.map +1 -1
- package/lib/module/components/chat/footer/index.js +77 -15
- package/lib/module/components/chat/footer/index.js.map +1 -1
- package/lib/module/components/chat/footer/item/UploadImageItem.js +21 -1
- package/lib/module/components/chat/footer/item/UploadImageItem.js.map +1 -1
- package/lib/module/components/chat/index.js +7 -6
- package/lib/module/components/chat/index.js.map +1 -1
- package/lib/module/components/chat/item/ChatAIAnswerMessageItem.js +36 -5
- package/lib/module/components/chat/item/ChatAIAnswerMessageItem.js.map +1 -1
- package/lib/module/components/chat/item/DeeplinkItem.js +30 -2
- package/lib/module/components/chat/item/DeeplinkItem.js.map +1 -1
- package/lib/module/components/chat/item/MessageActionsBar.js +243 -0
- package/lib/module/components/chat/item/MessageActionsBar.js.map +1 -0
- package/lib/module/components/chat/item/actions/ActionButton.js +56 -0
- package/lib/module/components/chat/item/actions/ActionButton.js.map +1 -0
- package/lib/module/components/portal/BottomSheet.js +245 -0
- package/lib/module/components/portal/BottomSheet.js.map +1 -0
- package/lib/module/components/portal/Popup.js +278 -0
- package/lib/module/components/portal/Popup.js.map +1 -0
- package/lib/module/components/portal/index.js +11 -5
- package/lib/module/components/portal/index.js.map +1 -1
- package/lib/module/components/product/CardHorizontal.js +44 -7
- package/lib/module/components/product/CardHorizontal.js.map +1 -1
- package/lib/module/constants/events.js +33 -0
- package/lib/module/constants/events.js.map +1 -1
- package/lib/module/constants/index.js +5 -1
- package/lib/module/constants/index.js.map +1 -1
- package/lib/module/constants/query.js +5 -1
- package/lib/module/constants/query.js.map +1 -1
- package/lib/module/context/ChatContext.js +9 -3
- package/lib/module/context/ChatContext.js.map +1 -1
- package/lib/module/hooks/message/useSendMessage.js +21 -3
- package/lib/module/hooks/message/useSendMessage.js.map +1 -1
- package/lib/module/hooks/message/useStreamMessage.js +10 -3
- package/lib/module/hooks/message/useStreamMessage.js.map +1 -1
- package/lib/module/hooks/messageActions/index.js +13 -0
- package/lib/module/hooks/messageActions/index.js.map +1 -0
- package/lib/module/hooks/messageActions/useAudioPlayer.js +269 -0
- package/lib/module/hooks/messageActions/useAudioPlayer.js.map +1 -0
- package/lib/module/hooks/messageActions/useCopyToClipboard.js +38 -0
- package/lib/module/hooks/messageActions/useCopyToClipboard.js.map +1 -0
- package/lib/module/hooks/messageActions/useFeedback.js +93 -0
- package/lib/module/hooks/messageActions/useFeedback.js.map +1 -0
- package/lib/module/hooks/messageActions/useSendFeedback.js +24 -0
- package/lib/module/hooks/messageActions/useSendFeedback.js.map +1 -0
- package/lib/module/hooks/messageActions/useShareMessage.js +128 -0
- package/lib/module/hooks/messageActions/useShareMessage.js.map +1 -0
- package/lib/module/hooks/session/useDeleteSession.js +23 -0
- package/lib/module/hooks/session/useDeleteSession.js.map +1 -0
- package/lib/module/hooks/session/useRenameSession.js +28 -0
- package/lib/module/hooks/session/useRenameSession.js.map +1 -0
- package/lib/module/hooks/session/useSearchSessions.js +5 -1
- package/lib/module/hooks/session/useSearchSessions.js.map +1 -1
- package/lib/module/hooks/session/useShareSession.js +16 -0
- package/lib/module/hooks/session/useShareSession.js.map +1 -0
- package/lib/module/hooks/upload/useImageUpload.js +2 -1
- package/lib/module/hooks/upload/useImageUpload.js.map +1 -1
- package/lib/module/services/endpoints.js +6 -1
- package/lib/module/services/endpoints.js.map +1 -1
- package/lib/module/services/index.js +1 -0
- package/lib/module/services/index.js.map +1 -1
- package/lib/module/services/playbackService.js +22 -0
- package/lib/module/services/playbackService.js.map +1 -0
- package/lib/module/store/audioPlayer.js +75 -0
- package/lib/module/store/audioPlayer.js.map +1 -0
- package/lib/module/store/messageActions.js +160 -0
- package/lib/module/store/messageActions.js.map +1 -0
- package/lib/module/store/session.js +6 -3
- package/lib/module/store/session.js.map +1 -1
- package/lib/module/translation/index.js +21 -25
- package/lib/module/translation/index.js.map +1 -1
- package/lib/module/translation/resources/i18n.js +39 -0
- package/lib/module/translation/resources/i18n.js.map +1 -0
- package/lib/module/types/chat.js +12 -1
- package/lib/module/types/chat.js.map +1 -1
- package/lib/module/types/index.js +1 -0
- package/lib/module/types/index.js.map +1 -1
- package/lib/module/types/messageActions.js +56 -0
- package/lib/module/types/messageActions.js.map +1 -0
- package/lib/module/utils/textCleaner.js +67 -0
- package/lib/module/utils/textCleaner.js.map +1 -0
- package/lib/module/utils/ui.js +27 -1
- package/lib/module/utils/ui.js.map +1 -1
- package/lib/typescript/src/components/Drawer/DeleteSessionPopup.d.ts +3 -0
- package/lib/typescript/src/components/Drawer/DeleteSessionPopup.d.ts.map +1 -0
- package/lib/typescript/src/components/Drawer/DrawerContent.d.ts.map +1 -1
- package/lib/typescript/src/components/Drawer/RenameSessionPopup.d.ts +3 -0
- package/lib/typescript/src/components/Drawer/RenameSessionPopup.d.ts.map +1 -0
- package/lib/typescript/src/components/Drawer/SearchInput.d.ts.map +1 -1
- package/lib/typescript/src/components/Drawer/SessionItem.d.ts.map +1 -1
- package/lib/typescript/src/components/Drawer/SessionList.d.ts.map +1 -1
- package/lib/typescript/src/components/Drawer/SessionOptionsBottomSheet.d.ts +3 -0
- package/lib/typescript/src/components/Drawer/SessionOptionsBottomSheet.d.ts.map +1 -0
- package/lib/typescript/src/components/Drawer/ShareSessionPopup.d.ts +3 -0
- package/lib/typescript/src/components/Drawer/ShareSessionPopup.d.ts.map +1 -0
- package/lib/typescript/src/components/chat/ChatEmpty.d.ts.map +1 -1
- package/lib/typescript/src/components/chat/ChatHeader.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 +3 -2
- package/lib/typescript/src/components/chat/SuggestionItem.d.ts.map +1 -1
- package/lib/typescript/src/components/chat/footer/index.d.ts.map +1 -1
- package/lib/typescript/src/components/chat/footer/item/UploadImageItem.d.ts.map +1 -1
- package/lib/typescript/src/components/chat/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/DeeplinkItem.d.ts +2 -1
- package/lib/typescript/src/components/chat/item/DeeplinkItem.d.ts.map +1 -1
- package/lib/typescript/src/components/chat/item/MessageActionsBar.d.ts +16 -0
- package/lib/typescript/src/components/chat/item/MessageActionsBar.d.ts.map +1 -0
- package/lib/typescript/src/components/chat/item/actions/ActionButton.d.ts +19 -0
- package/lib/typescript/src/components/chat/item/actions/ActionButton.d.ts.map +1 -0
- package/lib/typescript/src/components/portal/BottomSheet.d.ts +8 -0
- package/lib/typescript/src/components/portal/BottomSheet.d.ts.map +1 -0
- package/lib/typescript/src/components/portal/Popup.d.ts +4 -0
- package/lib/typescript/src/components/portal/Popup.d.ts.map +1 -0
- package/lib/typescript/src/components/portal/index.d.ts.map +1 -1
- package/lib/typescript/src/components/product/CardHorizontal.d.ts +3 -2
- package/lib/typescript/src/components/product/CardHorizontal.d.ts.map +1 -1
- package/lib/typescript/src/constants/events.d.ts +33 -0
- package/lib/typescript/src/constants/events.d.ts.map +1 -1
- package/lib/typescript/src/constants/index.d.ts +5 -1
- package/lib/typescript/src/constants/index.d.ts.map +1 -1
- package/lib/typescript/src/constants/query.d.ts +4 -0
- package/lib/typescript/src/constants/query.d.ts.map +1 -1
- package/lib/typescript/src/context/ChatContext.d.ts.map +1 -1
- package/lib/typescript/src/hooks/message/useSendMessage.d.ts +2 -2
- package/lib/typescript/src/hooks/message/useSendMessage.d.ts.map +1 -1
- package/lib/typescript/src/hooks/message/useStreamMessage.d.ts.map +1 -1
- package/lib/typescript/src/hooks/messageActions/index.d.ts +10 -0
- package/lib/typescript/src/hooks/messageActions/index.d.ts.map +1 -0
- package/lib/typescript/src/hooks/messageActions/useAudioPlayer.d.ts +14 -0
- package/lib/typescript/src/hooks/messageActions/useAudioPlayer.d.ts.map +1 -0
- package/lib/typescript/src/hooks/messageActions/useCopyToClipboard.d.ts +9 -0
- package/lib/typescript/src/hooks/messageActions/useCopyToClipboard.d.ts.map +1 -0
- package/lib/typescript/src/hooks/messageActions/useFeedback.d.ts +18 -0
- package/lib/typescript/src/hooks/messageActions/useFeedback.d.ts.map +1 -0
- package/lib/typescript/src/hooks/messageActions/useSendFeedback.d.ts +6 -0
- package/lib/typescript/src/hooks/messageActions/useSendFeedback.d.ts.map +1 -0
- package/lib/typescript/src/hooks/messageActions/useShareMessage.d.ts +12 -0
- package/lib/typescript/src/hooks/messageActions/useShareMessage.d.ts.map +1 -0
- package/lib/typescript/src/hooks/session/useDeleteSession.d.ts +2 -0
- package/lib/typescript/src/hooks/session/useDeleteSession.d.ts.map +1 -0
- package/lib/typescript/src/hooks/session/useRenameSession.d.ts +2 -0
- package/lib/typescript/src/hooks/session/useRenameSession.d.ts.map +1 -0
- package/lib/typescript/src/hooks/session/useSearchSessions.d.ts +1 -1
- package/lib/typescript/src/hooks/session/useSearchSessions.d.ts.map +1 -1
- package/lib/typescript/src/hooks/session/useShareSession.d.ts +2 -0
- package/lib/typescript/src/hooks/session/useShareSession.d.ts.map +1 -0
- package/lib/typescript/src/hooks/upload/useImageUpload.d.ts +1 -1
- package/lib/typescript/src/hooks/upload/useImageUpload.d.ts.map +1 -1
- package/lib/typescript/src/services/endpoints.d.ts +5 -0
- package/lib/typescript/src/services/endpoints.d.ts.map +1 -1
- package/lib/typescript/src/services/index.d.ts +1 -0
- package/lib/typescript/src/services/index.d.ts.map +1 -1
- package/lib/typescript/src/services/playbackService.d.ts +2 -0
- package/lib/typescript/src/services/playbackService.d.ts.map +1 -0
- package/lib/typescript/src/store/audioPlayer.d.ts +27 -0
- package/lib/typescript/src/store/audioPlayer.d.ts.map +1 -0
- package/lib/typescript/src/store/messageActions.d.ts +9 -0
- package/lib/typescript/src/store/messageActions.d.ts.map +1 -0
- package/lib/typescript/src/store/session.d.ts.map +1 -1
- package/lib/typescript/src/translation/index.d.ts +3 -4
- package/lib/typescript/src/translation/index.d.ts.map +1 -1
- package/lib/typescript/src/translation/resources/i18n.d.ts +5 -0
- package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -0
- package/lib/typescript/src/types/chat.d.ts +16 -1
- package/lib/typescript/src/types/chat.d.ts.map +1 -1
- package/lib/typescript/src/types/dto.d.ts +11 -0
- package/lib/typescript/src/types/dto.d.ts.map +1 -1
- package/lib/typescript/src/types/index.d.ts +1 -0
- package/lib/typescript/src/types/index.d.ts.map +1 -1
- package/lib/typescript/src/types/messageActions.d.ts +85 -0
- package/lib/typescript/src/types/messageActions.d.ts.map +1 -0
- package/lib/typescript/src/types/ui.d.ts +16 -1
- package/lib/typescript/src/types/ui.d.ts.map +1 -1
- package/lib/typescript/src/utils/textCleaner.d.ts +30 -0
- package/lib/typescript/src/utils/textCleaner.d.ts.map +1 -0
- package/lib/typescript/src/utils/ui.d.ts +12 -1
- package/lib/typescript/src/utils/ui.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/components/Drawer/DeleteSessionPopup.tsx +121 -0
- package/src/components/Drawer/DrawerContent.tsx +23 -7
- package/src/components/Drawer/RenameSessionPopup.tsx +145 -0
- package/src/components/Drawer/SearchInput.tsx +11 -2
- package/src/components/Drawer/SessionItem.tsx +22 -8
- package/src/components/Drawer/SessionList.tsx +0 -2
- package/src/components/Drawer/SessionOptionsBottomSheet.tsx +138 -0
- package/src/components/Drawer/ShareSessionPopup.tsx +145 -0
- package/src/components/chat/ChatEmpty.tsx +15 -5
- package/src/components/chat/ChatHeader.tsx +9 -4
- package/src/components/chat/ChatMessageList.tsx +78 -18
- package/src/components/chat/SuggestionItem.tsx +4 -3
- package/src/components/chat/footer/index.tsx +95 -14
- package/src/components/chat/footer/item/UploadImageItem.tsx +21 -1
- package/src/components/chat/index.tsx +8 -11
- package/src/components/chat/item/ChatAIAnswerMessageItem.tsx +55 -6
- package/src/components/chat/item/DeeplinkItem.tsx +30 -2
- package/src/components/chat/item/MessageActionsBar.tsx +326 -0
- package/src/components/chat/item/actions/ActionButton.tsx +65 -0
- package/src/components/portal/BottomSheet.tsx +307 -0
- package/src/components/portal/Popup.tsx +345 -0
- package/src/components/portal/index.tsx +5 -1
- package/src/components/product/CardHorizontal.tsx +45 -10
- package/src/constants/events.ts +34 -0
- package/src/constants/index.ts +6 -1
- package/src/constants/query.ts +4 -0
- package/src/context/ChatContext.tsx +6 -0
- package/src/hooks/message/useSendMessage.ts +47 -4
- package/src/hooks/message/useStreamMessage.ts +15 -4
- package/src/hooks/messageActions/index.ts +10 -0
- package/src/hooks/messageActions/useAudioPlayer.ts +346 -0
- package/src/hooks/messageActions/useCopyToClipboard.ts +38 -0
- package/src/hooks/messageActions/useFeedback.ts +114 -0
- package/src/hooks/messageActions/useSendFeedback.ts +31 -0
- package/src/hooks/messageActions/useShareMessage.ts +146 -0
- package/src/hooks/session/useDeleteSession.ts +25 -0
- package/src/hooks/session/useRenameSession.ts +37 -0
- package/src/hooks/session/useSearchSessions.ts +6 -1
- package/src/hooks/session/useShareSession.ts +22 -0
- package/src/hooks/upload/useImageUpload.ts +6 -2
- package/src/ignore.d.ts +20 -1
- package/src/services/endpoints.ts +10 -0
- package/src/services/index.ts +1 -0
- package/src/services/playbackService.ts +22 -0
- package/src/store/audioPlayer.ts +112 -0
- package/src/store/messageActions.ts +161 -0
- package/src/store/session.ts +4 -2
- package/src/translation/index.ts +27 -19
- package/src/translation/resources/i18n.ts +45 -0
- package/src/types/chat.ts +21 -1
- package/src/types/dto.ts +14 -0
- package/src/types/index.ts +1 -0
- package/src/types/messageActions.ts +131 -0
- package/src/types/ui.ts +19 -1
- package/src/utils/textCleaner.ts +65 -0
- package/src/utils/ui.tsx +29 -2
- package/lib/module/translation/resources/vi.js +0 -12
- package/lib/module/translation/resources/vi.js.map +0 -1
- package/lib/typescript/src/translation/resources/vi.d.ts +0 -11
- package/lib/typescript/src/translation/resources/vi.d.ts.map +0 -1
- package/src/translation/resources/vi.ts +0 -10
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useCopyToClipboard Hook
|
|
3
|
+
* Custom hook for copying text to clipboard with markdown cleanup
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useCallback, useState } from 'react';
|
|
7
|
+
import Clipboard from '@react-native-clipboard/clipboard';
|
|
8
|
+
import UIUtils from '../../utils/ui';
|
|
9
|
+
import { cleanMarkdown } from '../../utils/textCleaner';
|
|
10
|
+
|
|
11
|
+
export const useCopyToClipboard = () => {
|
|
12
|
+
const [isCopying, setIsCopying] = useState(false);
|
|
13
|
+
|
|
14
|
+
const copyToClipboard = useCallback(async (text: string) => {
|
|
15
|
+
if (!text || text.trim().length === 0) {
|
|
16
|
+
UIUtils.toast.showError('Không có nội dung để sao chép');
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
setIsCopying(true);
|
|
22
|
+
|
|
23
|
+
// Remove markdown formatting for cleaner copy
|
|
24
|
+
const cleanText = cleanMarkdown(text);
|
|
25
|
+
|
|
26
|
+
Clipboard.setString(cleanText);
|
|
27
|
+
|
|
28
|
+
UIUtils.toast.showSuccess('Đã sao chép vào clipboard');
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error('Copy to clipboard error:', error);
|
|
31
|
+
UIUtils.toast.showError(error);
|
|
32
|
+
} finally {
|
|
33
|
+
setIsCopying(false);
|
|
34
|
+
}
|
|
35
|
+
}, []);
|
|
36
|
+
|
|
37
|
+
return { copyToClipboard, isCopying };
|
|
38
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useFeedback Hook
|
|
3
|
+
* Custom hook for handling Like/Dislike feedback logic
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useCallback } from 'react';
|
|
7
|
+
import useMessageActionsStore from '../../store/messageActions';
|
|
8
|
+
import { FeedbackType } from '../../types/messageActions';
|
|
9
|
+
import { useSendFeedback } from './useSendFeedback';
|
|
10
|
+
import UIUtils from '../../utils/ui';
|
|
11
|
+
import type { IMessageItem } from '../../types/dto';
|
|
12
|
+
import useSessionStore from '../../store/session';
|
|
13
|
+
import { useChatContext } from '../../context/ChatContext';
|
|
14
|
+
import { GAEvents } from 'src/constants/events';
|
|
15
|
+
|
|
16
|
+
export const useFeedback = (messageId: string, message?: IMessageItem) => {
|
|
17
|
+
const logGA = useChatContext().logGA;
|
|
18
|
+
const sessionId = useSessionStore((state) => state.sessionId);
|
|
19
|
+
|
|
20
|
+
const feedback = useMessageActionsStore((state) =>
|
|
21
|
+
state.getFeedback(messageId)
|
|
22
|
+
);
|
|
23
|
+
const setFeedback = useMessageActionsStore((state) => state.setFeedback);
|
|
24
|
+
const activeFeedbackList = useMessageActionsStore(
|
|
25
|
+
(state) => state.activeFeedbackList
|
|
26
|
+
);
|
|
27
|
+
const setActiveFeedbackList = useMessageActionsStore(
|
|
28
|
+
(state) => state.setActiveFeedbackList
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const { mutateAsync: sendFeedback, isLoading } = useSendFeedback();
|
|
32
|
+
|
|
33
|
+
// Check feedback from server (message) or local store
|
|
34
|
+
const serverFeedbackType = message?.feedback_type;
|
|
35
|
+
const serverFeedbackContent = message?.feedback_content;
|
|
36
|
+
|
|
37
|
+
const effectiveFeedback =
|
|
38
|
+
feedback ||
|
|
39
|
+
(serverFeedbackType && serverFeedbackContent
|
|
40
|
+
? {
|
|
41
|
+
messageId,
|
|
42
|
+
feedbackType: serverFeedbackType as FeedbackType,
|
|
43
|
+
reason: serverFeedbackContent,
|
|
44
|
+
timestamp: message.modified_at,
|
|
45
|
+
}
|
|
46
|
+
: null);
|
|
47
|
+
|
|
48
|
+
const isLikeActive = effectiveFeedback?.feedbackType === FeedbackType.like;
|
|
49
|
+
const isDislikeActive =
|
|
50
|
+
effectiveFeedback?.feedbackType === FeedbackType.dislike;
|
|
51
|
+
const isLikeListOpen = activeFeedbackList === `${messageId}-like`;
|
|
52
|
+
const isDislikeListOpen = activeFeedbackList === `${messageId}-dislike`;
|
|
53
|
+
|
|
54
|
+
const toggleLikeList = useCallback(() => {
|
|
55
|
+
if (isLikeActive) return; // Already selected, cannot change
|
|
56
|
+
setActiveFeedbackList(isLikeListOpen ? null : `${messageId}-like`);
|
|
57
|
+
}, [messageId, isLikeActive, isLikeListOpen, setActiveFeedbackList]);
|
|
58
|
+
|
|
59
|
+
const toggleDislikeList = useCallback(() => {
|
|
60
|
+
if (isDislikeActive) return; // Already selected, cannot change
|
|
61
|
+
setActiveFeedbackList(isDislikeListOpen ? null : `${messageId}-dislike`);
|
|
62
|
+
}, [messageId, isDislikeActive, isDislikeListOpen, setActiveFeedbackList]);
|
|
63
|
+
|
|
64
|
+
const selectReason = useCallback(
|
|
65
|
+
async (type: FeedbackType, reason: string) => {
|
|
66
|
+
console.log('selectReason', sessionId);
|
|
67
|
+
if (!sessionId) {
|
|
68
|
+
console.error('Session ID is required to send feedback');
|
|
69
|
+
UIUtils.toast.open({
|
|
70
|
+
title: 'Không thể gửi phản hồi. Vui lòng thử lại.',
|
|
71
|
+
});
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
// Send to backend FIRST (no optimistic update)
|
|
77
|
+
await sendFeedback({
|
|
78
|
+
sessionId,
|
|
79
|
+
payload: {
|
|
80
|
+
message_id: messageId,
|
|
81
|
+
feedback_type: type,
|
|
82
|
+
feedback_content: reason,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
logGA(GAEvents.chatDetailLikeDislikeBtnTap, {
|
|
87
|
+
fb_type: type,
|
|
88
|
+
message_id: messageId,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Update local state AFTER successful API call
|
|
92
|
+
setFeedback(messageId, type, reason);
|
|
93
|
+
|
|
94
|
+
UIUtils.toast.showSuccess('Cảm ơn phản hồi của bạn!');
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.error('Failed to send feedback:', error);
|
|
97
|
+
UIUtils.toast.showError(error);
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
[messageId, setFeedback, sendFeedback, sessionId, logGA]
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
feedback: effectiveFeedback,
|
|
105
|
+
isLikeActive,
|
|
106
|
+
isDislikeActive,
|
|
107
|
+
isLikeListOpen,
|
|
108
|
+
isDislikeListOpen,
|
|
109
|
+
isLoading,
|
|
110
|
+
toggleLikeList,
|
|
111
|
+
toggleDislikeList,
|
|
112
|
+
selectReason,
|
|
113
|
+
};
|
|
114
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useSendFeedback Hook
|
|
3
|
+
* React Query mutation hook for sending feedback to backend
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useMutation } from '@tanstack/react-query';
|
|
7
|
+
import { apiInstance } from '../../services/apis';
|
|
8
|
+
import { ENDPOINTS } from '../../services/endpoints';
|
|
9
|
+
import { SendFeedbackRequest, SendFeedbackResponse } from '../../types/dto';
|
|
10
|
+
import { QUERY_KEYS } from '../../constants/query';
|
|
11
|
+
import { BaseResponse } from 'src/types';
|
|
12
|
+
|
|
13
|
+
interface SendFeedbackParams {
|
|
14
|
+
sessionId: string;
|
|
15
|
+
payload: SendFeedbackRequest;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const useSendFeedback = () =>
|
|
19
|
+
useMutation({
|
|
20
|
+
mutationKey: [QUERY_KEYS.SEND_FEEDBACK],
|
|
21
|
+
mutationFn: async ({ sessionId, payload }: SendFeedbackParams) => {
|
|
22
|
+
const res = await apiInstance?.put<BaseResponse<SendFeedbackResponse>>(
|
|
23
|
+
ENDPOINTS.assistantService.sendFeedback(sessionId),
|
|
24
|
+
payload
|
|
25
|
+
);
|
|
26
|
+
return res?.data?.data;
|
|
27
|
+
},
|
|
28
|
+
retry: 2,
|
|
29
|
+
retryDelay: (attemptIndex: number) =>
|
|
30
|
+
Math.min(1000 * 2 ** attemptIndex, 30000),
|
|
31
|
+
});
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useShareMessage Hook
|
|
3
|
+
* Custom hook for sharing message content via native share sheet
|
|
4
|
+
* Supports platform-specific sharing (Zalo, Facebook, Messenger)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { useCallback } from 'react';
|
|
8
|
+
import Share from 'react-native-share';
|
|
9
|
+
import RNFetchBlob from 'react-native-blob-util';
|
|
10
|
+
import useMessageActionsStore from '../../store/messageActions';
|
|
11
|
+
import { ShareState } from '../../types/messageActions';
|
|
12
|
+
import UIUtils from '../../utils/ui';
|
|
13
|
+
import { cleanMarkdown, truncateText } from '../../utils/textCleaner';
|
|
14
|
+
import { useChatContext } from '../../context/ChatContext';
|
|
15
|
+
import { GAEvents } from '../../constants/events';
|
|
16
|
+
import useSessionStore from '../../store/session';
|
|
17
|
+
|
|
18
|
+
const MAX_SHARE_TEXT_LENGTH = 3000;
|
|
19
|
+
|
|
20
|
+
export const useShareMessage = (
|
|
21
|
+
messageId: string,
|
|
22
|
+
messageContent: string,
|
|
23
|
+
productImages?: string[]
|
|
24
|
+
) => {
|
|
25
|
+
const logGA = useChatContext().logGA;
|
|
26
|
+
const shareState = useMessageActionsStore((state) =>
|
|
27
|
+
state.getShareState(messageId)
|
|
28
|
+
);
|
|
29
|
+
const setShareState = useMessageActionsStore((state) => state.setShareState);
|
|
30
|
+
|
|
31
|
+
const currentState = shareState?.state || ShareState.idle;
|
|
32
|
+
|
|
33
|
+
const shareMessage = useCallback(async () => {
|
|
34
|
+
if (!messageContent || messageContent.trim().length === 0) {
|
|
35
|
+
UIUtils.toast.showError('Không có nội dung để chia sẻ');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
setShareState(messageId, ShareState.loading);
|
|
41
|
+
|
|
42
|
+
// Clean and truncate text
|
|
43
|
+
const cleanText = cleanMarkdown(messageContent);
|
|
44
|
+
const truncatedText = truncateText(cleanText, MAX_SHARE_TEXT_LENGTH);
|
|
45
|
+
|
|
46
|
+
const localImagePaths: string[] = [];
|
|
47
|
+
|
|
48
|
+
// Download all images to local cache if available
|
|
49
|
+
if (productImages && productImages.length > 0) {
|
|
50
|
+
for (let i = 0; i < productImages.length; i++) {
|
|
51
|
+
const imageUrl = productImages[i];
|
|
52
|
+
if (!imageUrl || imageUrl.trim() === '') {
|
|
53
|
+
console.warn(`[Share] Skipping empty image URL at index ${i}`);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
// Use unique timestamp for each image to avoid conflicts
|
|
59
|
+
const timestamp = Date.now() + i;
|
|
60
|
+
const imagePath = `${RNFetchBlob.fs.dirs.CacheDir}/share_image_${timestamp}.jpg`;
|
|
61
|
+
|
|
62
|
+
await RNFetchBlob.config({
|
|
63
|
+
path: imagePath,
|
|
64
|
+
}).fetch('GET', imageUrl);
|
|
65
|
+
|
|
66
|
+
// Verify file exists and has content
|
|
67
|
+
const fileExists = await RNFetchBlob.fs.exists(imagePath);
|
|
68
|
+
if (!fileExists) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const fileInfo = await RNFetchBlob.fs.stat(imagePath);
|
|
73
|
+
if (fileInfo.size === 0) {
|
|
74
|
+
await RNFetchBlob.fs.unlink(imagePath);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
localImagePaths.push(`file://${imagePath}`);
|
|
79
|
+
} catch (imageError) {
|
|
80
|
+
console.error(
|
|
81
|
+
`[Share] Failed to download image ${i} (${imageUrl}):`,
|
|
82
|
+
imageError
|
|
83
|
+
);
|
|
84
|
+
// Continue with other images
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Prepare share options
|
|
90
|
+
const shareOptions: any = {
|
|
91
|
+
title: 'Chia sẻ từ Chatbot AI',
|
|
92
|
+
message: truncatedText,
|
|
93
|
+
failOnCancel: false,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// Add image URLs if available
|
|
97
|
+
if (localImagePaths.length === 1) {
|
|
98
|
+
shareOptions.url = localImagePaths[0];
|
|
99
|
+
} else if (localImagePaths.length > 1) {
|
|
100
|
+
shareOptions.urls = localImagePaths;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Open share dialog
|
|
104
|
+
// Platform-specific sharing:
|
|
105
|
+
const result = await Share.open(shareOptions);
|
|
106
|
+
|
|
107
|
+
// Handle success
|
|
108
|
+
if (result) {
|
|
109
|
+
setShareState(messageId, ShareState.success);
|
|
110
|
+
|
|
111
|
+
// Clean up temporary image files
|
|
112
|
+
for (const localImagePath of localImagePaths) {
|
|
113
|
+
try {
|
|
114
|
+
const cleanPath = localImagePath.replace('file://', '');
|
|
115
|
+
await RNFetchBlob.fs.unlink(cleanPath);
|
|
116
|
+
} catch (cleanupError) {
|
|
117
|
+
console.warn('[Share] Failed to cleanup temp image:', cleanupError);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
logGA(GAEvents.chatDetailShareMessBtnTap, {
|
|
121
|
+
conversation_id: useSessionStore.getState().sessionId,
|
|
122
|
+
message_id: messageId,
|
|
123
|
+
});
|
|
124
|
+
} else {
|
|
125
|
+
setShareState(messageId, ShareState.idle);
|
|
126
|
+
}
|
|
127
|
+
} catch (error: any) {
|
|
128
|
+
console.error('Share error:', error);
|
|
129
|
+
|
|
130
|
+
// User cancelled share dialog
|
|
131
|
+
if (error?.message?.includes('User did not share')) {
|
|
132
|
+
setShareState(messageId, ShareState.idle);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
setShareState(messageId, ShareState.error);
|
|
137
|
+
UIUtils.toast.showError('Không thể chia sẻ. Vui lòng thử lại.');
|
|
138
|
+
}
|
|
139
|
+
}, [messageId, messageContent, productImages, setShareState, logGA]);
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
shareState: currentState,
|
|
143
|
+
isLoading: currentState === ShareState.loading,
|
|
144
|
+
shareMessage,
|
|
145
|
+
};
|
|
146
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
2
|
+
import { QUERY_KEYS } from '../../constants/query';
|
|
3
|
+
import { apiInstance } from '../../services/apis';
|
|
4
|
+
import { ENDPOINTS } from '../../services/endpoints';
|
|
5
|
+
import { BaseResponse } from '../../types/common';
|
|
6
|
+
|
|
7
|
+
export const useDeleteSession = () => {
|
|
8
|
+
const queryClient = useQueryClient();
|
|
9
|
+
|
|
10
|
+
return useMutation({
|
|
11
|
+
mutationKey: [QUERY_KEYS.DELETE_SESSION],
|
|
12
|
+
mutationFn: async (sessionId: string) => {
|
|
13
|
+
const res = await apiInstance?.delete<BaseResponse<any>>(
|
|
14
|
+
ENDPOINTS.assistantService.deleteSession(sessionId)
|
|
15
|
+
);
|
|
16
|
+
return res?.data;
|
|
17
|
+
},
|
|
18
|
+
onSuccess: () => {
|
|
19
|
+
// Refetch sessions list after successful delete
|
|
20
|
+
queryClient.refetchQueries({
|
|
21
|
+
queryKey: [QUERY_KEYS.SEARCH_SESSIONS],
|
|
22
|
+
});
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
2
|
+
import { QUERY_KEYS } from '../../constants/query';
|
|
3
|
+
import { apiInstance } from '../../services/apis';
|
|
4
|
+
import { ENDPOINTS } from '../../services/endpoints';
|
|
5
|
+
import { BaseResponse } from '../../types/common';
|
|
6
|
+
import { SessionSearchItem } from '../../types/dto';
|
|
7
|
+
|
|
8
|
+
interface RenameSessionRequest {
|
|
9
|
+
title: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const useRenameSession = () => {
|
|
13
|
+
const queryClient = useQueryClient();
|
|
14
|
+
|
|
15
|
+
return useMutation({
|
|
16
|
+
mutationKey: [QUERY_KEYS.UPDATE_SESSION],
|
|
17
|
+
mutationFn: async ({
|
|
18
|
+
sessionId,
|
|
19
|
+
title,
|
|
20
|
+
}: {
|
|
21
|
+
sessionId: string;
|
|
22
|
+
title: string;
|
|
23
|
+
}) => {
|
|
24
|
+
const res = await apiInstance?.put<BaseResponse<SessionSearchItem>>(
|
|
25
|
+
ENDPOINTS.assistantService.updateSession(sessionId),
|
|
26
|
+
{ title } as RenameSessionRequest
|
|
27
|
+
);
|
|
28
|
+
return res?.data?.data;
|
|
29
|
+
},
|
|
30
|
+
onSuccess: () => {
|
|
31
|
+
// Refetch sessions list after successful rename
|
|
32
|
+
queryClient.refetchQueries({
|
|
33
|
+
queryKey: [QUERY_KEYS.SEARCH_SESSIONS],
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
};
|
|
@@ -10,7 +10,8 @@ const LIMIT = 20;
|
|
|
10
10
|
|
|
11
11
|
export const useSearchSessions = (
|
|
12
12
|
enabled: boolean = true,
|
|
13
|
-
searchTerm?: string
|
|
13
|
+
searchTerm?: string,
|
|
14
|
+
cb?: (isSuccess?: boolean) => void
|
|
14
15
|
) => {
|
|
15
16
|
const queryKey = [QUERY_KEYS.SEARCH_SESSIONS, searchTerm];
|
|
16
17
|
|
|
@@ -30,11 +31,13 @@ export const useSearchSessions = (
|
|
|
30
31
|
|
|
31
32
|
// Validate response structure
|
|
32
33
|
if (!response) {
|
|
34
|
+
cb?.(false);
|
|
33
35
|
throw new Error('No response received from API');
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
// Check for successful response: HTTP status 200 and API statusCode 0
|
|
37
39
|
if (res?.status !== 200 || response?.statusCode !== 0) {
|
|
40
|
+
cb?.(false);
|
|
38
41
|
return {
|
|
39
42
|
...response,
|
|
40
43
|
data: [],
|
|
@@ -44,12 +47,14 @@ export const useSearchSessions = (
|
|
|
44
47
|
// Ensure data is an array
|
|
45
48
|
if (!Array.isArray(response?.data)) {
|
|
46
49
|
// If data is not an array, return empty array to prevent crashes
|
|
50
|
+
cb?.(false);
|
|
47
51
|
return {
|
|
48
52
|
...response,
|
|
49
53
|
data: [],
|
|
50
54
|
} as BaseResponse<SearchSessionsResponse>;
|
|
51
55
|
}
|
|
52
56
|
|
|
57
|
+
cb?.(true);
|
|
53
58
|
return response as BaseResponse<SearchSessionsResponse>;
|
|
54
59
|
},
|
|
55
60
|
getNextPageParam: (
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useMutation } from '@tanstack/react-query';
|
|
2
|
+
import { QUERY_KEYS } from '../../constants/query';
|
|
3
|
+
import { apiInstance } from '../../services/apis';
|
|
4
|
+
import { ENDPOINTS } from '../../services/endpoints';
|
|
5
|
+
import { BaseResponse } from '../../types/common';
|
|
6
|
+
|
|
7
|
+
interface ShareSessionResponse {
|
|
8
|
+
share_url: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const useShareSession = () => {
|
|
12
|
+
return useMutation({
|
|
13
|
+
mutationKey: [QUERY_KEYS.SHARE_SESSION],
|
|
14
|
+
mutationFn: async (sessionId: string) => {
|
|
15
|
+
const res = await apiInstance?.put<BaseResponse<ShareSessionResponse>>(
|
|
16
|
+
ENDPOINTS.assistantService.shareSession(sessionId),
|
|
17
|
+
{}
|
|
18
|
+
);
|
|
19
|
+
return res?.data?.data;
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
};
|
|
@@ -16,7 +16,7 @@ type RNBlobTask = {
|
|
|
16
16
|
|
|
17
17
|
interface UseImageUploadProps {
|
|
18
18
|
file: ImageUpload;
|
|
19
|
-
onSuccess?: (data: any) => void;
|
|
19
|
+
onSuccess?: (data: any, duration?: number) => void;
|
|
20
20
|
onError?: (err: any) => void;
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -70,6 +70,7 @@ export const useImageUpload = ({
|
|
|
70
70
|
|
|
71
71
|
cancelRef.current = task;
|
|
72
72
|
|
|
73
|
+
const startUploadTime = Date.now();
|
|
73
74
|
task
|
|
74
75
|
.uploadProgress?.({ interval: 300 }, (written, total) => {
|
|
75
76
|
if (isMounted && total > 0) {
|
|
@@ -87,7 +88,10 @@ export const useImageUpload = ({
|
|
|
87
88
|
setIsUploading(false);
|
|
88
89
|
setProgress(100);
|
|
89
90
|
setIsSuccess(true);
|
|
90
|
-
onSuccess?.(
|
|
91
|
+
onSuccess?.(
|
|
92
|
+
response?.data,
|
|
93
|
+
(Date.now() - startUploadTime) / 1000
|
|
94
|
+
);
|
|
91
95
|
})
|
|
92
96
|
.catch((err: any) => {
|
|
93
97
|
if (!isMounted) return;
|
package/src/ignore.d.ts
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
1
|
declare module '@droppii/libs/*';
|
|
2
|
-
declare module '@droppii/libs'
|
|
2
|
+
declare module '@droppii/libs' {
|
|
3
|
+
export type KBottomSheetProps = any;
|
|
4
|
+
export type KPopupProps = any;
|
|
5
|
+
export type KToastBarProps = any;
|
|
6
|
+
|
|
7
|
+
export const KColors: any;
|
|
8
|
+
export const KSpacingValue: any;
|
|
9
|
+
export const KContainer: any;
|
|
10
|
+
export const KLabel: any;
|
|
11
|
+
export const KImage: any;
|
|
12
|
+
export const KButton: any;
|
|
13
|
+
export const KInput: any;
|
|
14
|
+
export const KRadiusValue: any;
|
|
15
|
+
export const KDivider: any;
|
|
16
|
+
export const KListItem: any;
|
|
17
|
+
export const KDims: any;
|
|
18
|
+
export const KBars: any;
|
|
19
|
+
export const KRating: any;
|
|
20
|
+
export const KPromotionTag: any;
|
|
21
|
+
}
|
|
3
22
|
declare module '*';
|
|
@@ -7,6 +7,16 @@ export const ENDPOINTS = {
|
|
|
7
7
|
`/assistant-service/v1/app/chat/sessions/${sessionId}/stream`,
|
|
8
8
|
createSession: '/assistant-service/v1/app/chat/sessions',
|
|
9
9
|
searchSessions: '/assistant-service/v1/app/chat/sessions/search',
|
|
10
|
+
updateSession: (sessionId: string) =>
|
|
11
|
+
`/assistant-service/v1/app/chat/sessions/${sessionId}`,
|
|
12
|
+
shareSession: (sessionId: string) =>
|
|
13
|
+
`/assistant-service/v1/app/chat/sessions/${sessionId}/share`,
|
|
14
|
+
deleteSession: (sessionId: string) =>
|
|
15
|
+
`/assistant-service/v1/app/chat/sessions/${sessionId}`,
|
|
16
|
+
sendFeedback: (sessionId: string) =>
|
|
17
|
+
`/assistant-service/v1/app/chat/sessions/${sessionId}/feedback`,
|
|
18
|
+
generateAudio: (sessionId: string, messageId: string) =>
|
|
19
|
+
`/assistant-service/v1/app/chat/sessions/${sessionId}/messages/${messageId}/speech`,
|
|
10
20
|
},
|
|
11
21
|
uploaderService: {
|
|
12
22
|
upload: '/uploader-service/v1/uploader/permanently',
|
package/src/services/index.ts
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playback Service for react-native-track-player
|
|
3
|
+
* Handles remote control events from lock screen, control center, and headphones
|
|
4
|
+
*/
|
|
5
|
+
import TrackPlayer, { Event } from 'react-native-track-player';
|
|
6
|
+
|
|
7
|
+
export async function PlaybackService() {
|
|
8
|
+
TrackPlayer.addEventListener(Event.RemotePlay, async () => {
|
|
9
|
+
console.log('[PlaybackService] RemotePlay event');
|
|
10
|
+
await TrackPlayer.play();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
TrackPlayer.addEventListener(Event.RemotePause, async () => {
|
|
14
|
+
console.log('[PlaybackService] RemotePause event');
|
|
15
|
+
await TrackPlayer.pause();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
TrackPlayer.addEventListener(Event.RemoteStop, async () => {
|
|
19
|
+
console.log('[PlaybackService] RemoteStop event');
|
|
20
|
+
await TrackPlayer.stop();
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audio Player Store
|
|
3
|
+
* Manages audio playback state across messages
|
|
4
|
+
* Handles play/pause/stop for each message and session cleanup
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { create } from 'zustand';
|
|
8
|
+
|
|
9
|
+
export interface AudioPlayerState {
|
|
10
|
+
// Currently playing message ID
|
|
11
|
+
currentMessageId: string | null;
|
|
12
|
+
// Current session ID
|
|
13
|
+
currentSessionId: string | null;
|
|
14
|
+
// Playing state
|
|
15
|
+
isPlaying: boolean;
|
|
16
|
+
// Loading state (fetching audio from server)
|
|
17
|
+
isLoading: boolean;
|
|
18
|
+
// Audio URL cache: messageId -> audioUrl
|
|
19
|
+
audioCache: Map<string, string>;
|
|
20
|
+
// Error state
|
|
21
|
+
error: string | null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface AudioPlayerActions {
|
|
25
|
+
// Set currently playing message
|
|
26
|
+
setCurrentMessage: (messageId: string | null, sessionId: string) => void;
|
|
27
|
+
// Set playing state
|
|
28
|
+
setIsPlaying: (isPlaying: boolean) => void;
|
|
29
|
+
// Set loading state
|
|
30
|
+
setIsLoading: (isLoading: boolean) => void;
|
|
31
|
+
// Cache audio URL for a message
|
|
32
|
+
cacheAudioUrl: (messageId: string, audioUrl: string) => void;
|
|
33
|
+
// Get cached audio URL
|
|
34
|
+
getCachedAudioUrl: (messageId: string) => string | null;
|
|
35
|
+
// Set error
|
|
36
|
+
setError: (error: string | null) => void;
|
|
37
|
+
// Clear all state (when session changes)
|
|
38
|
+
clearSession: () => void;
|
|
39
|
+
// Stop current playback
|
|
40
|
+
stopPlayback: () => void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type AudioPlayerStore = AudioPlayerState & AudioPlayerActions;
|
|
44
|
+
|
|
45
|
+
const useAudioPlayerStore = create<AudioPlayerStore>((set, get) => ({
|
|
46
|
+
// Initial state
|
|
47
|
+
currentMessageId: null,
|
|
48
|
+
currentSessionId: null,
|
|
49
|
+
isPlaying: false,
|
|
50
|
+
isLoading: false,
|
|
51
|
+
audioCache: new Map(),
|
|
52
|
+
error: null,
|
|
53
|
+
|
|
54
|
+
// Actions
|
|
55
|
+
setCurrentMessage: (messageId, sessionId) => {
|
|
56
|
+
const state = get();
|
|
57
|
+
|
|
58
|
+
// If switching to a different session, clear the cache
|
|
59
|
+
if (state.currentSessionId && state.currentSessionId !== sessionId) {
|
|
60
|
+
set({
|
|
61
|
+
currentMessageId: messageId,
|
|
62
|
+
currentSessionId: sessionId,
|
|
63
|
+
audioCache: new Map(),
|
|
64
|
+
isPlaying: false,
|
|
65
|
+
error: null,
|
|
66
|
+
});
|
|
67
|
+
} else {
|
|
68
|
+
set({
|
|
69
|
+
currentMessageId: messageId,
|
|
70
|
+
currentSessionId: sessionId,
|
|
71
|
+
error: null,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
setIsPlaying: (isPlaying) => set({ isPlaying }),
|
|
77
|
+
|
|
78
|
+
setIsLoading: (isLoading) => set({ isLoading }),
|
|
79
|
+
|
|
80
|
+
cacheAudioUrl: (messageId, audioUrl) => {
|
|
81
|
+
const state = get();
|
|
82
|
+
const newCache = new Map(state.audioCache);
|
|
83
|
+
newCache.set(messageId, audioUrl);
|
|
84
|
+
set({ audioCache: newCache });
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
getCachedAudioUrl: (messageId) => {
|
|
88
|
+
const state = get();
|
|
89
|
+
return state.audioCache.get(messageId) || null;
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
setError: (error) => set({ error }),
|
|
93
|
+
|
|
94
|
+
clearSession: () =>
|
|
95
|
+
set({
|
|
96
|
+
currentMessageId: null,
|
|
97
|
+
currentSessionId: null,
|
|
98
|
+
isPlaying: false,
|
|
99
|
+
isLoading: false,
|
|
100
|
+
audioCache: new Map(),
|
|
101
|
+
error: null,
|
|
102
|
+
}),
|
|
103
|
+
|
|
104
|
+
stopPlayback: () =>
|
|
105
|
+
set({
|
|
106
|
+
currentMessageId: null,
|
|
107
|
+
isPlaying: false,
|
|
108
|
+
error: null,
|
|
109
|
+
}),
|
|
110
|
+
}));
|
|
111
|
+
|
|
112
|
+
export default useAudioPlayerStore;
|