@droppii-org/chat-mobile 0.2.50 → 0.2.52
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/assets/images/index.js +2 -1
- package/lib/module/assets/images/index.js.map +1 -1
- package/lib/module/assets/images/resource-center-empty-box.png +0 -0
- package/lib/module/components/MediaViewerModal.js +2 -1
- package/lib/module/components/MediaViewerModal.js.map +1 -1
- package/lib/module/components/MergedImageGrid.js +1 -0
- package/lib/module/components/MergedImageGrid.js.map +1 -1
- package/lib/module/components/StickerPicker/StickerCategoryTabs.js +86 -0
- package/lib/module/components/StickerPicker/StickerCategoryTabs.js.map +1 -0
- package/lib/module/components/StickerPicker/StickerPickerPlaceholder.js +18 -0
- package/lib/module/components/StickerPicker/StickerPickerPlaceholder.js.map +1 -0
- package/lib/module/components/StickerPicker/StickerPickerRowItem.js +35 -0
- package/lib/module/components/StickerPicker/StickerPickerRowItem.js.map +1 -0
- package/lib/module/components/StickerPicker/StickerSectionList.js +110 -0
- package/lib/module/components/StickerPicker/StickerSectionList.js.map +1 -0
- package/lib/module/components/StickerPicker/constants.js +14 -0
- package/lib/module/components/StickerPicker/constants.js.map +1 -0
- package/lib/module/components/StickerPicker/index.js +134 -0
- package/lib/module/components/StickerPicker/index.js.map +1 -0
- package/lib/module/components/messages/stickerMessage/index.js +28 -0
- package/lib/module/components/messages/stickerMessage/index.js.map +1 -0
- package/lib/module/config/api-endpoints.js +5 -1
- package/lib/module/config/api-endpoints.js.map +1 -1
- package/lib/module/core/notification/pushToken.js +7 -7
- package/lib/module/core/notification/pushToken.js.map +1 -1
- package/lib/module/hooks/conversation/useGetConversationDetail.js +15 -0
- package/lib/module/hooks/conversation/useGetConversationDetail.js.map +1 -1
- package/lib/module/hooks/flows/useGetConversationFlowMainMenu.js +1 -1
- package/lib/module/hooks/flows/useGetConversationFlowMainMenu.js.map +1 -1
- package/lib/module/hooks/message/useSendMessage.js +45 -18
- package/lib/module/hooks/message/useSendMessage.js.map +1 -1
- package/lib/module/hooks/query-keys.js +11 -0
- package/lib/module/hooks/query-keys.js.map +1 -1
- package/lib/module/hooks/resourceCenter/useThreadResources.js +116 -0
- package/lib/module/hooks/resourceCenter/useThreadResources.js.map +1 -0
- package/lib/module/hooks/stickers/useFetchStickerByCategory.js +12 -0
- package/lib/module/hooks/stickers/useFetchStickerByCategory.js.map +1 -0
- package/lib/module/hooks/stickers/useStickerCategories.js +14 -0
- package/lib/module/hooks/stickers/useStickerCategories.js.map +1 -0
- package/lib/module/hooks/stickers/useStickerHistory.js +16 -0
- package/lib/module/hooks/stickers/useStickerHistory.js.map +1 -0
- package/lib/module/hooks/useChatCompose.js +1 -18
- package/lib/module/hooks/useChatCompose.js.map +1 -1
- package/lib/module/hooks/useImageAttachment.js +0 -2
- package/lib/module/hooks/useImageAttachment.js.map +1 -1
- package/lib/module/hooks/useMediaViewer.js +4 -1
- package/lib/module/hooks/useMediaViewer.js.map +1 -1
- package/lib/module/hooks/useSendAttachment.js +4 -1
- package/lib/module/hooks/useSendAttachment.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/screens/MediaView/index.js +104 -42
- package/lib/module/screens/MediaView/index.js.map +1 -1
- package/lib/module/screens/ResourceCenter/FileResourceRow.js +80 -0
- package/lib/module/screens/ResourceCenter/FileResourceRow.js.map +1 -0
- package/lib/module/screens/ResourceCenter/GridRenderer.js +83 -0
- package/lib/module/screens/ResourceCenter/GridRenderer.js.map +1 -0
- package/lib/module/screens/ResourceCenter/GridThumbnail.js +97 -0
- package/lib/module/screens/ResourceCenter/GridThumbnail.js.map +1 -0
- package/lib/module/screens/ResourceCenter/LinkResourceRow.js +81 -0
- package/lib/module/screens/ResourceCenter/LinkResourceRow.js.map +1 -0
- package/lib/module/screens/ResourceCenter/ListRenderer.js +60 -0
- package/lib/module/screens/ResourceCenter/ListRenderer.js.map +1 -0
- package/lib/module/screens/ResourceCenter/ResourceCenterTabBar.js +75 -0
- package/lib/module/screens/ResourceCenter/ResourceCenterTabBar.js.map +1 -0
- package/lib/module/screens/ResourceCenter/ResourceCenterTabScene.js +122 -0
- package/lib/module/screens/ResourceCenter/ResourceCenterTabScene.js.map +1 -0
- package/lib/module/screens/ResourceCenter/ResourceEmptyState.js +36 -0
- package/lib/module/screens/ResourceCenter/ResourceEmptyState.js.map +1 -0
- package/lib/module/screens/ResourceCenter/index.js +104 -0
- package/lib/module/screens/ResourceCenter/index.js.map +1 -0
- package/lib/module/screens/ResourceCenter/types.js +25 -0
- package/lib/module/screens/ResourceCenter/types.js.map +1 -0
- package/lib/module/screens/chat-detail/ChatComposer.js +64 -16
- package/lib/module/screens/chat-detail/ChatComposer.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatDetail.js +6 -20
- package/lib/module/screens/chat-detail/ChatDetail.js.map +1 -1
- package/lib/module/screens/chat-detail/hooks/useChatComposerAnimation.js +15 -4
- package/lib/module/screens/chat-detail/hooks/useChatComposerAnimation.js.map +1 -1
- package/lib/module/screens/chat-detail/hooks/useChatComposerState.js +3 -0
- package/lib/module/screens/chat-detail/hooks/useChatComposerState.js.map +1 -1
- package/lib/module/screens/chat-detail/legend/LegendChatMessage.js +5 -6
- package/lib/module/screens/chat-detail/legend/LegendChatMessage.js.map +1 -1
- package/lib/module/services/attachmentOrchestrator.js +20 -5
- package/lib/module/services/attachmentOrchestrator.js.map +1 -1
- package/lib/module/services/endpoints.js +4 -1
- package/lib/module/services/endpoints.js.map +1 -1
- package/lib/module/services/resourceCenter.js +102 -0
- package/lib/module/services/resourceCenter.js.map +1 -0
- package/lib/module/store/conversation.js +1 -20
- package/lib/module/store/conversation.js.map +1 -1
- package/lib/module/translation/resources/i18n.js +10 -1
- package/lib/module/translation/resources/i18n.js.map +1 -1
- package/lib/module/types/attachment.js +2 -0
- package/lib/module/types/chat.js +26 -0
- package/lib/module/types/chat.js.map +1 -1
- package/lib/module/types/sticker.js +2 -0
- package/lib/module/types/sticker.js.map +1 -0
- package/lib/module/utils/conversation.js +1 -24
- package/lib/module/utils/conversation.js.map +1 -1
- package/lib/module/utils/messageUtils.js +2 -0
- package/lib/module/utils/messageUtils.js.map +1 -1
- package/lib/module/utils/url.js +11 -0
- package/lib/module/utils/url.js.map +1 -1
- package/lib/typescript/src/assets/images/index.d.ts +1 -0
- package/lib/typescript/src/assets/images/index.d.ts.map +1 -1
- package/lib/typescript/src/components/MediaViewerModal.d.ts +3 -1
- package/lib/typescript/src/components/MediaViewerModal.d.ts.map +1 -1
- package/lib/typescript/src/components/MergedImageGrid.d.ts.map +1 -1
- package/lib/typescript/src/components/StickerPicker/StickerCategoryTabs.d.ts +9 -0
- package/lib/typescript/src/components/StickerPicker/StickerCategoryTabs.d.ts.map +1 -0
- package/lib/typescript/src/components/StickerPicker/StickerPickerPlaceholder.d.ts +2 -0
- package/lib/typescript/src/components/StickerPicker/StickerPickerPlaceholder.d.ts.map +1 -0
- package/lib/typescript/src/components/StickerPicker/StickerPickerRowItem.d.ts +8 -0
- package/lib/typescript/src/components/StickerPicker/StickerPickerRowItem.d.ts.map +1 -0
- package/lib/typescript/src/components/StickerPicker/StickerSectionList.d.ts +15 -0
- package/lib/typescript/src/components/StickerPicker/StickerSectionList.d.ts.map +1 -0
- package/lib/typescript/src/components/StickerPicker/constants.d.ts +11 -0
- package/lib/typescript/src/components/StickerPicker/constants.d.ts.map +1 -0
- package/lib/typescript/src/components/StickerPicker/index.d.ts +7 -0
- package/lib/typescript/src/components/StickerPicker/index.d.ts.map +1 -0
- package/lib/typescript/src/components/messages/stickerMessage/index.d.ts +3 -0
- package/lib/typescript/src/components/messages/stickerMessage/index.d.ts.map +1 -0
- package/lib/typescript/src/config/api-endpoints.d.ts +4 -0
- package/lib/typescript/src/config/api-endpoints.d.ts.map +1 -1
- package/lib/typescript/src/hooks/conversation/useGetConversationDetail.d.ts +2 -1
- package/lib/typescript/src/hooks/conversation/useGetConversationDetail.d.ts.map +1 -1
- package/lib/typescript/src/hooks/message/useSendMessage.d.ts +5 -3
- package/lib/typescript/src/hooks/message/useSendMessage.d.ts.map +1 -1
- package/lib/typescript/src/hooks/query-keys.d.ts +11 -0
- package/lib/typescript/src/hooks/query-keys.d.ts.map +1 -1
- package/lib/typescript/src/hooks/resourceCenter/useThreadResources.d.ts +21 -0
- package/lib/typescript/src/hooks/resourceCenter/useThreadResources.d.ts.map +1 -0
- package/lib/typescript/src/hooks/stickers/useFetchStickerByCategory.d.ts +3 -0
- package/lib/typescript/src/hooks/stickers/useFetchStickerByCategory.d.ts.map +1 -0
- package/lib/typescript/src/hooks/stickers/useStickerCategories.d.ts +3 -0
- package/lib/typescript/src/hooks/stickers/useStickerCategories.d.ts.map +1 -0
- package/lib/typescript/src/hooks/stickers/useStickerHistory.d.ts +3 -0
- package/lib/typescript/src/hooks/stickers/useStickerHistory.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useChatCompose.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useImageAttachment.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useMediaViewer.d.ts +5 -1
- package/lib/typescript/src/hooks/useMediaViewer.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useSendAttachment.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/screens/MediaView/index.d.ts.map +1 -1
- package/lib/typescript/src/screens/ResourceCenter/FileResourceRow.d.ts +7 -0
- package/lib/typescript/src/screens/ResourceCenter/FileResourceRow.d.ts.map +1 -0
- package/lib/typescript/src/screens/ResourceCenter/GridRenderer.d.ts +14 -0
- package/lib/typescript/src/screens/ResourceCenter/GridRenderer.d.ts.map +1 -0
- package/lib/typescript/src/screens/ResourceCenter/GridThumbnail.d.ts +10 -0
- package/lib/typescript/src/screens/ResourceCenter/GridThumbnail.d.ts.map +1 -0
- package/lib/typescript/src/screens/ResourceCenter/LinkResourceRow.d.ts +8 -0
- package/lib/typescript/src/screens/ResourceCenter/LinkResourceRow.d.ts.map +1 -0
- package/lib/typescript/src/screens/ResourceCenter/ListRenderer.d.ts +16 -0
- package/lib/typescript/src/screens/ResourceCenter/ListRenderer.d.ts.map +1 -0
- package/lib/typescript/src/screens/ResourceCenter/ResourceCenterTabBar.d.ts +3 -0
- package/lib/typescript/src/screens/ResourceCenter/ResourceCenterTabBar.d.ts.map +1 -0
- package/lib/typescript/src/screens/ResourceCenter/ResourceCenterTabScene.d.ts +16 -0
- package/lib/typescript/src/screens/ResourceCenter/ResourceCenterTabScene.d.ts.map +1 -0
- package/lib/typescript/src/screens/ResourceCenter/ResourceEmptyState.d.ts +6 -0
- package/lib/typescript/src/screens/ResourceCenter/ResourceEmptyState.d.ts.map +1 -0
- package/lib/typescript/src/screens/ResourceCenter/index.d.ts +4 -0
- package/lib/typescript/src/screens/ResourceCenter/index.d.ts.map +1 -0
- package/lib/typescript/src/screens/ResourceCenter/types.d.ts +28 -0
- package/lib/typescript/src/screens/ResourceCenter/types.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts +2 -2
- package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/hooks/useChatComposerAnimation.d.ts +2 -1
- package/lib/typescript/src/screens/chat-detail/hooks/useChatComposerAnimation.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/hooks/useChatComposerState.d.ts +3 -0
- package/lib/typescript/src/screens/chat-detail/hooks/useChatComposerState.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts.map +1 -1
- package/lib/typescript/src/services/attachmentOrchestrator.d.ts.map +1 -1
- package/lib/typescript/src/services/endpoints.d.ts +4 -1
- package/lib/typescript/src/services/endpoints.d.ts.map +1 -1
- package/lib/typescript/src/services/resourceCenter.d.ts +9 -0
- package/lib/typescript/src/services/resourceCenter.d.ts.map +1 -0
- package/lib/typescript/src/store/conversation.d.ts +0 -3
- package/lib/typescript/src/store/conversation.d.ts.map +1 -1
- package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -1
- package/lib/typescript/src/types/attachment.d.ts +2 -0
- package/lib/typescript/src/types/attachment.d.ts.map +1 -1
- package/lib/typescript/src/types/chat.d.ts +66 -15
- package/lib/typescript/src/types/chat.d.ts.map +1 -1
- package/lib/typescript/src/types/sticker.d.ts +21 -0
- package/lib/typescript/src/types/sticker.d.ts.map +1 -0
- package/lib/typescript/src/utils/conversation.d.ts +1 -2
- package/lib/typescript/src/utils/conversation.d.ts.map +1 -1
- package/lib/typescript/src/utils/messageUtils.d.ts.map +1 -1
- package/lib/typescript/src/utils/url.d.ts +7 -0
- package/lib/typescript/src/utils/url.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/assets/images/index.ts +1 -0
- package/src/assets/images/resource-center-empty-box.png +0 -0
- package/src/components/MediaViewerModal.tsx +8 -3
- package/src/components/MergedImageGrid.tsx +1 -0
- package/src/components/StickerPicker/StickerCategoryTabs.tsx +103 -0
- package/src/components/StickerPicker/StickerPickerPlaceholder.tsx +16 -0
- package/src/components/StickerPicker/StickerPickerRowItem.tsx +43 -0
- package/src/components/StickerPicker/StickerSectionList.tsx +141 -0
- package/src/components/StickerPicker/constants.ts +20 -0
- package/src/components/StickerPicker/index.tsx +166 -0
- package/src/components/messages/stickerMessage/index.tsx +26 -0
- package/src/config/api-endpoints.ts +8 -0
- package/src/core/notification/pushToken.ts +7 -7
- package/src/hooks/conversation/useGetConversationDetail.ts +20 -1
- package/src/hooks/flows/useGetConversationFlowMainMenu.ts +1 -1
- package/src/hooks/message/useSendMessage.ts +49 -27
- package/src/hooks/query-keys.ts +16 -0
- package/src/hooks/resourceCenter/useThreadResources.ts +124 -0
- package/src/hooks/stickers/useFetchStickerByCategory.ts +15 -0
- package/src/hooks/stickers/useStickerCategories.ts +17 -0
- package/src/hooks/stickers/useStickerHistory.ts +19 -0
- package/src/hooks/useChatCompose.ts +0 -19
- package/src/hooks/useImageAttachment.ts +1 -2
- package/src/hooks/useMediaViewer.ts +21 -7
- package/src/hooks/useSendAttachment.ts +4 -1
- package/src/index.tsx +2 -0
- package/src/screens/MediaView/index.tsx +117 -54
- package/src/screens/ResourceCenter/FileResourceRow.tsx +99 -0
- package/src/screens/ResourceCenter/GridRenderer.tsx +104 -0
- package/src/screens/ResourceCenter/GridThumbnail.tsx +98 -0
- package/src/screens/ResourceCenter/LinkResourceRow.tsx +96 -0
- package/src/screens/ResourceCenter/ListRenderer.tsx +81 -0
- package/src/screens/ResourceCenter/ResourceCenterTabBar.tsx +89 -0
- package/src/screens/ResourceCenter/ResourceCenterTabScene.tsx +142 -0
- package/src/screens/ResourceCenter/ResourceEmptyState.tsx +39 -0
- package/src/screens/ResourceCenter/index.tsx +129 -0
- package/src/screens/ResourceCenter/types.ts +56 -0
- package/src/screens/chat-detail/ChatComposer.tsx +106 -19
- package/src/screens/chat-detail/ChatDetail.tsx +5 -19
- package/src/screens/chat-detail/hooks/useChatComposerAnimation.ts +21 -5
- package/src/screens/chat-detail/hooks/useChatComposerState.ts +7 -0
- package/src/screens/chat-detail/legend/LegendChatMessage.tsx +4 -5
- package/src/services/attachmentOrchestrator.ts +16 -3
- package/src/services/endpoints.ts +6 -1
- package/src/services/resourceCenter.ts +137 -0
- package/src/store/conversation.ts +0 -24
- package/src/translation/resources/i18n.ts +10 -0
- package/src/types/attachment.ts +3 -0
- package/src/types/chat.ts +80 -15
- package/src/types/sticker.ts +23 -0
- package/src/utils/conversation.ts +0 -28
- package/src/utils/messageUtils.ts +3 -0
- package/src/utils/url.ts +11 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { DThreadResourceContentType } from '../../types/chat';
|
|
2
|
+
|
|
3
|
+
export interface ResourceCenterScreenProps {
|
|
4
|
+
conversationId: string;
|
|
5
|
+
/**
|
|
6
|
+
* Fallback groupID/userID for the race-condition window where `conversationId`
|
|
7
|
+
* isn't in the store yet (fresh deep-link / cold nav). Mirrors the route-param
|
|
8
|
+
* fallback pattern host apps already use elsewhere (e.g. `useSendMessage`).
|
|
9
|
+
*/
|
|
10
|
+
fallbackGroupID?: string;
|
|
11
|
+
fallbackUserID?: string;
|
|
12
|
+
initialTab?: DThreadResourceContentType;
|
|
13
|
+
onBack?: () => void;
|
|
14
|
+
/**
|
|
15
|
+
* Host-provided link-open handler — mirrors `ChatDetailProps.onPressUrl`. This library does
|
|
16
|
+
* NOT implement in-app-browser/deeplink/external-app logic itself (traced: `ChatDetail.tsx`
|
|
17
|
+
* only normalizes the URL and forwards to this same prop) — the host app supplies it.
|
|
18
|
+
*/
|
|
19
|
+
onPressUrl?: (url: string) => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type ResourceCenterLayout = 'grid' | 'list';
|
|
23
|
+
|
|
24
|
+
export interface ResourceTabConfig {
|
|
25
|
+
contentType: DThreadResourceContentType;
|
|
26
|
+
labelKey: string;
|
|
27
|
+
emptyKey: string;
|
|
28
|
+
layout: ResourceCenterLayout;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const RESOURCE_TABS: ResourceTabConfig[] = [
|
|
32
|
+
{
|
|
33
|
+
contentType: DThreadResourceContentType.IMAGE,
|
|
34
|
+
labelKey: 'chat:resource_center_tab_image',
|
|
35
|
+
emptyKey: 'chat:resource_center_empty_generic',
|
|
36
|
+
layout: 'grid',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
contentType: DThreadResourceContentType.VIDEO,
|
|
40
|
+
labelKey: 'chat:resource_center_tab_video',
|
|
41
|
+
emptyKey: 'chat:resource_center_empty_generic',
|
|
42
|
+
layout: 'grid',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
contentType: DThreadResourceContentType.FILE,
|
|
46
|
+
labelKey: 'chat:resource_center_tab_file',
|
|
47
|
+
emptyKey: 'chat:resource_center_empty_generic',
|
|
48
|
+
layout: 'list',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
contentType: DThreadResourceContentType.URL_TEXT,
|
|
52
|
+
labelKey: 'chat:resource_center_tab_link',
|
|
53
|
+
emptyKey: 'chat:resource_center_empty_link',
|
|
54
|
+
layout: 'list',
|
|
55
|
+
},
|
|
56
|
+
];
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
type RefObject,
|
|
9
9
|
} from 'react';
|
|
10
10
|
import {
|
|
11
|
+
Keyboard,
|
|
11
12
|
Platform,
|
|
12
13
|
StyleSheet,
|
|
13
14
|
type LayoutChangeEvent,
|
|
@@ -15,7 +16,10 @@ import {
|
|
|
15
16
|
} from 'react-native';
|
|
16
17
|
import { debounce } from 'lodash';
|
|
17
18
|
import Reanimated from 'react-native-reanimated';
|
|
18
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
KeyboardStickyView,
|
|
21
|
+
useKeyboardState,
|
|
22
|
+
} from 'react-native-keyboard-controller';
|
|
19
23
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
20
24
|
import { KContainer, KColors, KImage, KButton, KLabel } from '@droppii/libs';
|
|
21
25
|
|
|
@@ -30,12 +34,15 @@ import { ChatAttachmentPanel } from './ChatAttachmentPanel';
|
|
|
30
34
|
import { ChatLinkPreview } from './ChatLinkPreview';
|
|
31
35
|
import { ChatMessageInput } from './components/ChatMessageInput';
|
|
32
36
|
import { AttachmentPreview } from '../../components/AttachmentPreview';
|
|
37
|
+
import { StickerPicker } from '../../components/StickerPicker';
|
|
38
|
+
import type { StickerItem } from '../../types/sticker';
|
|
33
39
|
|
|
34
40
|
import {
|
|
35
41
|
DEFAULT_QUICK_ACTIONS,
|
|
36
42
|
getAttachmentPanelHeight,
|
|
37
43
|
DEFAULT_PANNEL_ACTIONS,
|
|
38
44
|
} from './constants';
|
|
45
|
+
import { STICKER_PICKER_HEIGHT } from '../../components/StickerPicker/constants';
|
|
39
46
|
import { DChatPannelActionItemType, type DChatPannelAction } from './types';
|
|
40
47
|
import { useChatComposerState } from './hooks/useChatComposerState';
|
|
41
48
|
import { useChatComposerAnimation } from './hooks/useChatComposerAnimation';
|
|
@@ -47,11 +54,12 @@ import { useAttachmentMapping } from './hooks/useAttachmentMapping';
|
|
|
47
54
|
import type { AttachmentType } from '../../types/attachment';
|
|
48
55
|
import { useChatCompose } from '../../hooks/useChatCompose';
|
|
49
56
|
import { ChatCurrentQuotedMessage } from './ChatCurrentQuotedMessage';
|
|
57
|
+
import { useSendMessage } from '../../hooks/message/useSendMessage';
|
|
50
58
|
|
|
51
59
|
interface ChatComposerInternalProps {
|
|
52
60
|
isEmptyMessage?: boolean;
|
|
53
61
|
pannelActions?: DChatPannelAction[];
|
|
54
|
-
|
|
62
|
+
isComposeDisabled?: boolean;
|
|
55
63
|
composerRef?: RefObject<View | null>;
|
|
56
64
|
onComposerLayout?: (event: LayoutChangeEvent) => void;
|
|
57
65
|
}
|
|
@@ -60,18 +68,22 @@ export const ChatComposer = memo(
|
|
|
60
68
|
({
|
|
61
69
|
isEmptyMessage = false,
|
|
62
70
|
pannelActions,
|
|
63
|
-
|
|
71
|
+
isComposeDisabled = false,
|
|
64
72
|
composerRef,
|
|
65
73
|
onComposerLayout,
|
|
66
74
|
}: ChatComposerInternalProps) => {
|
|
67
75
|
const insets = useSafeAreaInsets();
|
|
76
|
+
const isDisableCompose = isComposeDisabled;
|
|
77
|
+
const isKeyboardVisible = useKeyboardState((state) => state.isVisible);
|
|
68
78
|
|
|
69
79
|
const { isBotConversation, conversationFlow } = useChatDetailContext();
|
|
70
80
|
const { mutate: runFlow, isPending } = useRunFlow();
|
|
71
|
-
|
|
81
|
+
const { sendMessage } = useSendMessage();
|
|
72
82
|
const compose = useChatCompose();
|
|
73
83
|
|
|
74
84
|
const {
|
|
85
|
+
activePanelType,
|
|
86
|
+
setActivePanelType,
|
|
75
87
|
isAttachmentOpen,
|
|
76
88
|
setIsAttachmentOpen,
|
|
77
89
|
isInputFocused,
|
|
@@ -81,16 +93,25 @@ export const ChatComposer = memo(
|
|
|
81
93
|
inputRef,
|
|
82
94
|
} = useChatComposerState(compose.text);
|
|
83
95
|
|
|
84
|
-
const
|
|
96
|
+
const attachmentPanelHeight = useMemo(
|
|
85
97
|
() => getAttachmentPanelHeight(DEFAULT_PANNEL_ACTIONS.length),
|
|
86
98
|
[]
|
|
87
99
|
);
|
|
88
100
|
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
101
|
+
const panelHeight =
|
|
102
|
+
activePanelType === 'sticker'
|
|
103
|
+
? STICKER_PICKER_HEIGHT
|
|
104
|
+
: attachmentPanelHeight;
|
|
105
|
+
|
|
106
|
+
const {
|
|
107
|
+
accessorySlotStyle,
|
|
108
|
+
toggleAttachmentVisibility,
|
|
109
|
+
openWithHeight,
|
|
110
|
+
closeAttachment,
|
|
111
|
+
} = useChatComposerAnimation({
|
|
112
|
+
panelHeight,
|
|
113
|
+
onAttachmentOpenChange: setIsAttachmentOpen,
|
|
114
|
+
});
|
|
94
115
|
|
|
95
116
|
const handleSend = useCallback(async () => {
|
|
96
117
|
if (compose.isUploading || !compose.canSend) return;
|
|
@@ -103,8 +124,58 @@ export const ChatComposer = memo(
|
|
|
103
124
|
|
|
104
125
|
const handleInputFocus = useCallback(() => {
|
|
105
126
|
setIsInputFocused(true);
|
|
106
|
-
if (isAttachmentOpen)
|
|
107
|
-
|
|
127
|
+
if (isAttachmentOpen) {
|
|
128
|
+
// Use immediate close (no animation) when sticker panel is open to avoid
|
|
129
|
+
// layout conflict between the panel closing and keyboard appearing.
|
|
130
|
+
// Attachment panel is small enough that animation is fine (speedUp=true).
|
|
131
|
+
const immediate = activePanelType === 'sticker';
|
|
132
|
+
closeAttachment(!immediate, immediate);
|
|
133
|
+
setActivePanelType(null);
|
|
134
|
+
}
|
|
135
|
+
}, [
|
|
136
|
+
closeAttachment,
|
|
137
|
+
isAttachmentOpen,
|
|
138
|
+
activePanelType,
|
|
139
|
+
setIsInputFocused,
|
|
140
|
+
setActivePanelType,
|
|
141
|
+
]);
|
|
142
|
+
|
|
143
|
+
const handleToggleAttachment = useCallback(() => {
|
|
144
|
+
if (activePanelType === 'sticker') {
|
|
145
|
+
// switch from sticker to attachment
|
|
146
|
+
setActivePanelType('attachment');
|
|
147
|
+
openWithHeight(attachmentPanelHeight);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
setActivePanelType(isAttachmentOpen ? null : 'attachment');
|
|
151
|
+
toggleAttachmentVisibility();
|
|
152
|
+
}, [
|
|
153
|
+
activePanelType,
|
|
154
|
+
isAttachmentOpen,
|
|
155
|
+
attachmentPanelHeight,
|
|
156
|
+
openWithHeight,
|
|
157
|
+
toggleAttachmentVisibility,
|
|
158
|
+
setActivePanelType,
|
|
159
|
+
]);
|
|
160
|
+
|
|
161
|
+
const handleToggleSticker = useCallback(() => {
|
|
162
|
+
if (activePanelType === 'sticker') {
|
|
163
|
+
closeAttachment();
|
|
164
|
+
setActivePanelType(null);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (isKeyboardVisible) {
|
|
168
|
+
Keyboard.dismiss();
|
|
169
|
+
}
|
|
170
|
+
setActivePanelType('sticker');
|
|
171
|
+
openWithHeight(STICKER_PICKER_HEIGHT);
|
|
172
|
+
}, [
|
|
173
|
+
activePanelType,
|
|
174
|
+
isKeyboardVisible,
|
|
175
|
+
closeAttachment,
|
|
176
|
+
openWithHeight,
|
|
177
|
+
setActivePanelType,
|
|
178
|
+
]);
|
|
108
179
|
|
|
109
180
|
const handleInputBlur = useCallback(() => {
|
|
110
181
|
setIsInputFocused(false);
|
|
@@ -181,6 +252,13 @@ export const ChatComposer = memo(
|
|
|
181
252
|
[insets.bottom, isAttachmentOpen]
|
|
182
253
|
);
|
|
183
254
|
|
|
255
|
+
const handleStickerSelect = useCallback(
|
|
256
|
+
(sticker: StickerItem) => {
|
|
257
|
+
sendMessage({ sticker });
|
|
258
|
+
},
|
|
259
|
+
[sendMessage]
|
|
260
|
+
);
|
|
261
|
+
|
|
184
262
|
const attachments = useAttachmentMapping({
|
|
185
263
|
selectedImages: compose.selectedImages,
|
|
186
264
|
selectedFiles: compose.selectedFiles,
|
|
@@ -211,7 +289,7 @@ export const ChatComposer = memo(
|
|
|
211
289
|
/>
|
|
212
290
|
</KContainer.View>
|
|
213
291
|
<KLabel.Text typo="TextXsNormal">
|
|
214
|
-
|
|
292
|
+
{trans('chat:compose_disabled_seller_inactive')}
|
|
215
293
|
</KLabel.Text>
|
|
216
294
|
</KContainer.View>
|
|
217
295
|
);
|
|
@@ -265,15 +343,19 @@ export const ChatComposer = memo(
|
|
|
265
343
|
style={inputRowStyle}
|
|
266
344
|
>
|
|
267
345
|
<KContainer.Touchable
|
|
268
|
-
onPress={
|
|
346
|
+
onPress={handleToggleAttachment}
|
|
269
347
|
center
|
|
270
348
|
br="round"
|
|
271
349
|
>
|
|
272
350
|
<KImage.VectorIcons
|
|
273
|
-
name={
|
|
351
|
+
name={
|
|
352
|
+
activePanelType === 'attachment'
|
|
353
|
+
? 'close-circle-o'
|
|
354
|
+
: 'plus-circle-o'
|
|
355
|
+
}
|
|
274
356
|
size={24}
|
|
275
357
|
color={
|
|
276
|
-
|
|
358
|
+
activePanelType === 'attachment'
|
|
277
359
|
? KColors.palette.primary.w400
|
|
278
360
|
: KColors.palette.gray.w600
|
|
279
361
|
}
|
|
@@ -290,6 +372,7 @@ export const ChatComposer = memo(
|
|
|
290
372
|
isInputFocused={isInputFocused}
|
|
291
373
|
isMultilineInput={isMultilineInput}
|
|
292
374
|
inputRef={inputRef}
|
|
375
|
+
onEmojiPress={handleToggleSticker}
|
|
293
376
|
/>
|
|
294
377
|
|
|
295
378
|
<KContainer.Touchable
|
|
@@ -308,8 +391,7 @@ export const ChatComposer = memo(
|
|
|
308
391
|
opacity={compose.isUploading ? 0.6 : 1}
|
|
309
392
|
>
|
|
310
393
|
<KImage.VectorIcons
|
|
311
|
-
name="send"
|
|
312
|
-
provider="MaterialCommunityIcons"
|
|
394
|
+
name="send-b"
|
|
313
395
|
size={20}
|
|
314
396
|
color={KColors.white}
|
|
315
397
|
/>
|
|
@@ -335,7 +417,12 @@ export const ChatComposer = memo(
|
|
|
335
417
|
style={[styles.accessorySlot, accessorySlotStyle]}
|
|
336
418
|
pointerEvents={isAttachmentOpen ? 'auto' : 'none'}
|
|
337
419
|
>
|
|
338
|
-
|
|
420
|
+
{activePanelType === 'attachment' && (
|
|
421
|
+
<ChatAttachmentPanel actions={resolvedPannelActons} />
|
|
422
|
+
)}
|
|
423
|
+
{activePanelType === 'sticker' && (
|
|
424
|
+
<StickerPicker onSelectSticker={handleStickerSelect} />
|
|
425
|
+
)}
|
|
339
426
|
</Reanimated.View>
|
|
340
427
|
</KContainer.VisibleView>
|
|
341
428
|
</>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { memo, useCallback, useEffect,
|
|
1
|
+
import { memo, useCallback, useEffect, useRef } from 'react';
|
|
2
2
|
import { DeviceEventEmitter } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import { useKeyboardScrollToEnd } from '@legendapp/list/keyboard';
|
|
@@ -26,15 +26,12 @@ import type { ChatDetailProps } from './types';
|
|
|
26
26
|
import { normalizeUrl } from '../../utils/url';
|
|
27
27
|
import { MessageToolboxOverlay } from './messageToolbox/MessageToolboxOverlay';
|
|
28
28
|
import type { MeasuredRect, MessageToolboxRef } from './messageToolbox/types';
|
|
29
|
-
import { DBotStatusValue } from '../../types/chat';
|
|
30
29
|
import {
|
|
31
30
|
useGetConversationDetail,
|
|
32
|
-
|
|
31
|
+
useGetConversationTarget,
|
|
33
32
|
} from '../../hooks/conversation/useGetConversationDetail';
|
|
34
|
-
import { PeerType } from '@droppii/openim-rn-client-sdk';
|
|
35
33
|
import type { MessageItem } from '@droppii/openim-rn-client-sdk';
|
|
36
34
|
import { useMessages } from '../../hooks/message/useMessages';
|
|
37
|
-
import { getBotId } from '../../utils/conversation';
|
|
38
35
|
import { ChatEvent } from '../../types/events';
|
|
39
36
|
import { useSetIsShowFLashMessage } from '../../context/global';
|
|
40
37
|
|
|
@@ -69,19 +66,8 @@ const ChatDetail = memo(
|
|
|
69
66
|
|
|
70
67
|
const conversation = useConversation(conversationId);
|
|
71
68
|
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
if (targetBotId) {
|
|
75
|
-
return targetBotId;
|
|
76
|
-
}
|
|
77
|
-
return getBotId(conversation);
|
|
78
|
-
}
|
|
79
|
-
return undefined;
|
|
80
|
-
}, [conversation, targetBotId]);
|
|
81
|
-
|
|
82
|
-
const { data: botInfo } = useGetStatusConversationBot(botId);
|
|
83
|
-
|
|
84
|
-
const { status: botStatus } = botInfo || {};
|
|
69
|
+
const { data: target } = useGetConversationTarget(conversationId);
|
|
70
|
+
const isComposeDisabled = !!target && !target.isActive;
|
|
85
71
|
|
|
86
72
|
const handleBackDefault = useCallback(() => {
|
|
87
73
|
navigation.goBack();
|
|
@@ -205,7 +191,7 @@ const ChatDetail = memo(
|
|
|
205
191
|
<ChatComposer
|
|
206
192
|
isEmptyMessage={messages?.length === 0}
|
|
207
193
|
pannelActions={pannelActions}
|
|
208
|
-
|
|
194
|
+
isComposeDisabled={isComposeDisabled}
|
|
209
195
|
/>
|
|
210
196
|
|
|
211
197
|
<MediaViewerModal ref={mediaViewerRef} />
|
|
@@ -19,7 +19,8 @@ interface UseChatComposerAnimationReturn {
|
|
|
19
19
|
accessorySlotStyle: any;
|
|
20
20
|
toggleAttachmentVisibility: () => void;
|
|
21
21
|
openAttachment: () => void;
|
|
22
|
-
|
|
22
|
+
openWithHeight: (height: number) => void;
|
|
23
|
+
closeAttachment: (speedUp?: boolean, immediate?: boolean) => void;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export const useChatComposerAnimation = ({
|
|
@@ -43,10 +44,14 @@ export const useChatComposerAnimation = ({
|
|
|
43
44
|
}, [attachmentHeightSV, panelHeight, onAttachmentOpenChange]);
|
|
44
45
|
|
|
45
46
|
const closeAttachment = useCallback(
|
|
46
|
-
(speedUp = false) => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
47
|
+
(speedUp = false, immediate = false) => {
|
|
48
|
+
if (immediate) {
|
|
49
|
+
attachmentHeightSV.value = 0;
|
|
50
|
+
} else {
|
|
51
|
+
attachmentHeightSV.value = withTiming(0, {
|
|
52
|
+
duration: ACCESSORY_ANIMATION_MS / (speedUp ? 2 : 1),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
50
55
|
onAttachmentOpenChange(false);
|
|
51
56
|
},
|
|
52
57
|
[attachmentHeightSV, onAttachmentOpenChange]
|
|
@@ -56,6 +61,16 @@ export const useChatComposerAnimation = ({
|
|
|
56
61
|
return { height: attachmentHeightSV.value };
|
|
57
62
|
});
|
|
58
63
|
|
|
64
|
+
const openWithHeight = useCallback(
|
|
65
|
+
(height: number) => {
|
|
66
|
+
attachmentHeightSV.value = withTiming(height, {
|
|
67
|
+
duration: ACCESSORY_ANIMATION_MS,
|
|
68
|
+
});
|
|
69
|
+
onAttachmentOpenChange(true);
|
|
70
|
+
},
|
|
71
|
+
[attachmentHeightSV, onAttachmentOpenChange]
|
|
72
|
+
);
|
|
73
|
+
|
|
59
74
|
const toggleAttachmentVisibility = useCallback(() => {
|
|
60
75
|
// Use animated value as source of truth, not stale state
|
|
61
76
|
const isOpen = attachmentHeightSV.value > 0;
|
|
@@ -84,6 +99,7 @@ export const useChatComposerAnimation = ({
|
|
|
84
99
|
accessorySlotStyle,
|
|
85
100
|
toggleAttachmentVisibility,
|
|
86
101
|
openAttachment,
|
|
102
|
+
openWithHeight,
|
|
87
103
|
closeAttachment,
|
|
88
104
|
};
|
|
89
105
|
};
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { useState, useMemo, useRef } from 'react';
|
|
2
2
|
|
|
3
|
+
export type ActivePanelType = 'attachment' | 'sticker' | null;
|
|
4
|
+
|
|
3
5
|
interface UseChatComposerStateReturn {
|
|
6
|
+
activePanelType: ActivePanelType;
|
|
7
|
+
setActivePanelType: (type: ActivePanelType) => void;
|
|
4
8
|
isAttachmentOpen: boolean;
|
|
5
9
|
setIsAttachmentOpen: (open: boolean) => void;
|
|
6
10
|
isInputFocused: boolean;
|
|
@@ -14,6 +18,7 @@ interface UseChatComposerStateReturn {
|
|
|
14
18
|
export const useChatComposerState = (
|
|
15
19
|
value: string
|
|
16
20
|
): UseChatComposerStateReturn => {
|
|
21
|
+
const [activePanelType, setActivePanelType] = useState<ActivePanelType>(null);
|
|
17
22
|
const [isAttachmentOpen, setIsAttachmentOpen] = useState(false);
|
|
18
23
|
const [isInputFocused, setIsInputFocused] = useState(false);
|
|
19
24
|
const [isInputWrapped, setIsInputWrapped] = useState(false);
|
|
@@ -28,6 +33,8 @@ export const useChatComposerState = (
|
|
|
28
33
|
}, [isInputWrapped, value]);
|
|
29
34
|
|
|
30
35
|
return {
|
|
36
|
+
activePanelType,
|
|
37
|
+
setActivePanelType,
|
|
31
38
|
isAttachmentOpen,
|
|
32
39
|
setIsAttachmentOpen,
|
|
33
40
|
isInputFocused,
|
|
@@ -18,6 +18,7 @@ import { MessageStatusIndicator } from '../../../components/messages/MessageStat
|
|
|
18
18
|
import type { BaseLegendMessageProps } from '../../../components/messages/types';
|
|
19
19
|
import { messageStyles } from './messageTypes';
|
|
20
20
|
import QuotedMessage from '../../../components/messages/quotedMessage';
|
|
21
|
+
import { StickerMessage } from '../../../components/messages/stickerMessage';
|
|
21
22
|
|
|
22
23
|
type MessageProps = BaseLegendMessageProps & {
|
|
23
24
|
onPressUrl?: (url: string) => void;
|
|
@@ -36,9 +37,6 @@ interface LegendChatMessageProps {
|
|
|
36
37
|
customMessageItemFactory?: (m: MessageItem) => ReactNode;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
// OpenIM message types (not all exposed as MessageType enum)
|
|
40
|
-
const MERGED_MESSAGE_TYPE = 107; // MergerMessage - merged/grouped messages
|
|
41
|
-
|
|
42
40
|
const messageComponentMap: Partial<
|
|
43
41
|
Record<MessageType, ComponentType<MessageProps>>
|
|
44
42
|
> = {
|
|
@@ -49,14 +47,15 @@ const messageComponentMap: Partial<
|
|
|
49
47
|
[MessageType.PictureMessage]: ImageMessage,
|
|
50
48
|
[MessageType.VideoMessage]: VideoMessage,
|
|
51
49
|
[MessageType.FileMessage]: FileMessage,
|
|
52
|
-
[
|
|
50
|
+
[MessageType.MergeMessage]: MergedMessage,
|
|
53
51
|
[MessageType.RevokeMessage]: RevokedMessage,
|
|
52
|
+
[MessageType.StickerMessage]: StickerMessage,
|
|
54
53
|
};
|
|
55
54
|
|
|
56
55
|
const MEDIA_TYPES: number[] = [
|
|
57
56
|
MessageType.PictureMessage,
|
|
58
57
|
MessageType.VideoMessage,
|
|
59
|
-
|
|
58
|
+
MessageType.MergeMessage,
|
|
60
59
|
];
|
|
61
60
|
|
|
62
61
|
export const LegendChatMessage = memo(
|
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
AttachmentSendResult,
|
|
12
12
|
} from '../types/attachment';
|
|
13
13
|
import { getAttachmentHandler } from '../types/attachmentHandler';
|
|
14
|
+
import type { DChatApplicationType, IMessageItemEx } from '../types/chat';
|
|
14
15
|
import { useConversationStore } from '../store';
|
|
15
16
|
import { useMessageStore } from '../store/message';
|
|
16
17
|
import {
|
|
@@ -97,6 +98,7 @@ export namespace AttachmentOrchestratorService {
|
|
|
97
98
|
try {
|
|
98
99
|
const mergedMsgId = await sendMergedMessage(
|
|
99
100
|
payloads.map((p) => p.payload),
|
|
101
|
+
options.applicationType,
|
|
100
102
|
options.includeCaption
|
|
101
103
|
);
|
|
102
104
|
console.log('[AttachmentOrchestrator] Messages sent via merger:', {
|
|
@@ -122,6 +124,7 @@ export namespace AttachmentOrchestratorService {
|
|
|
122
124
|
|
|
123
125
|
async function sendMergedMessage(
|
|
124
126
|
payloads: unknown[],
|
|
127
|
+
applicationType?: DChatApplicationType,
|
|
125
128
|
_caption?: string
|
|
126
129
|
): Promise<string> {
|
|
127
130
|
const currentConversation = useConversationStore
|
|
@@ -274,8 +277,18 @@ export namespace AttachmentOrchestratorService {
|
|
|
274
277
|
}
|
|
275
278
|
);
|
|
276
279
|
|
|
277
|
-
const
|
|
278
|
-
const
|
|
280
|
+
const { currentUserId, currentGroupId } = useConversationStore.getState();
|
|
281
|
+
const recvID = currentConversation.userID || currentUserId || '';
|
|
282
|
+
const groupID = currentConversation.groupID || currentGroupId || '';
|
|
283
|
+
|
|
284
|
+
// Attachment messages otherwise skip `ex`, unlike text messages
|
|
285
|
+
// (buildSendParams), leaving applicationType context missing.
|
|
286
|
+
if (applicationType) {
|
|
287
|
+
const extendMessageInfo: IMessageItemEx = { applicationType };
|
|
288
|
+
for (let i = 0; i < allMessages.length; i++) {
|
|
289
|
+
(allMessages[i] as MessageItem).ex = JSON.stringify(extendMessageInfo);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
279
292
|
|
|
280
293
|
// Push all messages as "Sending" immediately so the user sees every
|
|
281
294
|
// bubble right away, instead of waiting for each network call in turn.
|
|
@@ -293,7 +306,7 @@ export namespace AttachmentOrchestratorService {
|
|
|
293
306
|
recvID,
|
|
294
307
|
groupID,
|
|
295
308
|
message,
|
|
296
|
-
offlinePushInfo: generateOfflinePushInfo(message
|
|
309
|
+
offlinePushInfo: generateOfflinePushInfo(message),
|
|
297
310
|
});
|
|
298
311
|
|
|
299
312
|
if (result) {
|
|
@@ -30,7 +30,12 @@ export const ENDPOINTS = {
|
|
|
30
30
|
`${getApiEndpoint('conversation')}/${id}/bot-flows/main-menu`,
|
|
31
31
|
getConversationStatusByBotId: (botId: string) =>
|
|
32
32
|
`${getApiEndpoint('bot')}/${botId}`,
|
|
33
|
-
|
|
33
|
+
getConversationTarget: (conversationId: string) =>
|
|
34
|
+
`${getApiEndpoint('conversation')}/${conversationId}/target`,
|
|
35
|
+
stickerCategories: () => getApiEndpoint('stickerCategories'),
|
|
36
|
+
stickersByCategoryId: (id: string) =>
|
|
37
|
+
`${getApiEndpoint('stickersByCategory')}/${id}/stickers`,
|
|
38
|
+
stickerHistory: () => getApiEndpoint('stickerHistory'),
|
|
34
39
|
},
|
|
35
40
|
uploaderService: {
|
|
36
41
|
get uploadImage() {
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { apiInstance } from './apis';
|
|
2
|
+
import { getApiEndpoint } from '../config/api-endpoints';
|
|
3
|
+
import { extractDomain } from '../utils/url';
|
|
4
|
+
import type { BaseResponse } from '../types/auth';
|
|
5
|
+
import {
|
|
6
|
+
DThreadResourceContentType,
|
|
7
|
+
type DThreadResource,
|
|
8
|
+
type DThreadResourceListResponse,
|
|
9
|
+
type DThreadResourceRawRow,
|
|
10
|
+
type DThreadResourceRequest,
|
|
11
|
+
} from '../types/chat';
|
|
12
|
+
|
|
13
|
+
interface RawPicture {
|
|
14
|
+
url?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface RawPictureContent {
|
|
18
|
+
bigPicture?: RawPicture;
|
|
19
|
+
snapshotPicture?: RawPicture;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface RawUrlTextEx {
|
|
23
|
+
urlMetadata?: {
|
|
24
|
+
title?: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
image?: string;
|
|
27
|
+
url?: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Confirmed real: `ex.urlMetadata` is sometimes absent entirely (link preview never
|
|
32
|
+
// resolved by BE) — `content.urls[0]` (the raw URL from the message text) is the
|
|
33
|
+
// fallback so the row still has something to display/open.
|
|
34
|
+
interface RawUrlTextContent {
|
|
35
|
+
urls?: string[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// FILE/VIDEO shapes verified 2026-07-26 against a real staging response (a 1-1
|
|
39
|
+
// thread, recvID = peer userID) — matched the originally-assumed OpenIM
|
|
40
|
+
// `fileElem`/`videoElem`-style field names exactly.
|
|
41
|
+
interface RawFileContent {
|
|
42
|
+
fileName?: string;
|
|
43
|
+
fileSize?: number;
|
|
44
|
+
fileType?: string;
|
|
45
|
+
sourceUrl?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface RawVideoContent {
|
|
49
|
+
videoUrl?: string;
|
|
50
|
+
snapshotUrl?: string;
|
|
51
|
+
duration?: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const safeJsonParse = <T>(raw: string | undefined): T | undefined => {
|
|
55
|
+
if (!raw) return undefined;
|
|
56
|
+
try {
|
|
57
|
+
return JSON.parse(raw) as T;
|
|
58
|
+
} catch {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const parseThreadResourceItem = (
|
|
64
|
+
row: DThreadResourceRawRow
|
|
65
|
+
): DThreadResource => {
|
|
66
|
+
const { chatLog } = row;
|
|
67
|
+
const contentType = chatLog.contentType as DThreadResourceContentType;
|
|
68
|
+
const base: DThreadResource = {
|
|
69
|
+
id: chatLog.clientMsgID,
|
|
70
|
+
contentType,
|
|
71
|
+
isRevoked: row.isRevoked,
|
|
72
|
+
createdAt: chatLog.sendTime,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
switch (contentType) {
|
|
76
|
+
case DThreadResourceContentType.IMAGE: {
|
|
77
|
+
const content = safeJsonParse<RawPictureContent>(chatLog.content);
|
|
78
|
+
return {
|
|
79
|
+
...base,
|
|
80
|
+
sourceUrl: content?.bigPicture?.url,
|
|
81
|
+
thumbnailUrl: content?.snapshotPicture?.url,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
case DThreadResourceContentType.VIDEO: {
|
|
85
|
+
const content = safeJsonParse<RawVideoContent>(chatLog.content);
|
|
86
|
+
return {
|
|
87
|
+
...base,
|
|
88
|
+
sourceUrl: content?.videoUrl,
|
|
89
|
+
thumbnailUrl: content?.snapshotUrl,
|
|
90
|
+
duration: content?.duration,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
case DThreadResourceContentType.FILE: {
|
|
94
|
+
const content = safeJsonParse<RawFileContent>(chatLog.content);
|
|
95
|
+
return {
|
|
96
|
+
...base,
|
|
97
|
+
sourceUrl: content?.sourceUrl,
|
|
98
|
+
fileName: content?.fileName,
|
|
99
|
+
fileSize: content?.fileSize,
|
|
100
|
+
mimeType: content?.fileType,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
case DThreadResourceContentType.URL_TEXT: {
|
|
104
|
+
const ex = safeJsonParse<RawUrlTextEx>(chatLog.ex);
|
|
105
|
+
const content = safeJsonParse<RawUrlTextContent>(chatLog.content);
|
|
106
|
+
const metadata = ex?.urlMetadata;
|
|
107
|
+
const linkUrl = metadata?.url ?? content?.urls?.[0];
|
|
108
|
+
return {
|
|
109
|
+
...base,
|
|
110
|
+
linkUrl,
|
|
111
|
+
linkTitle: metadata?.title,
|
|
112
|
+
linkDescription: metadata?.description,
|
|
113
|
+
linkImageUrl: metadata?.image,
|
|
114
|
+
linkDomain: linkUrl ? extractDomain(linkUrl) : undefined,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
default:
|
|
118
|
+
return base;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export namespace ResourceCenterService {
|
|
123
|
+
export const fetchThreadResources = async (
|
|
124
|
+
req: DThreadResourceRequest
|
|
125
|
+
): Promise<{ items: DThreadResource[]; page: number; hasMore: boolean }> => {
|
|
126
|
+
const res = await apiInstance?.post<
|
|
127
|
+
BaseResponse<DThreadResourceListResponse>
|
|
128
|
+
>(getApiEndpoint('messagesResources'), req);
|
|
129
|
+
const rows = res?.data?.data ?? [];
|
|
130
|
+
const items = rows.map(parseThreadResourceItem);
|
|
131
|
+
// `pageable.totalPages`/`totalElements` come back as `0` on this endpoint even
|
|
132
|
+
// when rows are present (confirmed against staging) — unreliable, don't use it.
|
|
133
|
+
// Item-count-vs-pageSize is the only trustworthy `hasMore` signal available.
|
|
134
|
+
const hasMore = items.length >= req.pageSize;
|
|
135
|
+
return { items, page: req.page, hasMore };
|
|
136
|
+
};
|
|
137
|
+
}
|