@droppii-org/chat-mobile 0.2.23 → 0.2.25
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/btn_resend.png +0 -0
- package/lib/module/assets/images/index.js +2 -1
- package/lib/module/assets/images/index.js.map +1 -1
- package/lib/module/components/AttachmentPreview/FilePreview.js +1 -1
- package/lib/module/components/AttachmentPreview/FilePreview.js.map +1 -1
- package/lib/module/components/messages/MessageStatusIndicator.js +70 -0
- package/lib/module/components/messages/MessageStatusIndicator.js.map +1 -0
- package/lib/module/components/messages/fileMessage/index.js.map +1 -1
- package/lib/module/components/messages/imageMessage/index.js +59 -50
- package/lib/module/components/messages/imageMessage/index.js.map +1 -1
- package/lib/module/components/messages/linkMessage/index.js +14 -6
- package/lib/module/components/messages/linkMessage/index.js.map +1 -1
- package/lib/module/components/messages/mergedMessage/index.js.map +1 -1
- package/lib/module/components/messages/textMessage/index.js +14 -6
- package/lib/module/components/messages/textMessage/index.js.map +1 -1
- package/lib/module/components/messages/videoMessage/index.js.map +1 -1
- package/lib/module/config/uploadLimits.js +13 -0
- package/lib/module/config/uploadLimits.js.map +1 -0
- package/lib/module/core/useChatListener.js +35 -4
- package/lib/module/core/useChatListener.js.map +1 -1
- package/lib/module/core/useUserListener.js +17 -7
- package/lib/module/core/useUserListener.js.map +1 -1
- package/lib/module/hooks/message/useSendMessage.js +62 -15
- package/lib/module/hooks/message/useSendMessage.js.map +1 -1
- package/lib/module/hooks/useChatCompose.js +153 -0
- package/lib/module/hooks/useChatCompose.js.map +1 -0
- package/lib/module/hooks/useChatMessages.js +39 -8
- package/lib/module/hooks/useChatMessages.js.map +1 -1
- package/lib/module/hooks/useFileAttachment.js +1 -1
- package/lib/module/hooks/useFileAttachment.js.map +1 -1
- package/lib/module/hooks/useFileDownload.js +1 -1
- package/lib/module/hooks/useFileDownload.js.map +1 -1
- package/lib/module/hooks/useImageAttachment.js +81 -152
- package/lib/module/hooks/useImageAttachment.js.map +1 -1
- package/lib/module/hooks/useSendAttachment.js +2 -2
- package/lib/module/hooks/useSendAttachment.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatComposer.js +50 -185
- package/lib/module/screens/chat-detail/ChatComposer.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatDetail.js +29 -137
- package/lib/module/screens/chat-detail/ChatDetail.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatListLegend.js +20 -15
- package/lib/module/screens/chat-detail/ChatListLegend.js.map +1 -1
- package/lib/module/screens/chat-detail/index.js +0 -1
- package/lib/module/screens/chat-detail/index.js.map +1 -1
- package/lib/module/screens/chat-detail/legend/LegendChatMessage.js +18 -13
- package/lib/module/screens/chat-detail/legend/LegendChatMessage.js.map +1 -1
- package/lib/module/services/attachmentHandlers/fileAttachmentHandler.js +2 -2
- package/lib/module/services/attachmentHandlers/fileAttachmentHandler.js.map +1 -1
- package/lib/module/services/attachmentHandlers/imageAttachmentHandler.js +3 -3
- package/lib/module/services/attachmentHandlers/imageAttachmentHandler.js.map +1 -1
- package/lib/module/services/attachmentHandlers/videoAttachmentHandler.js +2 -1
- package/lib/module/services/attachmentHandlers/videoAttachmentHandler.js.map +1 -1
- package/lib/module/services/attachmentOrchestrator.js +40 -17
- package/lib/module/services/attachmentOrchestrator.js.map +1 -1
- package/lib/module/services/message.js +13 -5
- package/lib/module/services/message.js.map +1 -1
- package/lib/module/translation/resources/i18n.js +4 -3
- package/lib/module/translation/resources/i18n.js.map +1 -1
- package/lib/module/types/chat.js.map +1 -1
- package/lib/module/types/global.d.js +2 -0
- package/lib/module/types/global.d.js.map +1 -0
- package/lib/module/types/index.d.js +3 -0
- package/lib/module/types/index.d.js.map +1 -0
- package/lib/module/utils/UIUtils.js +37 -0
- package/lib/module/utils/UIUtils.js.map +1 -0
- package/lib/module/utils/imageUtils.js +3 -3
- package/lib/module/utils/imageUtils.js.map +1 -1
- package/lib/module/utils/pLimit.js +19 -0
- package/lib/module/utils/pLimit.js.map +1 -0
- package/lib/module/utils/videoThumbnail.js +8 -3
- package/lib/module/utils/videoThumbnail.js.map +1 -1
- package/lib/module/utils/videoThumbnailUpload.js +76 -0
- package/lib/module/utils/videoThumbnailUpload.js.map +1 -0
- 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/messages/MessageStatusIndicator.d.ts +9 -0
- package/lib/typescript/src/components/messages/MessageStatusIndicator.d.ts.map +1 -0
- package/lib/typescript/src/components/messages/fileMessage/index.d.ts.map +1 -1
- package/lib/typescript/src/components/messages/imageMessage/index.d.ts.map +1 -1
- package/lib/typescript/src/components/messages/linkMessage/index.d.ts +3 -8
- package/lib/typescript/src/components/messages/linkMessage/index.d.ts.map +1 -1
- package/lib/typescript/src/components/messages/mergedMessage/index.d.ts.map +1 -1
- package/lib/typescript/src/components/messages/textMessage/index.d.ts +2 -8
- package/lib/typescript/src/components/messages/textMessage/index.d.ts.map +1 -1
- package/lib/typescript/src/components/messages/videoMessage/index.d.ts.map +1 -1
- package/lib/typescript/src/config/uploadLimits.d.ts +7 -0
- package/lib/typescript/src/config/uploadLimits.d.ts.map +1 -0
- package/lib/typescript/src/core/useChatListener.d.ts.map +1 -1
- package/lib/typescript/src/core/useUserListener.d.ts.map +1 -1
- package/lib/typescript/src/hooks/message/useSendMessage.d.ts +2 -0
- package/lib/typescript/src/hooks/message/useSendMessage.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useChatCompose.d.ts +27 -0
- package/lib/typescript/src/hooks/useChatCompose.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useChatMessages.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useImageAttachment.d.ts +2 -2
- package/lib/typescript/src/hooks/useImageAttachment.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useSendAttachment.d.ts +1 -1
- package/lib/typescript/src/hooks/useSendAttachment.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts +5 -2
- package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/index.d.ts +0 -2
- package/lib/typescript/src/screens/chat-detail/index.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts +3 -5
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/types.d.ts +1 -35
- package/lib/typescript/src/screens/chat-detail/types.d.ts.map +1 -1
- package/lib/typescript/src/services/attachmentHandlers/fileAttachmentHandler.d.ts.map +1 -1
- package/lib/typescript/src/services/attachmentHandlers/imageAttachmentHandler.d.ts.map +1 -1
- package/lib/typescript/src/services/attachmentHandlers/videoAttachmentHandler.d.ts.map +1 -1
- package/lib/typescript/src/services/attachmentOrchestrator.d.ts +1 -1
- package/lib/typescript/src/services/attachmentOrchestrator.d.ts.map +1 -1
- package/lib/typescript/src/services/message.d.ts +7 -0
- package/lib/typescript/src/services/message.d.ts.map +1 -1
- package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -1
- package/lib/typescript/src/types/chat.d.ts +11 -0
- package/lib/typescript/src/types/chat.d.ts.map +1 -1
- package/lib/typescript/src/utils/UIUtils.d.ts +4 -0
- package/lib/typescript/src/utils/UIUtils.d.ts.map +1 -0
- package/lib/typescript/src/utils/imageUtils.d.ts.map +1 -1
- package/lib/typescript/src/utils/pLimit.d.ts +6 -0
- package/lib/typescript/src/utils/pLimit.d.ts.map +1 -0
- package/lib/typescript/src/utils/videoThumbnail.d.ts.map +1 -1
- package/lib/typescript/src/utils/videoThumbnailUpload.d.ts +11 -0
- package/lib/typescript/src/utils/videoThumbnailUpload.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/assets/images/btn_resend.png +0 -0
- package/src/assets/images/index.ts +1 -0
- package/src/components/AttachmentPreview/FilePreview.tsx +1 -1
- package/src/components/messages/MessageStatusIndicator.tsx +82 -0
- package/src/components/messages/fileMessage/index.tsx +0 -1
- package/src/components/messages/imageMessage/index.tsx +71 -57
- package/src/components/messages/linkMessage/index.tsx +13 -19
- package/src/components/messages/mergedMessage/index.tsx +0 -1
- package/src/components/messages/textMessage/index.tsx +13 -18
- package/src/components/messages/videoMessage/index.tsx +0 -1
- package/src/config/uploadLimits.ts +11 -0
- package/src/core/useChatListener.ts +38 -4
- package/src/core/useUserListener.ts +17 -7
- package/src/hooks/message/useSendMessage.ts +103 -24
- package/src/hooks/useChatCompose.ts +195 -0
- package/src/hooks/useChatMessages.ts +54 -10
- package/src/hooks/useFileAttachment.ts +1 -1
- package/src/hooks/useFileDownload.ts +1 -1
- package/src/hooks/useImageAttachment.ts +120 -199
- package/src/hooks/useSendAttachment.ts +3 -6
- package/src/index.tsx +3 -5
- package/src/screens/chat-detail/ChatComposer.tsx +88 -240
- package/src/screens/chat-detail/ChatDetail.tsx +36 -195
- package/src/screens/chat-detail/ChatListLegend.tsx +16 -13
- package/src/screens/chat-detail/index.ts +1 -5
- package/src/screens/chat-detail/legend/LegendChatMessage.tsx +19 -12
- package/src/screens/chat-detail/types.ts +1 -38
- package/src/services/attachmentHandlers/fileAttachmentHandler.ts +3 -2
- package/src/services/attachmentHandlers/imageAttachmentHandler.ts +6 -3
- package/src/services/attachmentHandlers/videoAttachmentHandler.ts +2 -1
- package/src/services/attachmentOrchestrator.ts +49 -27
- package/src/services/message.ts +21 -4
- package/src/translation/resources/i18n.ts +6 -3
- package/src/types/chat.ts +6 -0
- package/src/types/global.d.ts +299 -0
- package/src/types/index.d.ts +1 -0
- package/src/utils/UIUtils.ts +31 -0
- package/src/utils/imageUtils.ts +3 -4
- package/src/utils/pLimit.ts +20 -0
- package/src/utils/videoThumbnail.ts +10 -3
- package/src/utils/videoThumbnailUpload.ts +103 -0
- package/lib/module/hooks/useVideoAttachment.js +0 -251
- package/lib/module/hooks/useVideoAttachment.js.map +0 -1
- package/lib/module/screens/chat-detail/hooks/useAttachmentSendHandler.js +0 -288
- package/lib/module/screens/chat-detail/hooks/useAttachmentSendHandler.js.map +0 -1
- package/lib/module/utils/giftedChatMessage.js +0 -90
- package/lib/module/utils/giftedChatMessage.js.map +0 -1
- package/lib/module/utils/ui.js +0 -17
- package/lib/module/utils/ui.js.map +0 -1
- package/lib/typescript/src/hooks/useVideoAttachment.d.ts +0 -29
- package/lib/typescript/src/hooks/useVideoAttachment.d.ts.map +0 -1
- package/lib/typescript/src/screens/chat-detail/hooks/useAttachmentSendHandler.d.ts +0 -26
- package/lib/typescript/src/screens/chat-detail/hooks/useAttachmentSendHandler.d.ts.map +0 -1
- package/lib/typescript/src/utils/giftedChatMessage.d.ts +0 -10
- package/lib/typescript/src/utils/giftedChatMessage.d.ts.map +0 -1
- package/lib/typescript/src/utils/ui.d.ts +0 -13
- package/lib/typescript/src/utils/ui.d.ts.map +0 -1
- package/src/hooks/useVideoAttachment.ts +0 -334
- package/src/screens/chat-detail/hooks/useAttachmentSendHandler.ts +0 -386
- package/src/utils/giftedChatMessage.ts +0 -137
- package/src/utils/ui.ts +0 -19
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
mergeMessages,
|
|
12
12
|
} from '../utils/message';
|
|
13
13
|
import { useMessageStore } from '../store/message';
|
|
14
|
+
import { useUserStore } from '../store/user';
|
|
15
|
+
import { useChatContext } from '../context';
|
|
14
16
|
|
|
15
17
|
type UseChatMessagesOptions = {
|
|
16
18
|
conversationId: string;
|
|
@@ -30,7 +32,11 @@ export function useChatMessages({
|
|
|
30
32
|
const pushNewMessage = useMessageStore((s) => s.pushNewMessage);
|
|
31
33
|
const reset = useMessageStore((s) => s.reset);
|
|
32
34
|
|
|
33
|
-
const
|
|
35
|
+
const { applicationType } = useChatContext();
|
|
36
|
+
const cachedUserId = useUserStore((s) => s.user?.userID);
|
|
37
|
+
const [currentUserId, setCurrentUserId] = useState<string | undefined>(
|
|
38
|
+
cachedUserId
|
|
39
|
+
);
|
|
34
40
|
const [isLoading, setIsLoading] = useState(false);
|
|
35
41
|
const [isLoadingEarlier, setIsLoadingEarlier] = useState(false);
|
|
36
42
|
const [isLoadingNewer, setIsLoadingNewer] = useState(false);
|
|
@@ -46,27 +52,60 @@ export function useChatMessages({
|
|
|
46
52
|
setError(null);
|
|
47
53
|
|
|
48
54
|
try {
|
|
49
|
-
|
|
55
|
+
// Fetch userId and history independently so an offline SDK state
|
|
56
|
+
// doesn't block reading local SQLite history.
|
|
57
|
+
const [historyResult, userIdResult] = await Promise.allSettled([
|
|
50
58
|
ChatMessageAPI.fetchHistoryMessages(conversationId, {
|
|
51
59
|
count: pageSize,
|
|
60
|
+
applicationType,
|
|
52
61
|
}),
|
|
53
62
|
ChatMessageAPI.getCurrentUserId(),
|
|
54
63
|
]);
|
|
55
64
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
setMessages(mergeMessages([], initialMessages), !history.isEnd);
|
|
65
|
+
if (userIdResult.status === 'fulfilled') {
|
|
66
|
+
setCurrentUserId(userIdResult.value);
|
|
67
|
+
}
|
|
60
68
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
69
|
+
if (historyResult.status === 'fulfilled') {
|
|
70
|
+
const initialMessages = historyResult.value
|
|
71
|
+
.messageList as DMessageItem[];
|
|
72
|
+
setMessages(
|
|
73
|
+
mergeMessages([], initialMessages),
|
|
74
|
+
!historyResult.value.isEnd
|
|
75
|
+
);
|
|
76
|
+
ChatMessageAPI.markConversationAsRead(conversationId).catch(
|
|
77
|
+
() => undefined
|
|
78
|
+
);
|
|
79
|
+
} else {
|
|
80
|
+
// Fallback: try searchLocalMessages to verify local SQLite is accessible offline
|
|
81
|
+
console.warn(
|
|
82
|
+
'[useChatMessages] getAdvancedHistoryMessageList failed, trying searchLocalMessages fallback:',
|
|
83
|
+
historyResult.reason
|
|
84
|
+
);
|
|
85
|
+
const localResult = await ChatMessageAPI.searchLocalMessages(
|
|
86
|
+
conversationId,
|
|
87
|
+
{ count: pageSize }
|
|
88
|
+
);
|
|
89
|
+
const localMessages = (localResult?.searchResultItems ?? []).flatMap(
|
|
90
|
+
(item: any) => item.messageList ?? []
|
|
91
|
+
) as DMessageItem[];
|
|
92
|
+
console.log(
|
|
93
|
+
'[useChatMessages] searchLocalMessages fallback result:',
|
|
94
|
+
localMessages.length,
|
|
95
|
+
'messages'
|
|
96
|
+
);
|
|
97
|
+
if (localMessages.length > 0) {
|
|
98
|
+
setMessages(mergeMessages([], localMessages), false);
|
|
99
|
+
} else {
|
|
100
|
+
throw historyResult.reason;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
64
103
|
} catch (err) {
|
|
65
104
|
setError(err instanceof Error ? err : new Error(String(err)));
|
|
66
105
|
} finally {
|
|
67
106
|
setIsLoading(false);
|
|
68
107
|
}
|
|
69
|
-
}, [conversationId, pageSize, setMessages]);
|
|
108
|
+
}, [applicationType, conversationId, pageSize, setMessages]);
|
|
70
109
|
|
|
71
110
|
const onLoadEarlier = useCallback(async () => {
|
|
72
111
|
if (isLoadingEarlier || !hasMoreEarlier || !messages.length) return;
|
|
@@ -148,6 +187,11 @@ export function useChatMessages({
|
|
|
148
187
|
|
|
149
188
|
const appendIncomingMessages = useCallback(
|
|
150
189
|
(incoming: MessageItem[]) => {
|
|
190
|
+
console.log('[Listener] OnRecvNewMessages', {
|
|
191
|
+
total: incoming.length,
|
|
192
|
+
conversationId: conversationIdRef.current,
|
|
193
|
+
clientMsgIDs: incoming.map((m) => m.clientMsgID),
|
|
194
|
+
});
|
|
151
195
|
const relevant = incoming.filter((message) =>
|
|
152
196
|
belongsToConversation(message, conversationIdRef.current)
|
|
153
197
|
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useState } from 'react';
|
|
2
2
|
import { ImageUploadAPI } from '../services/imageUpload';
|
|
3
3
|
import DeviceUtils from '../utils/device';
|
|
4
|
-
import UIUtils from '../utils/
|
|
4
|
+
import { UIUtils } from '../utils/UIUtils';
|
|
5
5
|
import { trans } from '../translation';
|
|
6
6
|
import { MAX_FILE_SIZE } from '../services/attachmentHandlers';
|
|
7
7
|
|
|
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useRef, useState } from 'react';
|
|
|
2
2
|
import { Platform, Share } from 'react-native';
|
|
3
3
|
import RNFS from 'react-native-fs';
|
|
4
4
|
import FileViewer from 'react-native-file-viewer';
|
|
5
|
-
import UIUtils from '../utils/
|
|
5
|
+
import { UIUtils } from '../utils/UIUtils';
|
|
6
6
|
import { trans } from '../translation';
|
|
7
7
|
|
|
8
8
|
export type DownloadStatus = 'idle' | 'downloading' | 'done' | 'error';
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { useCallback, useState } from 'react';
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
ImageOrVideo as PickedImage,
|
|
4
|
+
Video as PickedVideo,
|
|
5
|
+
} from 'react-native-image-crop-picker';
|
|
3
6
|
import DeviceUtils from '../utils/device';
|
|
4
|
-
import UIUtils from '../utils/
|
|
7
|
+
import { UIUtils } from '../utils/UIUtils';
|
|
5
8
|
import { trans } from '../translation';
|
|
6
9
|
import { getMimeType, isVideoFromPicker } from '../utils/imageUtils';
|
|
10
|
+
import { UPLOAD_CONCURRENCY, UPLOAD_LIMITS } from '../config/uploadLimits';
|
|
7
11
|
import { ImageUploadAPI } from '../services/imageUpload';
|
|
8
12
|
import VideoThumbnailUtil from '../utils/videoThumbnail';
|
|
13
|
+
import { pLimit } from '../utils/pLimit';
|
|
9
14
|
|
|
10
15
|
export interface ImageWithUploadStatus {
|
|
11
16
|
image: PickedImage;
|
|
@@ -20,7 +25,7 @@ export interface ImageWithUploadStatus {
|
|
|
20
25
|
export interface UseImageAttachmentReturn {
|
|
21
26
|
selectedImages: ImageWithUploadStatus[];
|
|
22
27
|
isImagePreviewVisible: boolean;
|
|
23
|
-
handleImageAttach: () => Promise<void>;
|
|
28
|
+
handleImageAttach: (multipleMode?: boolean) => Promise<void>;
|
|
24
29
|
handleImageRemove: (index: number) => void;
|
|
25
30
|
clearAllImages: () => void;
|
|
26
31
|
}
|
|
@@ -46,81 +51,49 @@ export const useImageAttachment = (): UseImageAttachmentReturn => {
|
|
|
46
51
|
|
|
47
52
|
const uploadImage = useCallback(
|
|
48
53
|
async (index: number, image: PickedImage) => {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
isUploading: true,
|
|
55
|
-
error: null,
|
|
56
|
-
uploadProgress: 0,
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
// ✅ OPTIMIZED: Use original path directly, no copy
|
|
60
|
-
const filePath = image.path.startsWith('file://')
|
|
61
|
-
? image.path.replace('file://', '')
|
|
62
|
-
: image.path;
|
|
54
|
+
updateImageStatus(index, {
|
|
55
|
+
isUploading: true,
|
|
56
|
+
error: null,
|
|
57
|
+
uploadProgress: 0,
|
|
58
|
+
});
|
|
63
59
|
|
|
64
|
-
|
|
60
|
+
const filePath = image.path.startsWith('file://')
|
|
61
|
+
? image.path.replace('file://', '')
|
|
62
|
+
: image.path;
|
|
65
63
|
|
|
66
|
-
|
|
67
|
-
try {
|
|
68
|
-
console.log('Upload', 'Progress', { percentage: progress });
|
|
69
|
-
updateImageStatus(index, { uploadProgress: progress });
|
|
70
|
-
} catch (err) {
|
|
71
|
-
console.error('Upload', 'Progress update error', err);
|
|
72
|
-
}
|
|
73
|
-
};
|
|
64
|
+
const filename = image.filename || 'image.jpg';
|
|
74
65
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
66
|
+
try {
|
|
67
|
+
const uploadedImage = await ImageUploadAPI.uploadImageFile(
|
|
68
|
+
{
|
|
69
|
+
path: filePath,
|
|
78
70
|
filename,
|
|
79
|
-
size: image.size,
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
size: image.size || 0,
|
|
87
|
-
width: image.width || 0,
|
|
88
|
-
height: image.height || 0,
|
|
89
|
-
mime: getMimeType(filename),
|
|
90
|
-
},
|
|
91
|
-
setProgress
|
|
92
|
-
);
|
|
93
|
-
console.log('Upload', 'Upload successful!', {
|
|
94
|
-
url: uploadedImage?.url,
|
|
95
|
-
});
|
|
96
|
-
} catch (uploadError: any) {
|
|
97
|
-
console.error('Upload', 'Server upload error', uploadError);
|
|
98
|
-
throw new Error(
|
|
99
|
-
`Upload failed: ${uploadError?.message || 'Unknown error'}`
|
|
100
|
-
);
|
|
101
|
-
}
|
|
71
|
+
size: image.size || 0,
|
|
72
|
+
width: image.width || 0,
|
|
73
|
+
height: image.height || 0,
|
|
74
|
+
mime: getMimeType(filename),
|
|
75
|
+
},
|
|
76
|
+
(progress) => updateImageStatus(index, { uploadProgress: progress })
|
|
77
|
+
);
|
|
102
78
|
|
|
103
|
-
if (!uploadedImage?.url)
|
|
104
|
-
throw new Error('No URL returned from server');
|
|
105
|
-
}
|
|
79
|
+
if (!uploadedImage?.url) throw new Error('No URL returned from server');
|
|
106
80
|
|
|
107
|
-
console.log('Upload', 'Upload complete, updating status...');
|
|
108
81
|
updateImageStatus(index, {
|
|
109
82
|
isUploading: false,
|
|
110
83
|
uploadProgress: 100,
|
|
111
84
|
uploadedUrl: uploadedImage.url,
|
|
112
85
|
});
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
updateImageStatus(index, {
|
|
119
|
-
|
|
120
|
-
|
|
86
|
+
} catch (error: unknown) {
|
|
87
|
+
const msg =
|
|
88
|
+
error instanceof Error
|
|
89
|
+
? error?.message
|
|
90
|
+
: trans('chat:image_upload_failed');
|
|
91
|
+
updateImageStatus(index, { isUploading: false, error: msg });
|
|
92
|
+
UIUtils.toast.open({
|
|
93
|
+
title: trans('chat:image_upload_failed'),
|
|
94
|
+
message: trans('chat:generic_error'),
|
|
95
|
+
theme: 'danger',
|
|
121
96
|
});
|
|
122
|
-
|
|
123
|
-
console.error('uploadImage', 'Full error', error);
|
|
124
97
|
}
|
|
125
98
|
},
|
|
126
99
|
[updateImageStatus]
|
|
@@ -128,55 +101,36 @@ export const useImageAttachment = (): UseImageAttachmentReturn => {
|
|
|
128
101
|
|
|
129
102
|
const generateVideoThumbnail = useCallback(
|
|
130
103
|
async (index: number, image: PickedImage) => {
|
|
131
|
-
if (!isVideoFromPicker(image)) return;
|
|
132
|
-
console.log('[ImageAttach] Generating thumbnail for video:', image);
|
|
104
|
+
if (!isVideoFromPicker(image)) return;
|
|
133
105
|
|
|
134
|
-
|
|
135
|
-
|
|
106
|
+
const video = image as PickedVideo;
|
|
107
|
+
let videoDuration =
|
|
108
|
+
typeof video.duration === 'number' ? video.duration : 0;
|
|
136
109
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
console.warn(
|
|
141
|
-
'[ImageAttach] Invalid duration, using default:',
|
|
142
|
-
videoDuration
|
|
143
|
-
);
|
|
144
|
-
videoDuration = 5000; // Default 5 seconds
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// ✅ FIX: Choose frame at 35% for better representation
|
|
148
|
-
const timeMs = Math.min(
|
|
149
|
-
Math.max(1000, Math.floor(videoDuration * 0.35)),
|
|
150
|
-
videoDuration - 1000
|
|
151
|
-
);
|
|
152
|
-
|
|
153
|
-
// ✅ OPTIMIZED: Use original path directly, no copy
|
|
154
|
-
const videoPath = image.path.startsWith('file://')
|
|
155
|
-
? image.path.replace('file://', '')
|
|
156
|
-
: image.path;
|
|
157
|
-
|
|
158
|
-
console.log('[ImageAttach] Generating thumbnail for video:', {
|
|
159
|
-
filename: image.filename,
|
|
160
|
-
videoPath,
|
|
161
|
-
videoDuration,
|
|
162
|
-
timeMs,
|
|
163
|
-
});
|
|
110
|
+
if (videoDuration <= 0 || videoDuration > 3600000) {
|
|
111
|
+
videoDuration = 5000;
|
|
112
|
+
}
|
|
164
113
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
)
|
|
114
|
+
// Clamp to avoid negative timeMs on short clips.
|
|
115
|
+
const timeMs = Math.min(
|
|
116
|
+
Math.max(1000, Math.floor(videoDuration * 0.35)),
|
|
117
|
+
Math.max(0, videoDuration - 1000)
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
// Pass image.path directly — VideoThumbnailUtil owns path normalization
|
|
121
|
+
// (Android requires file:// to be preserved; stripping it causes a crash).
|
|
122
|
+
const baseName =
|
|
123
|
+
image.filename?.replace(/\.[^.]+$/, '') || `video-${index}`;
|
|
124
|
+
|
|
125
|
+
const thumbnail = await VideoThumbnailUtil.generateThumbnail(image.path, {
|
|
126
|
+
time: timeMs,
|
|
127
|
+
quality: 80,
|
|
128
|
+
cacheId: `thumb-${baseName}-${Date.now()}`,
|
|
129
|
+
});
|
|
175
130
|
|
|
176
|
-
|
|
131
|
+
if (!thumbnail) return;
|
|
177
132
|
|
|
178
|
-
|
|
179
|
-
const baseName = image.filename?.split('.')[0] || 'video';
|
|
133
|
+
try {
|
|
180
134
|
const uploadedThumbnail = await ImageUploadAPI.uploadImageFile(
|
|
181
135
|
{
|
|
182
136
|
path: thumbnail.path,
|
|
@@ -186,34 +140,22 @@ export const useImageAttachment = (): UseImageAttachmentReturn => {
|
|
|
186
140
|
height: thumbnail.height || 180,
|
|
187
141
|
mime: 'image/jpeg',
|
|
188
142
|
},
|
|
189
|
-
(
|
|
190
|
-
console.log('[ImageAttach] Thumbnail upload progress:', {
|
|
191
|
-
index,
|
|
192
|
-
percentage: progress,
|
|
193
|
-
});
|
|
194
|
-
}
|
|
143
|
+
() => {}
|
|
195
144
|
);
|
|
196
145
|
|
|
197
|
-
if (
|
|
198
|
-
|
|
146
|
+
if (uploadedThumbnail?.url) {
|
|
147
|
+
updateImageStatus(index, {
|
|
148
|
+
thumbnailUrl: uploadedThumbnail.url,
|
|
149
|
+
thumbnailSize: uploadedThumbnail.size || 0,
|
|
150
|
+
});
|
|
199
151
|
}
|
|
200
|
-
|
|
201
|
-
//
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
console.log(
|
|
208
|
-
`✅ [ImageAttach] THUMBNAIL URL (picked): ${uploadedThumbnail.url}`
|
|
209
|
-
);
|
|
210
|
-
} catch (error) {
|
|
211
|
-
console.error('[ImageAttach] Video thumbnail generation error:', error);
|
|
212
|
-
UIUtils.toast.open({
|
|
213
|
-
title: 'Thumbnail Error',
|
|
214
|
-
message: `Failed to generate thumbnail for ${image.filename}`,
|
|
215
|
-
theme: 'danger',
|
|
216
|
-
});
|
|
152
|
+
} catch (error: unknown) {
|
|
153
|
+
// Thumbnail failure is non-fatal — video still sends without preview.
|
|
154
|
+
if (__DEV__) {
|
|
155
|
+
console.warn('[ImageAttach] Thumbnail upload failed:', error);
|
|
156
|
+
}
|
|
157
|
+
} finally {
|
|
158
|
+
VideoThumbnailUtil.cleanupThumbnail(thumbnail.path).catch(() => {});
|
|
217
159
|
}
|
|
218
160
|
},
|
|
219
161
|
[updateImageStatus]
|
|
@@ -221,89 +163,68 @@ export const useImageAttachment = (): UseImageAttachmentReturn => {
|
|
|
221
163
|
|
|
222
164
|
const handleImageAttach = useCallback(
|
|
223
165
|
async (multipleMode: boolean = true) => {
|
|
224
|
-
console.log('ImageAttach', 'Step 1: User tapped attachment button', {
|
|
225
|
-
multipleMode,
|
|
226
|
-
});
|
|
227
166
|
try {
|
|
228
|
-
console.log('ImageAttach', 'Step 2: Opening image picker...');
|
|
229
167
|
const images = await DeviceUtils.openImagePicker(multipleMode);
|
|
168
|
+
if (!images || images.length === 0) return;
|
|
230
169
|
|
|
231
|
-
if (!images || images.length === 0) {
|
|
232
|
-
console.log('ImageAttach', 'Step 3: User cancelled picker');
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
console.log('ImageAttach', 'Step 4: Images picked', {
|
|
237
|
-
count: images.length,
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
// The gallery picker can return both images and videos. Apply the
|
|
241
|
-
// correct size limit per type: 5MB for images, 200MB for videos.
|
|
242
|
-
const MAX_IMAGE_SIZE = 5 * 1024 * 1024;
|
|
243
|
-
const MAX_VIDEO_SIZE = 200 * 1024 * 1024;
|
|
244
170
|
const isWithinLimit = (item: PickedImage) => {
|
|
245
171
|
const maxSize = isVideoFromPicker(item)
|
|
246
|
-
?
|
|
247
|
-
:
|
|
172
|
+
? UPLOAD_LIMITS.video
|
|
173
|
+
: UPLOAD_LIMITS.image;
|
|
248
174
|
return (item.size || 0) <= maxSize;
|
|
249
175
|
};
|
|
250
176
|
|
|
251
177
|
const oversizedItems = images.filter((img) => !isWithinLimit(img));
|
|
252
|
-
|
|
253
178
|
if (oversizedItems.length > 0) {
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
179
|
+
const isVideo = oversizedItems.some(isVideoFromPicker);
|
|
180
|
+
const maxSize = Math.floor(
|
|
181
|
+
(isVideo ? UPLOAD_LIMITS.video : UPLOAD_LIMITS.image) / 1024 / 1024
|
|
182
|
+
);
|
|
258
183
|
UIUtils.toast.open({
|
|
259
184
|
title: trans(
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
: 'chat:image_too_large'
|
|
185
|
+
isVideo ? 'chat:video_too_large' : 'chat:image_too_large',
|
|
186
|
+
{ maxSize: String(maxSize) }
|
|
263
187
|
),
|
|
264
188
|
theme: 'danger',
|
|
265
189
|
});
|
|
266
|
-
const validImages = images.filter(isWithinLimit);
|
|
267
|
-
if (validImages.length === 0) return;
|
|
268
|
-
images.splice(0, images.length, ...validImages);
|
|
269
190
|
}
|
|
270
191
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
192
|
+
const finalImages = images.filter(isWithinLimit);
|
|
193
|
+
if (finalImages.length === 0) return;
|
|
194
|
+
|
|
195
|
+
// Derive startIndex inside the updater so concurrent calls don't
|
|
196
|
+
// produce the same index when state hasn't committed yet.
|
|
197
|
+
let startIndex = 0;
|
|
198
|
+
setSelectedImages((prev) => {
|
|
199
|
+
startIndex = prev.length;
|
|
200
|
+
return [
|
|
201
|
+
...prev,
|
|
202
|
+
...finalImages.map((image) => ({
|
|
203
|
+
image,
|
|
204
|
+
uploadProgress: 0,
|
|
205
|
+
error: null,
|
|
206
|
+
isUploading: true,
|
|
207
|
+
})),
|
|
208
|
+
];
|
|
277
209
|
});
|
|
278
|
-
|
|
279
|
-
setSelectedImages((prev) => [
|
|
280
|
-
...prev,
|
|
281
|
-
...images.map((image) => ({
|
|
282
|
-
image,
|
|
283
|
-
uploadProgress: 0,
|
|
284
|
-
error: null,
|
|
285
|
-
isUploading: true,
|
|
286
|
-
})),
|
|
287
|
-
]);
|
|
288
210
|
setIsImagePreviewVisible(true);
|
|
289
211
|
|
|
290
|
-
//
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
)
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
);
|
|
305
|
-
} catch (error) {
|
|
306
|
-
console.error('ImageAttach', 'ERROR', error);
|
|
212
|
+
// Thumbnail generation and upload run concurrently per-item,
|
|
213
|
+
// both limited to UPLOAD_CONCURRENCY to avoid OOM on low-end devices.
|
|
214
|
+
await Promise.all([
|
|
215
|
+
pLimit(
|
|
216
|
+
finalImages,
|
|
217
|
+
(image, offset) =>
|
|
218
|
+
generateVideoThumbnail(startIndex + offset, image),
|
|
219
|
+
UPLOAD_CONCURRENCY
|
|
220
|
+
),
|
|
221
|
+
pLimit(
|
|
222
|
+
finalImages,
|
|
223
|
+
(image, offset) => uploadImage(startIndex + offset, image),
|
|
224
|
+
UPLOAD_CONCURRENCY
|
|
225
|
+
),
|
|
226
|
+
]);
|
|
227
|
+
} catch (error: unknown) {
|
|
307
228
|
UIUtils.toast.open({
|
|
308
229
|
title: trans('chat:image_pick_failed'),
|
|
309
230
|
message: error instanceof Error ? error.message : 'Unknown error',
|
|
@@ -311,7 +232,7 @@ export const useImageAttachment = (): UseImageAttachmentReturn => {
|
|
|
311
232
|
});
|
|
312
233
|
}
|
|
313
234
|
},
|
|
314
|
-
[
|
|
235
|
+
[uploadImage, generateVideoThumbnail]
|
|
315
236
|
);
|
|
316
237
|
|
|
317
238
|
const handleImageRemove = useCallback((index: number) => {
|
|
@@ -56,8 +56,7 @@ export interface UseSendAttachmentReturn {
|
|
|
56
56
|
error: string | null;
|
|
57
57
|
send(
|
|
58
58
|
items: AttachmentItem[],
|
|
59
|
-
options?: Partial<AttachmentSendOptions
|
|
60
|
-
onMessageSent?: (message: any) => void
|
|
59
|
+
options?: Partial<AttachmentSendOptions>
|
|
61
60
|
): Promise<boolean>;
|
|
62
61
|
clearError(): void;
|
|
63
62
|
}
|
|
@@ -75,8 +74,7 @@ export function useSendAttachment(): UseSendAttachmentReturn {
|
|
|
75
74
|
const send = useCallback(
|
|
76
75
|
async (
|
|
77
76
|
items: AttachmentItem[],
|
|
78
|
-
options?: Partial<AttachmentSendOptions
|
|
79
|
-
onMessageSent?: (message: any) => void
|
|
77
|
+
options?: Partial<AttachmentSendOptions>
|
|
80
78
|
): Promise<boolean> => {
|
|
81
79
|
console.log('[useSendAttachment] Start sending', {
|
|
82
80
|
totalItems: items.length,
|
|
@@ -245,8 +243,7 @@ export function useSendAttachment(): UseSendAttachmentReturn {
|
|
|
245
243
|
{
|
|
246
244
|
strategy: 'individual',
|
|
247
245
|
...options,
|
|
248
|
-
}
|
|
249
|
-
onMessageSent
|
|
246
|
+
}
|
|
250
247
|
);
|
|
251
248
|
|
|
252
249
|
console.log('[useSendAttachment] Send result:', {
|
package/src/index.tsx
CHANGED
|
@@ -50,9 +50,7 @@ export type {
|
|
|
50
50
|
ChatQuickActionsRenderParams,
|
|
51
51
|
ChatAttachmentPanelProps,
|
|
52
52
|
} from './screens/chat-detail';
|
|
53
|
-
|
|
54
|
-
export {
|
|
55
|
-
mapOpenIMMessageToGiftedChat,
|
|
56
|
-
mapOpenIMMessagesToGiftedChat,
|
|
57
|
-
} from './utils/giftedChatMessage';
|
|
53
|
+
|
|
58
54
|
export type { PeerType } from '@droppii/openim-rn-client-sdk';
|
|
55
|
+
|
|
56
|
+
export { registerUIUtils, UIUtils } from './utils/UIUtils';
|