@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
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import RNFS from 'react-native-fs';
|
|
2
|
+
import VideoThumbnailUtil from './videoThumbnail';
|
|
3
|
+
import { ImageUploadAPI } from '../services/imageUpload';
|
|
4
|
+
import { UIUtils } from './UIUtils';
|
|
5
|
+
import type { Video as PickedVideo } from 'react-native-image-crop-picker';
|
|
6
|
+
import type { ImageWithUploadStatus } from '../hooks/useImageAttachment';
|
|
7
|
+
|
|
8
|
+
export type VideoWithUploadStatus = Omit<ImageWithUploadStatus, 'image'> & {
|
|
9
|
+
image: PickedVideo;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export interface VideoThumbnailResult {
|
|
13
|
+
url: string;
|
|
14
|
+
size: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function generateAndUploadVideoThumbnails(
|
|
18
|
+
videos: VideoWithUploadStatus[]
|
|
19
|
+
): Promise<Record<string, VideoThumbnailResult>> {
|
|
20
|
+
const thumbnails: Record<string, VideoThumbnailResult> = {};
|
|
21
|
+
const failed: string[] = [];
|
|
22
|
+
|
|
23
|
+
for (let idx = 0; idx < videos.length; idx++) {
|
|
24
|
+
const vidItem = videos[idx]!;
|
|
25
|
+
// Key by uploadedUrl so callers can look up by the same value.
|
|
26
|
+
const videoId = vidItem.uploadedUrl || `video-${idx}`;
|
|
27
|
+
let cachedVideoPath: string | null = null;
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
let videoDuration = vidItem.image.duration ?? 0;
|
|
31
|
+
|
|
32
|
+
if (videoDuration <= 0 || videoDuration > 3600000) {
|
|
33
|
+
videoDuration = 5000;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Clamp upper bound to avoid negative timeMs on clips shorter than 1s.
|
|
37
|
+
const timeMs = Math.min(
|
|
38
|
+
Math.max(1000, Math.floor(videoDuration * 0.35)),
|
|
39
|
+
Math.max(0, videoDuration - 1000)
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
// Copy the picker-returned path to CachesDirectory before generating a
|
|
43
|
+
// thumbnail. On iOS the picker can return Photos-library paths that
|
|
44
|
+
// react-native-create-thumbnail cannot open directly; a copy to a
|
|
45
|
+
// stable app-owned location is required.
|
|
46
|
+
const filename = vidItem.image.filename || 'video.mp4';
|
|
47
|
+
const ext = filename.split('.').pop() || 'mp4';
|
|
48
|
+
const cachedFileName = `thumb-src-${idx}-${Date.now()}.${ext}`;
|
|
49
|
+
cachedVideoPath = `${RNFS.CachesDirectoryPath}/${cachedFileName}`;
|
|
50
|
+
await RNFS.copyFile(vidItem.image.path, cachedVideoPath);
|
|
51
|
+
|
|
52
|
+
const thumbnail = await VideoThumbnailUtil.generateThumbnail(
|
|
53
|
+
cachedVideoPath,
|
|
54
|
+
{ time: timeMs, quality: 80, cacheId: `thumb-${idx}-${Date.now()}` }
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
if (!thumbnail) throw new Error('Failed to generate thumbnail');
|
|
58
|
+
|
|
59
|
+
const baseName = filename.split('.')[0] || `video-${idx}`;
|
|
60
|
+
const uploaded = await ImageUploadAPI.uploadImageFile(
|
|
61
|
+
{
|
|
62
|
+
path: thumbnail.path,
|
|
63
|
+
filename: `thumb-${baseName}.jpg`,
|
|
64
|
+
size: 0,
|
|
65
|
+
width: thumbnail.width || 320,
|
|
66
|
+
height: thumbnail.height || 180,
|
|
67
|
+
mime: 'image/jpeg',
|
|
68
|
+
},
|
|
69
|
+
() => {}
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
if (!uploaded?.url) throw new Error('No thumbnail URL returned');
|
|
73
|
+
|
|
74
|
+
thumbnails[videoId] = { url: uploaded.url, size: uploaded.size || 0 };
|
|
75
|
+
|
|
76
|
+
// Clean up the local thumbnail file after a successful upload.
|
|
77
|
+
VideoThumbnailUtil.cleanupThumbnail(thumbnail.path).catch((err) =>
|
|
78
|
+
console.warn('[videoThumbnailUpload] Thumbnail cleanup failed:', err)
|
|
79
|
+
);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
82
|
+
console.error('[videoThumbnailUpload] Failed for video:', idx, msg);
|
|
83
|
+
failed.push(vidItem.image.filename || `video-${idx}`);
|
|
84
|
+
// Do NOT insert a poison entry — callers treat a missing key as "no thumbnail".
|
|
85
|
+
} finally {
|
|
86
|
+
if (cachedVideoPath) {
|
|
87
|
+
RNFS.unlink(cachedVideoPath).catch((err) =>
|
|
88
|
+
console.warn('[videoThumbnailUpload] Temp copy cleanup failed:', err)
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (failed.length > 0) {
|
|
95
|
+
UIUtils.toast.open({
|
|
96
|
+
title: 'Thumbnail Generation Failed',
|
|
97
|
+
message: `Failed for: ${failed.join(', ')}. Videos will be sent without thumbnails.`,
|
|
98
|
+
theme: 'warning',
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return thumbnails;
|
|
103
|
+
}
|
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { useCallback, useState } from 'react';
|
|
4
|
-
import RNFS from 'react-native-fs';
|
|
5
|
-
import DeviceUtils from "../utils/device.js";
|
|
6
|
-
import UIUtils from "../utils/ui.js";
|
|
7
|
-
import { trans } from "../translation/index.js";
|
|
8
|
-
import { getMimeType } from "../utils/imageUtils.js";
|
|
9
|
-
import { ImageUploadAPI } from "../services/imageUpload.js";
|
|
10
|
-
import VideoThumbnailUtil from "../utils/videoThumbnail.js";
|
|
11
|
-
export const useVideoAttachment = () => {
|
|
12
|
-
const [selectedVideos, setSelectedVideos] = useState([]);
|
|
13
|
-
const [isVideoPreviewVisible, setIsVideoPreviewVisible] = useState(false);
|
|
14
|
-
const updateVideoStatus = useCallback((index, updates) => {
|
|
15
|
-
setSelectedVideos(prev => {
|
|
16
|
-
const newVideos = [...prev];
|
|
17
|
-
if (newVideos[index]) {
|
|
18
|
-
newVideos[index] = {
|
|
19
|
-
...newVideos[index],
|
|
20
|
-
...updates
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
return newVideos;
|
|
24
|
-
});
|
|
25
|
-
}, []);
|
|
26
|
-
const uploadVideo = useCallback(async (index, video) => {
|
|
27
|
-
console.log('UploadVideo', 'Step 1: Starting upload for video', {
|
|
28
|
-
index
|
|
29
|
-
});
|
|
30
|
-
let cachedFilePath = null;
|
|
31
|
-
try {
|
|
32
|
-
console.log('UploadVideo', 'Step 2: Updating status to uploading...');
|
|
33
|
-
updateVideoStatus(index, {
|
|
34
|
-
isUploading: true,
|
|
35
|
-
error: null,
|
|
36
|
-
uploadProgress: 0
|
|
37
|
-
});
|
|
38
|
-
try {
|
|
39
|
-
console.log('UploadVideo', 'Step 3: Preparing file...', {
|
|
40
|
-
originalPath: video.path,
|
|
41
|
-
filename: video.filename
|
|
42
|
-
});
|
|
43
|
-
const ext = video.filename?.split('.').pop() || 'mp4';
|
|
44
|
-
const fileName = `temp-video-${Date.now()}-${index}.${ext}`;
|
|
45
|
-
cachedFilePath = `${RNFS.DocumentDirectoryPath}/${fileName}`;
|
|
46
|
-
console.log('UploadVideo', 'Step 4: Copying file to cache...', {
|
|
47
|
-
from: video.path,
|
|
48
|
-
to: cachedFilePath
|
|
49
|
-
});
|
|
50
|
-
await RNFS.copyFile(video.path, cachedFilePath);
|
|
51
|
-
console.log('UploadVideo', 'Step 5: File copied successfully');
|
|
52
|
-
} catch (fileError) {
|
|
53
|
-
console.error('UploadVideo', 'File copy error', fileError);
|
|
54
|
-
throw new Error(`Failed to prepare file: ${fileError?.message || 'Unknown error'}`);
|
|
55
|
-
}
|
|
56
|
-
const filename = video.filename || 'video.mp4';
|
|
57
|
-
const setProgress = progress => {
|
|
58
|
-
try {
|
|
59
|
-
console.log('UploadVideo', 'Progress', {
|
|
60
|
-
percentage: progress
|
|
61
|
-
});
|
|
62
|
-
updateVideoStatus(index, {
|
|
63
|
-
uploadProgress: progress
|
|
64
|
-
});
|
|
65
|
-
} catch (err) {
|
|
66
|
-
console.error('UploadVideo', 'Progress update error', err);
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
let uploadedVideo;
|
|
70
|
-
try {
|
|
71
|
-
console.log('UploadVideo', 'Step 6: Uploading to server...', {
|
|
72
|
-
filename,
|
|
73
|
-
size: video.size,
|
|
74
|
-
duration: video.duration
|
|
75
|
-
});
|
|
76
|
-
uploadedVideo = await ImageUploadAPI.uploadImageFile({
|
|
77
|
-
path: cachedFilePath || '',
|
|
78
|
-
filename,
|
|
79
|
-
size: video.size || 0,
|
|
80
|
-
width: video.width || 0,
|
|
81
|
-
height: video.height || 0,
|
|
82
|
-
mime: getMimeType(filename)
|
|
83
|
-
}, setProgress);
|
|
84
|
-
console.log('UploadVideo', 'Step 7: Upload successful!', {
|
|
85
|
-
url: uploadedVideo?.url
|
|
86
|
-
});
|
|
87
|
-
} catch (uploadError) {
|
|
88
|
-
console.error('UploadVideo', 'Server upload error', uploadError);
|
|
89
|
-
throw new Error(`Upload failed: ${uploadError?.message || 'Unknown error'}`);
|
|
90
|
-
}
|
|
91
|
-
if (!uploadedVideo?.url) {
|
|
92
|
-
throw new Error('No URL returned from server');
|
|
93
|
-
}
|
|
94
|
-
console.log('UploadVideo', 'Step 8: Video upload complete');
|
|
95
|
-
|
|
96
|
-
// Step 8.5: Generate and upload thumbnail (REQUIRED before ready to send)
|
|
97
|
-
let thumbnailUrl;
|
|
98
|
-
let thumbnailPath;
|
|
99
|
-
let thumbnailSize;
|
|
100
|
-
try {
|
|
101
|
-
console.log('UploadVideo', 'Step 8.5a: Generating thumbnail...');
|
|
102
|
-
const thumbnail = await VideoThumbnailUtil.generateThumbnail(video.path, {
|
|
103
|
-
time: 1000,
|
|
104
|
-
quality: 80
|
|
105
|
-
} // time in milliseconds (1 second)
|
|
106
|
-
);
|
|
107
|
-
if (!thumbnail) {
|
|
108
|
-
throw new Error('Failed to generate thumbnail from video');
|
|
109
|
-
}
|
|
110
|
-
thumbnailPath = thumbnail.path;
|
|
111
|
-
console.log('✅ [UploadVideo] THUMBNAIL PATH:', thumbnail.path);
|
|
112
|
-
console.log('UploadVideo', 'Step 8.5b: Thumbnail generated', {
|
|
113
|
-
path: thumbnail.path,
|
|
114
|
-
width: thumbnail.width,
|
|
115
|
-
height: thumbnail.height
|
|
116
|
-
});
|
|
117
|
-
console.log('UploadVideo', 'Step 8.5c: Uploading thumbnail...');
|
|
118
|
-
const uploadedThumbnail = await ImageUploadAPI.uploadImageFile({
|
|
119
|
-
path: thumbnail.path,
|
|
120
|
-
filename: `thumb-${filename}`,
|
|
121
|
-
size: 0,
|
|
122
|
-
width: thumbnail.width || 320,
|
|
123
|
-
height: thumbnail.height || 180,
|
|
124
|
-
mime: 'image/jpeg'
|
|
125
|
-
}, progress => {
|
|
126
|
-
console.log('UploadVideo', 'Thumbnail upload progress', {
|
|
127
|
-
percentage: progress
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
if (!uploadedThumbnail?.url) {
|
|
131
|
-
throw new Error('Thumbnail URL not returned from server');
|
|
132
|
-
}
|
|
133
|
-
thumbnailUrl = uploadedThumbnail.url;
|
|
134
|
-
thumbnailSize = uploadedThumbnail.size || 0;
|
|
135
|
-
console.log('✅ [UploadVideo] THUMBNAIL URL READY:', thumbnailUrl);
|
|
136
|
-
console.log('UploadVideo', 'Step 8.5d: Thumbnail uploaded', {
|
|
137
|
-
url: thumbnailUrl,
|
|
138
|
-
size: `${thumbnailSize} bytes`
|
|
139
|
-
});
|
|
140
|
-
} catch (thumbError) {
|
|
141
|
-
const errorMsg = thumbError?.message || 'Failed to generate/upload thumbnail';
|
|
142
|
-
console.error('❌ [UploadVideo] THUMBNAIL ERROR:', errorMsg);
|
|
143
|
-
throw new Error(errorMsg);
|
|
144
|
-
}
|
|
145
|
-
console.log('UploadVideo', 'Step 9: Preparing data...');
|
|
146
|
-
updateVideoStatus(index, {
|
|
147
|
-
isUploading: false,
|
|
148
|
-
uploadProgress: 100,
|
|
149
|
-
uploadedUrl: uploadedVideo.url,
|
|
150
|
-
thumbnailUrl,
|
|
151
|
-
thumbnailPath,
|
|
152
|
-
thumbnailSize
|
|
153
|
-
});
|
|
154
|
-
console.log('UploadVideo', 'Step 10: Ready to send - waiting for user confirmation');
|
|
155
|
-
console.log('UploadVideo', 'Step 11: Cleaning up cache files...');
|
|
156
|
-
try {
|
|
157
|
-
if (cachedFilePath) {
|
|
158
|
-
await ImageUploadAPI.cleanupImageFile(cachedFilePath);
|
|
159
|
-
}
|
|
160
|
-
if (thumbnailPath) {
|
|
161
|
-
await VideoThumbnailUtil.cleanupThumbnail(thumbnailPath);
|
|
162
|
-
}
|
|
163
|
-
} catch (cleanupError) {
|
|
164
|
-
console.warn('UploadVideo', 'Cleanup warning', cleanupError);
|
|
165
|
-
}
|
|
166
|
-
console.log('UploadVideo', 'Step 12: COMPLETE ✓');
|
|
167
|
-
} catch (error) {
|
|
168
|
-
console.error('UploadVideo', 'ERROR', error);
|
|
169
|
-
const errorMsg = error?.message || trans('chat:video_upload_failed');
|
|
170
|
-
updateVideoStatus(index, {
|
|
171
|
-
isUploading: false,
|
|
172
|
-
error: errorMsg
|
|
173
|
-
});
|
|
174
|
-
try {
|
|
175
|
-
if (cachedFilePath) {
|
|
176
|
-
await ImageUploadAPI.cleanupImageFile(cachedFilePath);
|
|
177
|
-
}
|
|
178
|
-
} catch (cleanupError) {
|
|
179
|
-
console.warn('UploadVideo', 'Cleanup after error', cleanupError);
|
|
180
|
-
}
|
|
181
|
-
console.error('uploadVideo', 'Full error', error);
|
|
182
|
-
}
|
|
183
|
-
}, [updateVideoStatus]);
|
|
184
|
-
const handleVideoAttach = useCallback(async () => {
|
|
185
|
-
console.log('VideoAttach', 'Step 1: User tapped video attachment button');
|
|
186
|
-
try {
|
|
187
|
-
console.log('VideoAttach', 'Step 2: Opening video picker...');
|
|
188
|
-
const videos = await DeviceUtils.openVideoPicker();
|
|
189
|
-
if (!videos || videos.length === 0) {
|
|
190
|
-
console.log('VideoAttach', 'Step 3: User cancelled picker');
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
console.log('VideoAttach', 'Step 4: Videos picked', {
|
|
194
|
-
count: videos.length
|
|
195
|
-
});
|
|
196
|
-
const MAX_FILE_SIZE = 200 * 1024 * 1024; // 200MB for videos
|
|
197
|
-
const oversizedVideos = videos.filter(vid => (vid.size || 0) > MAX_FILE_SIZE);
|
|
198
|
-
if (oversizedVideos.length > 0) {
|
|
199
|
-
console.warn('VideoAttach', 'Step 5: Some videos too large', {
|
|
200
|
-
count: oversizedVideos.length,
|
|
201
|
-
maxSize: MAX_FILE_SIZE
|
|
202
|
-
});
|
|
203
|
-
UIUtils.toast.open({
|
|
204
|
-
title: trans('chat:video_too_large'),
|
|
205
|
-
theme: 'danger'
|
|
206
|
-
});
|
|
207
|
-
const validVideos = videos.filter(vid => (vid.size || 0) <= MAX_FILE_SIZE);
|
|
208
|
-
if (validVideos.length === 0) return;
|
|
209
|
-
videos.splice(0, videos.length, ...validVideos);
|
|
210
|
-
}
|
|
211
|
-
console.log('VideoAttach', 'Step 6: Size validation passed');
|
|
212
|
-
const startIndex = selectedVideos.length;
|
|
213
|
-
console.log('VideoAttach', 'Step 7: Adding to preview list', {
|
|
214
|
-
count: videos.length,
|
|
215
|
-
startIndex
|
|
216
|
-
});
|
|
217
|
-
setSelectedVideos(prev => [...prev, ...videos.map(video => ({
|
|
218
|
-
type: 'video',
|
|
219
|
-
video,
|
|
220
|
-
uploadProgress: 0,
|
|
221
|
-
error: null,
|
|
222
|
-
isUploading: true
|
|
223
|
-
}))]);
|
|
224
|
-
setIsVideoPreviewVisible(true);
|
|
225
|
-
console.log('VideoAttach', 'Step 8: Starting uploads...');
|
|
226
|
-
await Promise.all(videos.map((video, offset) => uploadVideo(startIndex + offset, video)));
|
|
227
|
-
} catch (error) {
|
|
228
|
-
console.error('VideoAttach', 'ERROR', error);
|
|
229
|
-
UIUtils.toast.open({
|
|
230
|
-
title: trans('chat:video_pick_failed'),
|
|
231
|
-
message: error instanceof Error ? error.message : 'Unknown error',
|
|
232
|
-
theme: 'danger'
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
}, [selectedVideos.length, uploadVideo]);
|
|
236
|
-
const handleVideoRemove = useCallback(index => {
|
|
237
|
-
setSelectedVideos(prev => prev.filter((_, i) => i !== index));
|
|
238
|
-
}, []);
|
|
239
|
-
const clearAllVideos = useCallback(() => {
|
|
240
|
-
setSelectedVideos([]);
|
|
241
|
-
setIsVideoPreviewVisible(false);
|
|
242
|
-
}, []);
|
|
243
|
-
return {
|
|
244
|
-
selectedVideos,
|
|
245
|
-
isVideoPreviewVisible,
|
|
246
|
-
handleVideoAttach,
|
|
247
|
-
handleVideoRemove,
|
|
248
|
-
clearAllVideos
|
|
249
|
-
};
|
|
250
|
-
};
|
|
251
|
-
//# sourceMappingURL=useVideoAttachment.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["useCallback","useState","RNFS","DeviceUtils","UIUtils","trans","getMimeType","ImageUploadAPI","VideoThumbnailUtil","useVideoAttachment","selectedVideos","setSelectedVideos","isVideoPreviewVisible","setIsVideoPreviewVisible","updateVideoStatus","index","updates","prev","newVideos","uploadVideo","video","console","log","cachedFilePath","isUploading","error","uploadProgress","originalPath","path","filename","ext","split","pop","fileName","Date","now","DocumentDirectoryPath","from","to","copyFile","fileError","Error","message","setProgress","progress","percentage","err","uploadedVideo","size","duration","uploadImageFile","width","height","mime","url","uploadError","thumbnailUrl","thumbnailPath","thumbnailSize","thumbnail","generateThumbnail","time","quality","uploadedThumbnail","thumbError","errorMsg","uploadedUrl","cleanupImageFile","cleanupThumbnail","cleanupError","warn","handleVideoAttach","videos","openVideoPicker","length","count","MAX_FILE_SIZE","oversizedVideos","filter","vid","maxSize","toast","open","title","theme","validVideos","splice","startIndex","map","type","Promise","all","offset","handleVideoRemove","_","i","clearAllVideos"],"sourceRoot":"../../../src","sources":["hooks/useVideoAttachment.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { useCallback } from 'react';
|
|
4
|
-
import RNFS from 'react-native-fs';
|
|
5
|
-
import { isVideoFromPicker } from "../../../utils/imageUtils.js";
|
|
6
|
-
import VideoThumbnailUtil from "../../../utils/videoThumbnail.js";
|
|
7
|
-
import { ImageUploadAPI } from "../../../services/imageUpload.js";
|
|
8
|
-
import UIUtils from "../../../utils/ui.js";
|
|
9
|
-
export const useAttachmentSendHandler = ({
|
|
10
|
-
selectedImages,
|
|
11
|
-
selectedFiles,
|
|
12
|
-
sendAttachments,
|
|
13
|
-
clearAllImages,
|
|
14
|
-
clearAllFiles,
|
|
15
|
-
onSend,
|
|
16
|
-
onMessageSent,
|
|
17
|
-
textMessage,
|
|
18
|
-
urlMetadata,
|
|
19
|
-
onSendText
|
|
20
|
-
}) => {
|
|
21
|
-
const handleSend = useCallback(async () => {
|
|
22
|
-
console.log('[ChatComposer] handleSend called', {
|
|
23
|
-
selectedImagesCount: selectedImages.length,
|
|
24
|
-
selectedImagesData: selectedImages.map(img => ({
|
|
25
|
-
filename: img.image.filename,
|
|
26
|
-
isUploading: img.isUploading,
|
|
27
|
-
uploadedUrl: img.uploadedUrl ? 'SET' : 'MISSING',
|
|
28
|
-
uploadProgress: img.uploadProgress
|
|
29
|
-
}))
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
// Separate images and videos from selectedImages
|
|
33
|
-
const actualImages = selectedImages.filter(img => !isVideoFromPicker(img.image));
|
|
34
|
-
const videosFromGallery = selectedImages.filter(img => isVideoFromPicker(img.image));
|
|
35
|
-
console.log('🎬 [ChatComposer] Separation result:', {
|
|
36
|
-
actualImagesCount: actualImages.length,
|
|
37
|
-
videosFromGalleryCount: videosFromGallery.length,
|
|
38
|
-
videosFromGalleryList: videosFromGallery.map(v => ({
|
|
39
|
-
filename: v.image.filename,
|
|
40
|
-
uploadedUrl: v.uploadedUrl
|
|
41
|
-
}))
|
|
42
|
-
});
|
|
43
|
-
const uploadedImages = actualImages.filter(img => img.uploadedUrl);
|
|
44
|
-
const uploadedVideosFromGallery = videosFromGallery.filter(vid => vid.uploadedUrl);
|
|
45
|
-
|
|
46
|
-
// Auto-generate thumbnails for gallery videos
|
|
47
|
-
// Use unique ID per video to avoid race conditions and path mismatches
|
|
48
|
-
const galleryVideoThumbnails = {};
|
|
49
|
-
const failedVideos = []; // Track failed videos
|
|
50
|
-
|
|
51
|
-
if (uploadedVideosFromGallery.length > 0) {
|
|
52
|
-
console.log('[ChatComposer] Auto-generating thumbnails for gallery videos...');
|
|
53
|
-
for (let idx = 0; idx < uploadedVideosFromGallery.length; idx++) {
|
|
54
|
-
const vidItem = uploadedVideosFromGallery[idx];
|
|
55
|
-
const videoId = vidItem.uploadedUrl || `video-${idx}`; // Use unique ID instead of path
|
|
56
|
-
let cachedVideoPath = null;
|
|
57
|
-
try {
|
|
58
|
-
const originalVideoPath = vidItem.image.path;
|
|
59
|
-
let videoDuration = vidItem.image.duration || 0;
|
|
60
|
-
const videoMime = vidItem.image.mime;
|
|
61
|
-
const filename = vidItem.image.filename || 'video.mp4';
|
|
62
|
-
console.log('[ChatComposer] Generating thumbnail for video:', {
|
|
63
|
-
index: idx,
|
|
64
|
-
filename,
|
|
65
|
-
originalVideoPath,
|
|
66
|
-
videoDuration,
|
|
67
|
-
videoMime,
|
|
68
|
-
uploadedUrl: vidItem.uploadedUrl
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
// ✅ FIX: Validate duration is reasonable
|
|
72
|
-
if (videoDuration <= 0 || videoDuration > 3600000) {
|
|
73
|
-
// Duration should be between 0 and 1 hour (3600000ms)
|
|
74
|
-
console.warn('[ChatComposer] Invalid duration, using default:', videoDuration);
|
|
75
|
-
videoDuration = 5000; // Default 5 seconds
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Copy video to persistent location
|
|
79
|
-
const ext = filename.split('.').pop() || 'mp4';
|
|
80
|
-
// ✅ FIX: Use unique ID to avoid race condition
|
|
81
|
-
const cachedFileName = `thumb-cache-${idx}-${Date.now()}-${Math.random().toString(36).slice(2)}.${ext}`;
|
|
82
|
-
cachedVideoPath = `${RNFS.DocumentDirectoryPath}/${cachedFileName}`;
|
|
83
|
-
console.log('[ChatComposer] Copying video to cache:', {
|
|
84
|
-
from: originalVideoPath,
|
|
85
|
-
to: cachedVideoPath
|
|
86
|
-
});
|
|
87
|
-
await RNFS.copyFile(originalVideoPath, cachedVideoPath);
|
|
88
|
-
console.log('[ChatComposer] Video cached successfully');
|
|
89
|
-
|
|
90
|
-
// ✅ FIX: Choose frame at 30-40% for better representation
|
|
91
|
-
// (not exactly middle, to avoid black frames/transitions)
|
|
92
|
-
const timeMs = Math.min(Math.max(1000, Math.floor(videoDuration * 0.35)), videoDuration - 1000);
|
|
93
|
-
console.log('[ChatComposer] Thumbnail settings for video:', {
|
|
94
|
-
index: idx,
|
|
95
|
-
videoDuration,
|
|
96
|
-
timeMs,
|
|
97
|
-
quality: 80,
|
|
98
|
-
videoPath: cachedVideoPath
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
// Generate thumbnail from cached copy
|
|
102
|
-
const thumbnail = await VideoThumbnailUtil.generateThumbnail(cachedVideoPath, {
|
|
103
|
-
time: timeMs,
|
|
104
|
-
quality: 80,
|
|
105
|
-
cacheId: `thumb-${idx}-${Date.now()}`
|
|
106
|
-
});
|
|
107
|
-
if (!thumbnail) {
|
|
108
|
-
throw new Error('Failed to generate thumbnail');
|
|
109
|
-
}
|
|
110
|
-
console.log('[ChatComposer] Thumbnail generated for video:', {
|
|
111
|
-
index: idx,
|
|
112
|
-
path: thumbnail.path,
|
|
113
|
-
width: thumbnail.width,
|
|
114
|
-
height: thumbnail.height
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
// Upload thumbnail
|
|
118
|
-
const baseName = vidItem.image.filename?.split('.')[0] || `video-${idx}`;
|
|
119
|
-
const uploadedThumbnail = await ImageUploadAPI.uploadImageFile({
|
|
120
|
-
path: thumbnail.path,
|
|
121
|
-
filename: `thumb-${baseName}.jpg`,
|
|
122
|
-
size: 0,
|
|
123
|
-
width: thumbnail.width || 320,
|
|
124
|
-
height: thumbnail.height || 180,
|
|
125
|
-
mime: 'image/jpeg'
|
|
126
|
-
}, progress => {
|
|
127
|
-
console.log('[ChatComposer] Thumbnail upload progress:', {
|
|
128
|
-
videoIndex: idx,
|
|
129
|
-
percentage: progress
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
if (!uploadedThumbnail?.url) {
|
|
133
|
-
throw new Error('No thumbnail URL returned');
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// ✅ FIX: Store with unique ID instead of path
|
|
137
|
-
galleryVideoThumbnails[videoId] = {
|
|
138
|
-
url: uploadedThumbnail.url,
|
|
139
|
-
size: uploadedThumbnail.size || 0
|
|
140
|
-
};
|
|
141
|
-
console.log('[ChatComposer] Thumbnail ready for video:', {
|
|
142
|
-
videoIndex: idx,
|
|
143
|
-
videoId,
|
|
144
|
-
url: uploadedThumbnail.url,
|
|
145
|
-
size: uploadedThumbnail.size
|
|
146
|
-
});
|
|
147
|
-
console.log(`✅ [ChatComposer] Video #${idx} THUMBNAIL URL: ${uploadedThumbnail.url}`);
|
|
148
|
-
} catch (error) {
|
|
149
|
-
// ✅ FIX: Skip failed video instead of aborting all
|
|
150
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
151
|
-
console.error('[ChatComposer] Thumbnail generation error for video:', idx, errorMsg);
|
|
152
|
-
failedVideos.push(vidItem.image.filename);
|
|
153
|
-
|
|
154
|
-
// Mark this video as failed (no thumbnail)
|
|
155
|
-
galleryVideoThumbnails[videoId] = {
|
|
156
|
-
url: '',
|
|
157
|
-
size: 0
|
|
158
|
-
};
|
|
159
|
-
} finally {
|
|
160
|
-
// Cleanup cached video file
|
|
161
|
-
if (cachedVideoPath) {
|
|
162
|
-
try {
|
|
163
|
-
await RNFS.unlink(cachedVideoPath);
|
|
164
|
-
console.log('[ChatComposer] Cleaned up cached video:', {
|
|
165
|
-
videoIndex: idx,
|
|
166
|
-
path: cachedVideoPath
|
|
167
|
-
});
|
|
168
|
-
} catch (err) {
|
|
169
|
-
console.warn('[ChatComposer] Failed to clean cached video:', err);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Show error about failed videos if any
|
|
176
|
-
if (failedVideos.length > 0) {
|
|
177
|
-
UIUtils.toast.open({
|
|
178
|
-
title: 'Thumbnail Generation Failed',
|
|
179
|
-
message: `Failed for: ${failedVideos.join(', ')}. Videos will be sent without thumbnails.`,
|
|
180
|
-
theme: 'warning'
|
|
181
|
-
});
|
|
182
|
-
console.warn('[ChatComposer] Failed to generate thumbnails for:', failedVideos);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
const allUploadedVideos = [...uploadedVideosFromGallery];
|
|
186
|
-
console.log('[ChatComposer] handleSend summary:', {
|
|
187
|
-
actualImages: actualImages.length,
|
|
188
|
-
uploadedImages: uploadedImages.length,
|
|
189
|
-
videosFromGallery: videosFromGallery.length,
|
|
190
|
-
uploadedVideosFromGallery: uploadedVideosFromGallery.length,
|
|
191
|
-
allUploadedVideos: allUploadedVideos.length
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
// Log thumbnail URLs
|
|
195
|
-
if (uploadedVideosFromGallery.length > 0) {
|
|
196
|
-
uploadedVideosFromGallery.forEach((vid, vidIdx) => {
|
|
197
|
-
// ✅ Use same key as storage: uploadedUrl or video-${idx}
|
|
198
|
-
const videoId = vid.uploadedUrl || `video-${vidIdx}`;
|
|
199
|
-
const thumbnail = galleryVideoThumbnails[videoId];
|
|
200
|
-
console.log(`✅ [ChatComposer] Gallery Video Thumbnail:`, {
|
|
201
|
-
filename: vid.image.filename,
|
|
202
|
-
videoId,
|
|
203
|
-
thumbnailUrl: thumbnail?.url || 'NOT FOUND',
|
|
204
|
-
videoUrl: vid.uploadedUrl
|
|
205
|
-
});
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Send images and videos if any
|
|
210
|
-
if (uploadedImages.length > 0 || allUploadedVideos.length > 0) {
|
|
211
|
-
const attachmentItems = [...uploadedImages.map(img => ({
|
|
212
|
-
...img,
|
|
213
|
-
type: 'image'
|
|
214
|
-
})),
|
|
215
|
-
// Videos from gallery picker with auto-generated thumbnails
|
|
216
|
-
...uploadedVideosFromGallery.map((vid, vidIdx) => {
|
|
217
|
-
// ✅ Use same key as storage: uploadedUrl or video-${idx}
|
|
218
|
-
const videoId = vid.uploadedUrl || `video-${vidIdx}`;
|
|
219
|
-
const thumbnail = galleryVideoThumbnails[videoId];
|
|
220
|
-
return {
|
|
221
|
-
type: 'video',
|
|
222
|
-
video: vid.image,
|
|
223
|
-
uploadProgress: vid.uploadProgress,
|
|
224
|
-
error: vid.error,
|
|
225
|
-
isUploading: vid.isUploading,
|
|
226
|
-
uploadedUrl: vid.uploadedUrl,
|
|
227
|
-
thumbnailUrl: thumbnail?.url,
|
|
228
|
-
thumbnailSize: thumbnail?.size
|
|
229
|
-
};
|
|
230
|
-
})];
|
|
231
|
-
console.log('[ChatComposer] Sending attachments...', {
|
|
232
|
-
imagesCount: uploadedImages.length,
|
|
233
|
-
videosCount: uploadedVideosFromGallery.length,
|
|
234
|
-
hasTextMessage: !!textMessage
|
|
235
|
-
});
|
|
236
|
-
const success = await sendAttachments(attachmentItems, undefined, onMessageSent);
|
|
237
|
-
console.log('[ChatComposer] Attachments send result:', {
|
|
238
|
-
success
|
|
239
|
-
});
|
|
240
|
-
if (success) {
|
|
241
|
-
clearAllImages();
|
|
242
|
-
console.log('[ChatComposer] Images cleared after successful send');
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// Send each file as its own individual message
|
|
247
|
-
const uploadedFiles = selectedFiles.filter(f => f.uploadedUrl && !f.error);
|
|
248
|
-
if (uploadedFiles.length > 0) {
|
|
249
|
-
console.log('[ChatComposer] Sending files individually:', {
|
|
250
|
-
count: uploadedFiles.length
|
|
251
|
-
});
|
|
252
|
-
for (const file of uploadedFiles) {
|
|
253
|
-
await sendAttachments([{
|
|
254
|
-
type: 'file',
|
|
255
|
-
file: {
|
|
256
|
-
path: file.uri,
|
|
257
|
-
filename: file.name,
|
|
258
|
-
size: file.size
|
|
259
|
-
},
|
|
260
|
-
uploadProgress: 100,
|
|
261
|
-
error: null,
|
|
262
|
-
isUploading: false,
|
|
263
|
-
uploadedUrl: file.uploadedUrl
|
|
264
|
-
}], undefined, onMessageSent);
|
|
265
|
-
}
|
|
266
|
-
clearAllFiles?.();
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// Send text last
|
|
270
|
-
if (textMessage && onSendText) {
|
|
271
|
-
console.log('[ChatComposer] Sending text message after attachments', {
|
|
272
|
-
hasPreview: !!urlMetadata,
|
|
273
|
-
previewUrl: urlMetadata?.url
|
|
274
|
-
});
|
|
275
|
-
onSendText(textMessage, urlMetadata);
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
const hasAnySent = uploadedImages.length > 0 || allUploadedVideos.length > 0 || uploadedFiles.length > 0;
|
|
279
|
-
if (!hasAnySent) {
|
|
280
|
-
onSend?.();
|
|
281
|
-
}
|
|
282
|
-
return;
|
|
283
|
-
}, [selectedImages, selectedFiles, sendAttachments, clearAllImages, clearAllFiles, onSend, onMessageSent, textMessage, urlMetadata, onSendText]);
|
|
284
|
-
return {
|
|
285
|
-
handleSend
|
|
286
|
-
};
|
|
287
|
-
};
|
|
288
|
-
//# sourceMappingURL=useAttachmentSendHandler.js.map
|