@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.
Files changed (194) hide show
  1. package/lib/module/assets/images/btn_resend.png +0 -0
  2. package/lib/module/assets/images/index.js +2 -1
  3. package/lib/module/assets/images/index.js.map +1 -1
  4. package/lib/module/components/AttachmentPreview/FilePreview.js +1 -1
  5. package/lib/module/components/AttachmentPreview/FilePreview.js.map +1 -1
  6. package/lib/module/components/messages/MessageStatusIndicator.js +70 -0
  7. package/lib/module/components/messages/MessageStatusIndicator.js.map +1 -0
  8. package/lib/module/components/messages/fileMessage/index.js.map +1 -1
  9. package/lib/module/components/messages/imageMessage/index.js +59 -50
  10. package/lib/module/components/messages/imageMessage/index.js.map +1 -1
  11. package/lib/module/components/messages/linkMessage/index.js +14 -6
  12. package/lib/module/components/messages/linkMessage/index.js.map +1 -1
  13. package/lib/module/components/messages/mergedMessage/index.js.map +1 -1
  14. package/lib/module/components/messages/textMessage/index.js +14 -6
  15. package/lib/module/components/messages/textMessage/index.js.map +1 -1
  16. package/lib/module/components/messages/videoMessage/index.js.map +1 -1
  17. package/lib/module/config/uploadLimits.js +13 -0
  18. package/lib/module/config/uploadLimits.js.map +1 -0
  19. package/lib/module/core/useChatListener.js +35 -4
  20. package/lib/module/core/useChatListener.js.map +1 -1
  21. package/lib/module/core/useUserListener.js +17 -7
  22. package/lib/module/core/useUserListener.js.map +1 -1
  23. package/lib/module/hooks/message/useSendMessage.js +62 -15
  24. package/lib/module/hooks/message/useSendMessage.js.map +1 -1
  25. package/lib/module/hooks/useChatCompose.js +153 -0
  26. package/lib/module/hooks/useChatCompose.js.map +1 -0
  27. package/lib/module/hooks/useChatMessages.js +39 -8
  28. package/lib/module/hooks/useChatMessages.js.map +1 -1
  29. package/lib/module/hooks/useFileAttachment.js +1 -1
  30. package/lib/module/hooks/useFileAttachment.js.map +1 -1
  31. package/lib/module/hooks/useFileDownload.js +1 -1
  32. package/lib/module/hooks/useFileDownload.js.map +1 -1
  33. package/lib/module/hooks/useImageAttachment.js +81 -152
  34. package/lib/module/hooks/useImageAttachment.js.map +1 -1
  35. package/lib/module/hooks/useSendAttachment.js +2 -2
  36. package/lib/module/hooks/useSendAttachment.js.map +1 -1
  37. package/lib/module/index.js +1 -1
  38. package/lib/module/index.js.map +1 -1
  39. package/lib/module/screens/chat-detail/ChatComposer.js +50 -185
  40. package/lib/module/screens/chat-detail/ChatComposer.js.map +1 -1
  41. package/lib/module/screens/chat-detail/ChatDetail.js +29 -137
  42. package/lib/module/screens/chat-detail/ChatDetail.js.map +1 -1
  43. package/lib/module/screens/chat-detail/ChatListLegend.js +20 -15
  44. package/lib/module/screens/chat-detail/ChatListLegend.js.map +1 -1
  45. package/lib/module/screens/chat-detail/index.js +0 -1
  46. package/lib/module/screens/chat-detail/index.js.map +1 -1
  47. package/lib/module/screens/chat-detail/legend/LegendChatMessage.js +18 -13
  48. package/lib/module/screens/chat-detail/legend/LegendChatMessage.js.map +1 -1
  49. package/lib/module/services/attachmentHandlers/fileAttachmentHandler.js +2 -2
  50. package/lib/module/services/attachmentHandlers/fileAttachmentHandler.js.map +1 -1
  51. package/lib/module/services/attachmentHandlers/imageAttachmentHandler.js +3 -3
  52. package/lib/module/services/attachmentHandlers/imageAttachmentHandler.js.map +1 -1
  53. package/lib/module/services/attachmentHandlers/videoAttachmentHandler.js +2 -1
  54. package/lib/module/services/attachmentHandlers/videoAttachmentHandler.js.map +1 -1
  55. package/lib/module/services/attachmentOrchestrator.js +40 -17
  56. package/lib/module/services/attachmentOrchestrator.js.map +1 -1
  57. package/lib/module/services/message.js +13 -5
  58. package/lib/module/services/message.js.map +1 -1
  59. package/lib/module/translation/resources/i18n.js +4 -3
  60. package/lib/module/translation/resources/i18n.js.map +1 -1
  61. package/lib/module/types/chat.js.map +1 -1
  62. package/lib/module/types/global.d.js +2 -0
  63. package/lib/module/types/global.d.js.map +1 -0
  64. package/lib/module/types/index.d.js +3 -0
  65. package/lib/module/types/index.d.js.map +1 -0
  66. package/lib/module/utils/UIUtils.js +37 -0
  67. package/lib/module/utils/UIUtils.js.map +1 -0
  68. package/lib/module/utils/imageUtils.js +3 -3
  69. package/lib/module/utils/imageUtils.js.map +1 -1
  70. package/lib/module/utils/pLimit.js +19 -0
  71. package/lib/module/utils/pLimit.js.map +1 -0
  72. package/lib/module/utils/videoThumbnail.js +8 -3
  73. package/lib/module/utils/videoThumbnail.js.map +1 -1
  74. package/lib/module/utils/videoThumbnailUpload.js +76 -0
  75. package/lib/module/utils/videoThumbnailUpload.js.map +1 -0
  76. package/lib/typescript/src/assets/images/index.d.ts +1 -0
  77. package/lib/typescript/src/assets/images/index.d.ts.map +1 -1
  78. package/lib/typescript/src/components/messages/MessageStatusIndicator.d.ts +9 -0
  79. package/lib/typescript/src/components/messages/MessageStatusIndicator.d.ts.map +1 -0
  80. package/lib/typescript/src/components/messages/fileMessage/index.d.ts.map +1 -1
  81. package/lib/typescript/src/components/messages/imageMessage/index.d.ts.map +1 -1
  82. package/lib/typescript/src/components/messages/linkMessage/index.d.ts +3 -8
  83. package/lib/typescript/src/components/messages/linkMessage/index.d.ts.map +1 -1
  84. package/lib/typescript/src/components/messages/mergedMessage/index.d.ts.map +1 -1
  85. package/lib/typescript/src/components/messages/textMessage/index.d.ts +2 -8
  86. package/lib/typescript/src/components/messages/textMessage/index.d.ts.map +1 -1
  87. package/lib/typescript/src/components/messages/videoMessage/index.d.ts.map +1 -1
  88. package/lib/typescript/src/config/uploadLimits.d.ts +7 -0
  89. package/lib/typescript/src/config/uploadLimits.d.ts.map +1 -0
  90. package/lib/typescript/src/core/useChatListener.d.ts.map +1 -1
  91. package/lib/typescript/src/core/useUserListener.d.ts.map +1 -1
  92. package/lib/typescript/src/hooks/message/useSendMessage.d.ts +2 -0
  93. package/lib/typescript/src/hooks/message/useSendMessage.d.ts.map +1 -1
  94. package/lib/typescript/src/hooks/useChatCompose.d.ts +27 -0
  95. package/lib/typescript/src/hooks/useChatCompose.d.ts.map +1 -0
  96. package/lib/typescript/src/hooks/useChatMessages.d.ts.map +1 -1
  97. package/lib/typescript/src/hooks/useImageAttachment.d.ts +2 -2
  98. package/lib/typescript/src/hooks/useImageAttachment.d.ts.map +1 -1
  99. package/lib/typescript/src/hooks/useSendAttachment.d.ts +1 -1
  100. package/lib/typescript/src/hooks/useSendAttachment.d.ts.map +1 -1
  101. package/lib/typescript/src/index.d.ts +1 -2
  102. package/lib/typescript/src/index.d.ts.map +1 -1
  103. package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts +5 -2
  104. package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts.map +1 -1
  105. package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts +1 -1
  106. package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts.map +1 -1
  107. package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts +1 -1
  108. package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts.map +1 -1
  109. package/lib/typescript/src/screens/chat-detail/index.d.ts +0 -2
  110. package/lib/typescript/src/screens/chat-detail/index.d.ts.map +1 -1
  111. package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts +3 -5
  112. package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts.map +1 -1
  113. package/lib/typescript/src/screens/chat-detail/types.d.ts +1 -35
  114. package/lib/typescript/src/screens/chat-detail/types.d.ts.map +1 -1
  115. package/lib/typescript/src/services/attachmentHandlers/fileAttachmentHandler.d.ts.map +1 -1
  116. package/lib/typescript/src/services/attachmentHandlers/imageAttachmentHandler.d.ts.map +1 -1
  117. package/lib/typescript/src/services/attachmentHandlers/videoAttachmentHandler.d.ts.map +1 -1
  118. package/lib/typescript/src/services/attachmentOrchestrator.d.ts +1 -1
  119. package/lib/typescript/src/services/attachmentOrchestrator.d.ts.map +1 -1
  120. package/lib/typescript/src/services/message.d.ts +7 -0
  121. package/lib/typescript/src/services/message.d.ts.map +1 -1
  122. package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -1
  123. package/lib/typescript/src/types/chat.d.ts +11 -0
  124. package/lib/typescript/src/types/chat.d.ts.map +1 -1
  125. package/lib/typescript/src/utils/UIUtils.d.ts +4 -0
  126. package/lib/typescript/src/utils/UIUtils.d.ts.map +1 -0
  127. package/lib/typescript/src/utils/imageUtils.d.ts.map +1 -1
  128. package/lib/typescript/src/utils/pLimit.d.ts +6 -0
  129. package/lib/typescript/src/utils/pLimit.d.ts.map +1 -0
  130. package/lib/typescript/src/utils/videoThumbnail.d.ts.map +1 -1
  131. package/lib/typescript/src/utils/videoThumbnailUpload.d.ts +11 -0
  132. package/lib/typescript/src/utils/videoThumbnailUpload.d.ts.map +1 -0
  133. package/package.json +2 -2
  134. package/src/assets/images/btn_resend.png +0 -0
  135. package/src/assets/images/index.ts +1 -0
  136. package/src/components/AttachmentPreview/FilePreview.tsx +1 -1
  137. package/src/components/messages/MessageStatusIndicator.tsx +82 -0
  138. package/src/components/messages/fileMessage/index.tsx +0 -1
  139. package/src/components/messages/imageMessage/index.tsx +71 -57
  140. package/src/components/messages/linkMessage/index.tsx +13 -19
  141. package/src/components/messages/mergedMessage/index.tsx +0 -1
  142. package/src/components/messages/textMessage/index.tsx +13 -18
  143. package/src/components/messages/videoMessage/index.tsx +0 -1
  144. package/src/config/uploadLimits.ts +11 -0
  145. package/src/core/useChatListener.ts +38 -4
  146. package/src/core/useUserListener.ts +17 -7
  147. package/src/hooks/message/useSendMessage.ts +103 -24
  148. package/src/hooks/useChatCompose.ts +195 -0
  149. package/src/hooks/useChatMessages.ts +54 -10
  150. package/src/hooks/useFileAttachment.ts +1 -1
  151. package/src/hooks/useFileDownload.ts +1 -1
  152. package/src/hooks/useImageAttachment.ts +120 -199
  153. package/src/hooks/useSendAttachment.ts +3 -6
  154. package/src/index.tsx +3 -5
  155. package/src/screens/chat-detail/ChatComposer.tsx +88 -240
  156. package/src/screens/chat-detail/ChatDetail.tsx +36 -195
  157. package/src/screens/chat-detail/ChatListLegend.tsx +16 -13
  158. package/src/screens/chat-detail/index.ts +1 -5
  159. package/src/screens/chat-detail/legend/LegendChatMessage.tsx +19 -12
  160. package/src/screens/chat-detail/types.ts +1 -38
  161. package/src/services/attachmentHandlers/fileAttachmentHandler.ts +3 -2
  162. package/src/services/attachmentHandlers/imageAttachmentHandler.ts +6 -3
  163. package/src/services/attachmentHandlers/videoAttachmentHandler.ts +2 -1
  164. package/src/services/attachmentOrchestrator.ts +49 -27
  165. package/src/services/message.ts +21 -4
  166. package/src/translation/resources/i18n.ts +6 -3
  167. package/src/types/chat.ts +6 -0
  168. package/src/types/global.d.ts +299 -0
  169. package/src/types/index.d.ts +1 -0
  170. package/src/utils/UIUtils.ts +31 -0
  171. package/src/utils/imageUtils.ts +3 -4
  172. package/src/utils/pLimit.ts +20 -0
  173. package/src/utils/videoThumbnail.ts +10 -3
  174. package/src/utils/videoThumbnailUpload.ts +103 -0
  175. package/lib/module/hooks/useVideoAttachment.js +0 -251
  176. package/lib/module/hooks/useVideoAttachment.js.map +0 -1
  177. package/lib/module/screens/chat-detail/hooks/useAttachmentSendHandler.js +0 -288
  178. package/lib/module/screens/chat-detail/hooks/useAttachmentSendHandler.js.map +0 -1
  179. package/lib/module/utils/giftedChatMessage.js +0 -90
  180. package/lib/module/utils/giftedChatMessage.js.map +0 -1
  181. package/lib/module/utils/ui.js +0 -17
  182. package/lib/module/utils/ui.js.map +0 -1
  183. package/lib/typescript/src/hooks/useVideoAttachment.d.ts +0 -29
  184. package/lib/typescript/src/hooks/useVideoAttachment.d.ts.map +0 -1
  185. package/lib/typescript/src/screens/chat-detail/hooks/useAttachmentSendHandler.d.ts +0 -26
  186. package/lib/typescript/src/screens/chat-detail/hooks/useAttachmentSendHandler.d.ts.map +0 -1
  187. package/lib/typescript/src/utils/giftedChatMessage.d.ts +0 -10
  188. package/lib/typescript/src/utils/giftedChatMessage.d.ts.map +0 -1
  189. package/lib/typescript/src/utils/ui.d.ts +0 -13
  190. package/lib/typescript/src/utils/ui.d.ts.map +0 -1
  191. package/src/hooks/useVideoAttachment.ts +0 -334
  192. package/src/screens/chat-detail/hooks/useAttachmentSendHandler.ts +0 -386
  193. package/src/utils/giftedChatMessage.ts +0 -137
  194. 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