@droppii-org/chat-mobile 0.2.23 → 0.2.24
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/config/uploadLimits.js +13 -0
- package/lib/module/config/uploadLimits.js.map +1 -0
- package/lib/module/hooks/useChatCompose.js +142 -0
- package/lib/module/hooks/useChatCompose.js.map +1 -0
- package/lib/module/hooks/useImageAttachment.js +71 -151
- 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/hooks/useVideoAttachment.js +4 -4
- package/lib/module/hooks/useVideoAttachment.js.map +1 -1
- package/lib/module/index.js +0 -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 +16 -137
- package/lib/module/screens/chat-detail/ChatDetail.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/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 +6 -5
- package/lib/module/services/attachmentOrchestrator.js.map +1 -1
- 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/config/uploadLimits.d.ts +7 -0
- package/lib/typescript/src/config/uploadLimits.d.ts.map +1 -0
- 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/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/hooks/useVideoAttachment.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +0 -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/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/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/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 +1 -1
- package/src/config/uploadLimits.ts +11 -0
- package/src/hooks/useChatCompose.ts +186 -0
- package/src/hooks/useImageAttachment.ts +110 -197
- package/src/hooks/useSendAttachment.ts +3 -6
- package/src/hooks/useVideoAttachment.ts +4 -4
- package/src/index.tsx +1 -5
- package/src/screens/chat-detail/ChatComposer.tsx +88 -240
- package/src/screens/chat-detail/ChatDetail.tsx +24 -195
- package/src/screens/chat-detail/index.ts +1 -5
- 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 +6 -8
- 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/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/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/src/screens/chat-detail/hooks/useAttachmentSendHandler.ts +0 -386
- package/src/utils/giftedChatMessage.ts +0 -137
|
@@ -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
7
|
import UIUtils from '../utils/ui';
|
|
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,44 @@ 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
|
-
isUploading: false,
|
|
120
|
-
error: errorMsg,
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
console.error('uploadImage', 'Full error', error);
|
|
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 });
|
|
124
92
|
}
|
|
125
93
|
},
|
|
126
94
|
[updateImageStatus]
|
|
@@ -128,55 +96,36 @@ export const useImageAttachment = (): UseImageAttachmentReturn => {
|
|
|
128
96
|
|
|
129
97
|
const generateVideoThumbnail = useCallback(
|
|
130
98
|
async (index: number, image: PickedImage) => {
|
|
131
|
-
if (!isVideoFromPicker(image)) return;
|
|
132
|
-
console.log('[ImageAttach] Generating thumbnail for video:', image);
|
|
133
|
-
|
|
134
|
-
try {
|
|
135
|
-
let videoDuration = (image as any).duration || 0;
|
|
99
|
+
if (!isVideoFromPicker(image)) return;
|
|
136
100
|
|
|
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;
|
|
101
|
+
const video = image as PickedVideo;
|
|
102
|
+
let videoDuration =
|
|
103
|
+
typeof video.duration === 'number' ? video.duration : 0;
|
|
157
104
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
videoDuration,
|
|
162
|
-
timeMs,
|
|
163
|
-
});
|
|
105
|
+
if (videoDuration <= 0 || videoDuration > 3600000) {
|
|
106
|
+
videoDuration = 5000;
|
|
107
|
+
}
|
|
164
108
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
)
|
|
109
|
+
// Clamp to avoid negative timeMs on short clips.
|
|
110
|
+
const timeMs = Math.min(
|
|
111
|
+
Math.max(1000, Math.floor(videoDuration * 0.35)),
|
|
112
|
+
Math.max(0, videoDuration - 1000)
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
// Pass image.path directly — VideoThumbnailUtil owns path normalization
|
|
116
|
+
// (Android requires file:// to be preserved; stripping it causes a crash).
|
|
117
|
+
const baseName =
|
|
118
|
+
image.filename?.replace(/\.[^.]+$/, '') || `video-${index}`;
|
|
119
|
+
|
|
120
|
+
const thumbnail = await VideoThumbnailUtil.generateThumbnail(image.path, {
|
|
121
|
+
time: timeMs,
|
|
122
|
+
quality: 80,
|
|
123
|
+
cacheId: `thumb-${baseName}-${Date.now()}`,
|
|
124
|
+
});
|
|
175
125
|
|
|
176
|
-
|
|
126
|
+
if (!thumbnail) return;
|
|
177
127
|
|
|
178
|
-
|
|
179
|
-
const baseName = image.filename?.split('.')[0] || 'video';
|
|
128
|
+
try {
|
|
180
129
|
const uploadedThumbnail = await ImageUploadAPI.uploadImageFile(
|
|
181
130
|
{
|
|
182
131
|
path: thumbnail.path,
|
|
@@ -186,34 +135,22 @@ export const useImageAttachment = (): UseImageAttachmentReturn => {
|
|
|
186
135
|
height: thumbnail.height || 180,
|
|
187
136
|
mime: 'image/jpeg',
|
|
188
137
|
},
|
|
189
|
-
(
|
|
190
|
-
console.log('[ImageAttach] Thumbnail upload progress:', {
|
|
191
|
-
index,
|
|
192
|
-
percentage: progress,
|
|
193
|
-
});
|
|
194
|
-
}
|
|
138
|
+
() => {}
|
|
195
139
|
);
|
|
196
140
|
|
|
197
|
-
if (
|
|
198
|
-
|
|
141
|
+
if (uploadedThumbnail?.url) {
|
|
142
|
+
updateImageStatus(index, {
|
|
143
|
+
thumbnailUrl: uploadedThumbnail.url,
|
|
144
|
+
thumbnailSize: uploadedThumbnail.size || 0,
|
|
145
|
+
});
|
|
199
146
|
}
|
|
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
|
-
});
|
|
147
|
+
} catch (error: unknown) {
|
|
148
|
+
// Thumbnail failure is non-fatal — video still sends without preview.
|
|
149
|
+
if (__DEV__) {
|
|
150
|
+
console.warn('[ImageAttach] Thumbnail upload failed:', error);
|
|
151
|
+
}
|
|
152
|
+
} finally {
|
|
153
|
+
VideoThumbnailUtil.cleanupThumbnail(thumbnail.path).catch(() => {});
|
|
217
154
|
}
|
|
218
155
|
},
|
|
219
156
|
[updateImageStatus]
|
|
@@ -221,89 +158,65 @@ export const useImageAttachment = (): UseImageAttachmentReturn => {
|
|
|
221
158
|
|
|
222
159
|
const handleImageAttach = useCallback(
|
|
223
160
|
async (multipleMode: boolean = true) => {
|
|
224
|
-
console.log('ImageAttach', 'Step 1: User tapped attachment button', {
|
|
225
|
-
multipleMode,
|
|
226
|
-
});
|
|
227
161
|
try {
|
|
228
|
-
console.log('ImageAttach', 'Step 2: Opening image picker...');
|
|
229
162
|
const images = await DeviceUtils.openImagePicker(multipleMode);
|
|
163
|
+
if (!images || images.length === 0) return;
|
|
230
164
|
|
|
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
165
|
const isWithinLimit = (item: PickedImage) => {
|
|
245
166
|
const maxSize = isVideoFromPicker(item)
|
|
246
|
-
?
|
|
247
|
-
:
|
|
167
|
+
? UPLOAD_LIMITS.video
|
|
168
|
+
: UPLOAD_LIMITS.image;
|
|
248
169
|
return (item.size || 0) <= maxSize;
|
|
249
170
|
};
|
|
250
171
|
|
|
251
172
|
const oversizedItems = images.filter((img) => !isWithinLimit(img));
|
|
252
|
-
|
|
253
173
|
if (oversizedItems.length > 0) {
|
|
254
|
-
const hasOversizedVideo = oversizedItems.some(isVideoFromPicker);
|
|
255
|
-
console.warn('ImageAttach', 'Step 5: Some files too large', {
|
|
256
|
-
count: oversizedItems.length,
|
|
257
|
-
});
|
|
258
174
|
UIUtils.toast.open({
|
|
259
175
|
title: trans(
|
|
260
|
-
|
|
176
|
+
oversizedItems.some(isVideoFromPicker)
|
|
261
177
|
? 'chat:video_too_large'
|
|
262
178
|
: 'chat:image_too_large'
|
|
263
179
|
),
|
|
264
180
|
theme: 'danger',
|
|
265
181
|
});
|
|
266
|
-
const validImages = images.filter(isWithinLimit);
|
|
267
|
-
if (validImages.length === 0) return;
|
|
268
|
-
images.splice(0, images.length, ...validImages);
|
|
269
182
|
}
|
|
270
183
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
184
|
+
const finalImages = images.filter(isWithinLimit);
|
|
185
|
+
if (finalImages.length === 0) return;
|
|
186
|
+
|
|
187
|
+
// Derive startIndex inside the updater so concurrent calls don't
|
|
188
|
+
// produce the same index when state hasn't committed yet.
|
|
189
|
+
let startIndex = 0;
|
|
190
|
+
setSelectedImages((prev) => {
|
|
191
|
+
startIndex = prev.length;
|
|
192
|
+
return [
|
|
193
|
+
...prev,
|
|
194
|
+
...finalImages.map((image) => ({
|
|
195
|
+
image,
|
|
196
|
+
uploadProgress: 0,
|
|
197
|
+
error: null,
|
|
198
|
+
isUploading: true,
|
|
199
|
+
})),
|
|
200
|
+
];
|
|
277
201
|
});
|
|
278
|
-
|
|
279
|
-
setSelectedImages((prev) => [
|
|
280
|
-
...prev,
|
|
281
|
-
...images.map((image) => ({
|
|
282
|
-
image,
|
|
283
|
-
uploadProgress: 0,
|
|
284
|
-
error: null,
|
|
285
|
-
isUploading: true,
|
|
286
|
-
})),
|
|
287
|
-
]);
|
|
288
202
|
setIsImagePreviewVisible(true);
|
|
289
203
|
|
|
290
|
-
//
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
)
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
);
|
|
305
|
-
} catch (error) {
|
|
306
|
-
console.error('ImageAttach', 'ERROR', error);
|
|
204
|
+
// Thumbnail generation and upload run concurrently per-item,
|
|
205
|
+
// both limited to UPLOAD_CONCURRENCY to avoid OOM on low-end devices.
|
|
206
|
+
await Promise.all([
|
|
207
|
+
pLimit(
|
|
208
|
+
finalImages,
|
|
209
|
+
(image, offset) =>
|
|
210
|
+
generateVideoThumbnail(startIndex + offset, image),
|
|
211
|
+
UPLOAD_CONCURRENCY
|
|
212
|
+
),
|
|
213
|
+
pLimit(
|
|
214
|
+
finalImages,
|
|
215
|
+
(image, offset) => uploadImage(startIndex + offset, image),
|
|
216
|
+
UPLOAD_CONCURRENCY
|
|
217
|
+
),
|
|
218
|
+
]);
|
|
219
|
+
} catch (error: unknown) {
|
|
307
220
|
UIUtils.toast.open({
|
|
308
221
|
title: trans('chat:image_pick_failed'),
|
|
309
222
|
message: error instanceof Error ? error.message : 'Unknown error',
|
|
@@ -311,7 +224,7 @@ export const useImageAttachment = (): UseImageAttachmentReturn => {
|
|
|
311
224
|
});
|
|
312
225
|
}
|
|
313
226
|
},
|
|
314
|
-
[
|
|
227
|
+
[uploadImage, generateVideoThumbnail]
|
|
315
228
|
);
|
|
316
229
|
|
|
317
230
|
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:', {
|
|
@@ -4,6 +4,7 @@ import DeviceUtils from '../utils/device';
|
|
|
4
4
|
import UIUtils from '../utils/ui';
|
|
5
5
|
import { trans } from '../translation';
|
|
6
6
|
import { getMimeType } from '../utils/imageUtils';
|
|
7
|
+
import { UPLOAD_LIMITS } from '../config/uploadLimits';
|
|
7
8
|
import { ImageUploadAPI } from '../services/imageUpload';
|
|
8
9
|
import VideoThumbnailUtil from '../utils/videoThumbnail';
|
|
9
10
|
|
|
@@ -260,22 +261,21 @@ export const useVideoAttachment = (): UseVideoAttachmentReturn => {
|
|
|
260
261
|
count: videos.length,
|
|
261
262
|
});
|
|
262
263
|
|
|
263
|
-
const MAX_FILE_SIZE = 200 * 1024 * 1024; // 200MB for videos
|
|
264
264
|
const oversizedVideos = videos.filter(
|
|
265
|
-
(vid) => (vid.size || 0) >
|
|
265
|
+
(vid) => (vid.size || 0) > UPLOAD_LIMITS.video
|
|
266
266
|
);
|
|
267
267
|
|
|
268
268
|
if (oversizedVideos.length > 0) {
|
|
269
269
|
console.warn('VideoAttach', 'Step 5: Some videos too large', {
|
|
270
270
|
count: oversizedVideos.length,
|
|
271
|
-
maxSize:
|
|
271
|
+
maxSize: UPLOAD_LIMITS.video,
|
|
272
272
|
});
|
|
273
273
|
UIUtils.toast.open({
|
|
274
274
|
title: trans('chat:video_too_large'),
|
|
275
275
|
theme: 'danger',
|
|
276
276
|
});
|
|
277
277
|
const validVideos = videos.filter(
|
|
278
|
-
(vid) => (vid.size || 0) <=
|
|
278
|
+
(vid) => (vid.size || 0) <= UPLOAD_LIMITS.video
|
|
279
279
|
);
|
|
280
280
|
if (validVideos.length === 0) return;
|
|
281
281
|
videos.splice(0, videos.length, ...validVideos);
|
package/src/index.tsx
CHANGED
|
@@ -50,9 +50,5 @@ 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';
|