@droppii-org/chat-mobile 0.2.6 → 0.2.8

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 (398) hide show
  1. package/lib/module/components/AttachmentPreview.js +250 -0
  2. package/lib/module/components/AttachmentPreview.js.map +1 -0
  3. package/lib/module/components/MediaViewer/index.js +2 -0
  4. package/lib/module/components/MediaViewer/index.js.map +1 -0
  5. package/lib/module/components/MediaViewerModal.js +57 -0
  6. package/lib/module/components/MediaViewerModal.js.map +1 -0
  7. package/lib/module/components/MergedImageGrid.js +173 -0
  8. package/lib/module/components/MergedImageGrid.js.map +1 -0
  9. package/lib/module/components/ThreadCard/AvatarSection.js +4 -4
  10. package/lib/module/components/ThreadCard/AvatarSection.js.map +1 -1
  11. package/lib/module/components/ThreadCard/NamePrefixIcon.js +13 -16
  12. package/lib/module/components/ThreadCard/NamePrefixIcon.js.map +1 -1
  13. package/lib/module/components/ThreadCard/ThreadCard.js +13 -33
  14. package/lib/module/components/ThreadCard/ThreadCard.js.map +1 -1
  15. package/lib/module/components/ThreadCard/thread-card.utils.js +80 -4
  16. package/lib/module/components/ThreadCard/thread-card.utils.js.map +1 -1
  17. package/lib/module/components/messages/fileMessage/index.js +26 -0
  18. package/lib/module/components/messages/fileMessage/index.js.map +1 -0
  19. package/lib/module/components/messages/imageMessage/index.js +118 -0
  20. package/lib/module/components/messages/imageMessage/index.js.map +1 -0
  21. package/lib/module/components/messages/linkMessage/index.js +122 -0
  22. package/lib/module/components/messages/linkMessage/index.js.map +1 -0
  23. package/lib/module/components/messages/mergedMessage/index.js +37 -0
  24. package/lib/module/components/messages/mergedMessage/index.js.map +1 -0
  25. package/lib/module/components/messages/styles.js +41 -0
  26. package/lib/module/components/messages/styles.js.map +1 -0
  27. package/lib/module/components/messages/textMessage/index.js +38 -0
  28. package/lib/module/components/messages/textMessage/index.js.map +1 -0
  29. package/lib/module/components/messages/types.js +14 -0
  30. package/lib/module/components/messages/types.js.map +1 -0
  31. package/lib/module/components/messages/videoMessage/index.js +110 -0
  32. package/lib/module/components/messages/videoMessage/index.js.map +1 -0
  33. package/lib/module/config/api-endpoints.js +66 -0
  34. package/lib/module/config/api-endpoints.js.map +1 -0
  35. package/lib/module/config/attachment-priority.js +81 -0
  36. package/lib/module/config/attachment-priority.js.map +1 -0
  37. package/lib/module/config/configuration.js +50 -0
  38. package/lib/module/config/configuration.js.map +1 -0
  39. package/lib/module/config/index.js +22 -0
  40. package/lib/module/config/index.js.map +1 -0
  41. package/lib/module/context/ChatContext.js +7 -6
  42. package/lib/module/context/ChatContext.js.map +1 -1
  43. package/lib/module/core/index.js +19 -1
  44. package/lib/module/core/index.js.map +1 -1
  45. package/lib/module/core/useChatListener.js +0 -14
  46. package/lib/module/core/useChatListener.js.map +1 -1
  47. package/lib/module/hooks/message/useSendMessage.js +106 -0
  48. package/lib/module/hooks/message/useSendMessage.js.map +1 -0
  49. package/lib/module/hooks/useChatMessages.js +36 -121
  50. package/lib/module/hooks/useChatMessages.js.map +1 -1
  51. package/lib/module/hooks/useConversationList.js +29 -17
  52. package/lib/module/hooks/useConversationList.js.map +1 -1
  53. package/lib/module/hooks/useImageAttachment.js +263 -0
  54. package/lib/module/hooks/useImageAttachment.js.map +1 -0
  55. package/lib/module/hooks/useLinkPreview/useLinkPreview.js +3 -2
  56. package/lib/module/hooks/useLinkPreview/useLinkPreview.js.map +1 -1
  57. package/lib/module/hooks/useMediaViewer.js +24 -0
  58. package/lib/module/hooks/useMediaViewer.js.map +1 -0
  59. package/lib/module/hooks/useSendAttachment.js +182 -0
  60. package/lib/module/hooks/useSendAttachment.js.map +1 -0
  61. package/lib/module/hooks/useVideoAttachment.js +251 -0
  62. package/lib/module/hooks/useVideoAttachment.js.map +1 -0
  63. package/lib/module/index.js +13 -1
  64. package/lib/module/index.js.map +1 -1
  65. package/lib/module/screens/MediaView/VideoPlayer.js +177 -0
  66. package/lib/module/screens/MediaView/VideoPlayer.js.map +1 -0
  67. package/lib/module/screens/MediaView/index.js +264 -0
  68. package/lib/module/screens/MediaView/index.js.map +1 -0
  69. package/lib/module/screens/chat-detail/ChatComposer.js +190 -196
  70. package/lib/module/screens/chat-detail/ChatComposer.js.map +1 -1
  71. package/lib/module/screens/chat-detail/ChatDetail.js +106 -71
  72. package/lib/module/screens/chat-detail/ChatDetail.js.map +1 -1
  73. package/lib/module/screens/chat-detail/ChatDetailHeader.js +5 -8
  74. package/lib/module/screens/chat-detail/ChatDetailHeader.js.map +1 -1
  75. package/lib/module/screens/chat-detail/ChatLinkPreview.js +1 -1
  76. package/lib/module/screens/chat-detail/ChatLinkPreview.js.map +1 -1
  77. package/lib/module/screens/chat-detail/ChatListLegend.js +5 -15
  78. package/lib/module/screens/chat-detail/ChatListLegend.js.map +1 -1
  79. package/lib/module/screens/chat-detail/components/ChatInputActions.js +51 -0
  80. package/lib/module/screens/chat-detail/components/ChatInputActions.js.map +1 -0
  81. package/lib/module/screens/chat-detail/components/ChatMessageInput.js +93 -0
  82. package/lib/module/screens/chat-detail/components/ChatMessageInput.js.map +1 -0
  83. package/lib/module/screens/chat-detail/conversationHeader.utils.js +7 -9
  84. package/lib/module/screens/chat-detail/conversationHeader.utils.js.map +1 -1
  85. package/lib/module/screens/chat-detail/hooks/useAttachmentSendHandler.js +221 -0
  86. package/lib/module/screens/chat-detail/hooks/useAttachmentSendHandler.js.map +1 -0
  87. package/lib/module/screens/chat-detail/hooks/useChatComposerAnimation.js +114 -0
  88. package/lib/module/screens/chat-detail/hooks/useChatComposerAnimation.js.map +1 -0
  89. package/lib/module/screens/chat-detail/hooks/useChatComposerState.js +26 -0
  90. package/lib/module/screens/chat-detail/hooks/useChatComposerState.js.map +1 -0
  91. package/lib/module/screens/chat-detail/index.js +0 -1
  92. package/lib/module/screens/chat-detail/index.js.map +1 -1
  93. package/lib/module/screens/chat-detail/legend/LegendChatMessage.js +32 -25
  94. package/lib/module/screens/chat-detail/legend/LegendChatMessage.js.map +1 -1
  95. package/lib/module/screens/chat-detail/legend/messageTypes.js +15 -0
  96. package/lib/module/screens/chat-detail/legend/messageTypes.js.map +1 -0
  97. package/lib/module/screens/inbox/MessagesTab.js.map +1 -1
  98. package/lib/module/services/attachmentHandlers/fileAttachmentHandler.js +61 -0
  99. package/lib/module/services/attachmentHandlers/fileAttachmentHandler.js.map +1 -0
  100. package/lib/module/services/attachmentHandlers/imageAttachmentHandler.js +54 -0
  101. package/lib/module/services/attachmentHandlers/imageAttachmentHandler.js.map +1 -0
  102. package/lib/module/services/attachmentHandlers/index.js +12 -0
  103. package/lib/module/services/attachmentHandlers/index.js.map +1 -0
  104. package/lib/module/services/attachmentHandlers/videoAttachmentHandler.js +85 -0
  105. package/lib/module/services/attachmentHandlers/videoAttachmentHandler.js.map +1 -0
  106. package/lib/module/services/attachmentOrchestrator.js +225 -0
  107. package/lib/module/services/attachmentOrchestrator.js.map +1 -0
  108. package/lib/module/services/auth.js +35 -0
  109. package/lib/module/services/auth.js.map +1 -0
  110. package/lib/module/services/endpoints.js +20 -1
  111. package/lib/module/services/endpoints.js.map +1 -1
  112. package/lib/module/services/imageUpload.js +126 -0
  113. package/lib/module/services/imageUpload.js.map +1 -0
  114. package/lib/module/store/conversation.js +1 -1
  115. package/lib/module/store/conversation.js.map +1 -1
  116. package/lib/module/store/message.js +45 -0
  117. package/lib/module/store/message.js.map +1 -0
  118. package/lib/module/translation/resources/i18n.js +22 -2
  119. package/lib/module/translation/resources/i18n.js.map +1 -1
  120. package/lib/module/types/attachment.js +2 -0
  121. package/lib/module/types/attachment.js.map +1 -0
  122. package/lib/module/types/attachmentHandler.js +20 -0
  123. package/lib/module/types/attachmentHandler.js.map +1 -0
  124. package/lib/module/types/chat.js +2 -7
  125. package/lib/module/types/chat.js.map +1 -1
  126. package/lib/module/types/imageUpload.js +2 -0
  127. package/lib/module/types/imageUpload.js.map +1 -0
  128. package/lib/module/types/message.js +1 -0
  129. package/lib/module/types/message.js.map +1 -1
  130. package/lib/module/utils/chatImageDimens.js +148 -0
  131. package/lib/module/utils/chatImageDimens.js.map +1 -0
  132. package/lib/module/utils/conversation.js +34 -13
  133. package/lib/module/utils/conversation.js.map +1 -1
  134. package/lib/module/utils/device.js +65 -0
  135. package/lib/module/utils/device.js.map +1 -0
  136. package/lib/module/utils/imageUrlOptimizer.js +41 -0
  137. package/lib/module/utils/imageUrlOptimizer.js.map +1 -0
  138. package/lib/module/utils/imageUtils.js +69 -0
  139. package/lib/module/utils/imageUtils.js.map +1 -0
  140. package/lib/module/utils/legendListMessage.js +0 -3
  141. package/lib/module/utils/legendListMessage.js.map +1 -1
  142. package/lib/module/utils/message.js +5 -8
  143. package/lib/module/utils/message.js.map +1 -1
  144. package/lib/module/utils/resolveMessageType.js +3 -0
  145. package/lib/module/utils/resolveMessageType.js.map +1 -1
  146. package/lib/module/utils/ui.js +17 -0
  147. package/lib/module/utils/ui.js.map +1 -0
  148. package/lib/module/utils/url.js +3 -3
  149. package/lib/module/utils/url.js.map +1 -1
  150. package/lib/module/utils/videoThumbnail.js +62 -0
  151. package/lib/module/utils/videoThumbnail.js.map +1 -0
  152. package/lib/typescript/src/components/AttachmentPreview.d.ts +28 -0
  153. package/lib/typescript/src/components/AttachmentPreview.d.ts.map +1 -0
  154. package/lib/typescript/src/components/MediaViewer/index.d.ts +1 -0
  155. package/lib/typescript/src/components/MediaViewer/index.d.ts.map +1 -0
  156. package/lib/typescript/src/components/MediaViewerModal.d.ts +10 -0
  157. package/lib/typescript/src/components/MediaViewerModal.d.ts.map +1 -0
  158. package/lib/typescript/src/components/MergedImageGrid.d.ts +16 -0
  159. package/lib/typescript/src/components/MergedImageGrid.d.ts.map +1 -0
  160. package/lib/typescript/src/components/ThreadCard/AvatarSection.d.ts +2 -2
  161. package/lib/typescript/src/components/ThreadCard/AvatarSection.d.ts.map +1 -1
  162. package/lib/typescript/src/components/ThreadCard/NamePrefixIcon.d.ts +3 -4
  163. package/lib/typescript/src/components/ThreadCard/NamePrefixIcon.d.ts.map +1 -1
  164. package/lib/typescript/src/components/ThreadCard/ThreadCard.d.ts.map +1 -1
  165. package/lib/typescript/src/components/ThreadCard/thread-card.utils.d.ts.map +1 -1
  166. package/lib/typescript/src/components/messages/fileMessage/index.d.ts +3 -0
  167. package/lib/typescript/src/components/messages/fileMessage/index.d.ts.map +1 -0
  168. package/lib/typescript/src/components/messages/imageMessage/index.d.ts +3 -0
  169. package/lib/typescript/src/components/messages/imageMessage/index.d.ts.map +1 -0
  170. package/lib/typescript/src/components/messages/linkMessage/index.d.ts +9 -0
  171. package/lib/typescript/src/components/messages/linkMessage/index.d.ts.map +1 -0
  172. package/lib/typescript/src/components/messages/mergedMessage/index.d.ts +3 -0
  173. package/lib/typescript/src/components/messages/mergedMessage/index.d.ts.map +1 -0
  174. package/lib/typescript/src/components/messages/styles.d.ts +36 -0
  175. package/lib/typescript/src/components/messages/styles.d.ts.map +1 -0
  176. package/lib/typescript/src/components/messages/textMessage/index.d.ts +9 -0
  177. package/lib/typescript/src/components/messages/textMessage/index.d.ts.map +1 -0
  178. package/lib/typescript/src/components/messages/types.d.ts +10 -0
  179. package/lib/typescript/src/components/messages/types.d.ts.map +1 -0
  180. package/lib/typescript/src/components/messages/videoMessage/index.d.ts +3 -0
  181. package/lib/typescript/src/components/messages/videoMessage/index.d.ts.map +1 -0
  182. package/lib/typescript/src/config/api-endpoints.d.ts +40 -0
  183. package/lib/typescript/src/config/api-endpoints.d.ts.map +1 -0
  184. package/lib/typescript/src/config/attachment-priority.d.ts +31 -0
  185. package/lib/typescript/src/config/attachment-priority.d.ts.map +1 -0
  186. package/lib/typescript/src/config/configuration.d.ts +30 -0
  187. package/lib/typescript/src/config/configuration.d.ts.map +1 -0
  188. package/lib/typescript/src/config/index.d.ts +15 -0
  189. package/lib/typescript/src/config/index.d.ts.map +1 -0
  190. package/lib/typescript/src/context/ChatContext.d.ts +1 -1
  191. package/lib/typescript/src/context/ChatContext.d.ts.map +1 -1
  192. package/lib/typescript/src/core/index.d.ts +13 -1
  193. package/lib/typescript/src/core/index.d.ts.map +1 -1
  194. package/lib/typescript/src/core/useChatListener.d.ts.map +1 -1
  195. package/lib/typescript/src/hooks/message/useSendMessage.d.ts +12 -0
  196. package/lib/typescript/src/hooks/message/useSendMessage.d.ts.map +1 -0
  197. package/lib/typescript/src/hooks/useChatMessages.d.ts +0 -1
  198. package/lib/typescript/src/hooks/useChatMessages.d.ts.map +1 -1
  199. package/lib/typescript/src/hooks/useConversationList.d.ts +2 -1
  200. package/lib/typescript/src/hooks/useConversationList.d.ts.map +1 -1
  201. package/lib/typescript/src/hooks/useImageAttachment.d.ts +20 -0
  202. package/lib/typescript/src/hooks/useImageAttachment.d.ts.map +1 -0
  203. package/lib/typescript/src/hooks/useLinkPreview/useLinkPreview.d.ts.map +1 -1
  204. package/lib/typescript/src/hooks/useMediaViewer.d.ts +13 -0
  205. package/lib/typescript/src/hooks/useMediaViewer.d.ts.map +1 -0
  206. package/lib/typescript/src/hooks/useSendAttachment.d.ts +59 -0
  207. package/lib/typescript/src/hooks/useSendAttachment.d.ts.map +1 -0
  208. package/lib/typescript/src/hooks/useVideoAttachment.d.ts +29 -0
  209. package/lib/typescript/src/hooks/useVideoAttachment.d.ts.map +1 -0
  210. package/lib/typescript/src/index.d.ts +14 -1
  211. package/lib/typescript/src/index.d.ts.map +1 -1
  212. package/lib/typescript/src/screens/MediaView/VideoPlayer.d.ts +12 -0
  213. package/lib/typescript/src/screens/MediaView/VideoPlayer.d.ts.map +1 -0
  214. package/lib/typescript/src/screens/MediaView/index.d.ts +11 -0
  215. package/lib/typescript/src/screens/MediaView/index.d.ts.map +1 -0
  216. package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts +1 -1
  217. package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts.map +1 -1
  218. package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts +1 -1
  219. package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts.map +1 -1
  220. package/lib/typescript/src/screens/chat-detail/ChatDetailHeader.d.ts +1 -1
  221. package/lib/typescript/src/screens/chat-detail/ChatDetailHeader.d.ts.map +1 -1
  222. package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts +1 -1
  223. package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts.map +1 -1
  224. package/lib/typescript/src/screens/chat-detail/components/ChatInputActions.d.ts +8 -0
  225. package/lib/typescript/src/screens/chat-detail/components/ChatInputActions.d.ts.map +1 -0
  226. package/lib/typescript/src/screens/chat-detail/components/ChatMessageInput.d.ts +15 -0
  227. package/lib/typescript/src/screens/chat-detail/components/ChatMessageInput.d.ts.map +1 -0
  228. package/lib/typescript/src/screens/chat-detail/conversationHeader.utils.d.ts +1 -1
  229. package/lib/typescript/src/screens/chat-detail/conversationHeader.utils.d.ts.map +1 -1
  230. package/lib/typescript/src/screens/chat-detail/hooks/useAttachmentSendHandler.d.ts +23 -0
  231. package/lib/typescript/src/screens/chat-detail/hooks/useAttachmentSendHandler.d.ts.map +1 -0
  232. package/lib/typescript/src/screens/chat-detail/hooks/useChatComposerAnimation.d.ts +15 -0
  233. package/lib/typescript/src/screens/chat-detail/hooks/useChatComposerAnimation.d.ts.map +1 -0
  234. package/lib/typescript/src/screens/chat-detail/hooks/useChatComposerState.d.ts +13 -0
  235. package/lib/typescript/src/screens/chat-detail/hooks/useChatComposerState.d.ts.map +1 -0
  236. package/lib/typescript/src/screens/chat-detail/index.d.ts +0 -2
  237. package/lib/typescript/src/screens/chat-detail/index.d.ts.map +1 -1
  238. package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts +3 -3
  239. package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts.map +1 -1
  240. package/lib/typescript/src/screens/chat-detail/legend/messageTypes.d.ts +13 -0
  241. package/lib/typescript/src/screens/chat-detail/legend/messageTypes.d.ts.map +1 -0
  242. package/lib/typescript/src/screens/chat-detail/types.d.ts +10 -8
  243. package/lib/typescript/src/screens/chat-detail/types.d.ts.map +1 -1
  244. package/lib/typescript/src/services/attachmentHandlers/fileAttachmentHandler.d.ts +22 -0
  245. package/lib/typescript/src/services/attachmentHandlers/fileAttachmentHandler.d.ts.map +1 -0
  246. package/lib/typescript/src/services/attachmentHandlers/imageAttachmentHandler.d.ts +3 -0
  247. package/lib/typescript/src/services/attachmentHandlers/imageAttachmentHandler.d.ts.map +1 -0
  248. package/lib/typescript/src/services/attachmentHandlers/index.d.ts +5 -0
  249. package/lib/typescript/src/services/attachmentHandlers/index.d.ts.map +1 -0
  250. package/lib/typescript/src/services/attachmentHandlers/videoAttachmentHandler.d.ts +31 -0
  251. package/lib/typescript/src/services/attachmentHandlers/videoAttachmentHandler.d.ts.map +1 -0
  252. package/lib/typescript/src/services/attachmentOrchestrator.d.ts +5 -0
  253. package/lib/typescript/src/services/attachmentOrchestrator.d.ts.map +1 -0
  254. package/lib/typescript/src/services/auth.d.ts +19 -0
  255. package/lib/typescript/src/services/auth.d.ts.map +1 -0
  256. package/lib/typescript/src/services/endpoints.d.ts +11 -1
  257. package/lib/typescript/src/services/endpoints.d.ts.map +1 -1
  258. package/lib/typescript/src/services/imageUpload.d.ts +7 -0
  259. package/lib/typescript/src/services/imageUpload.d.ts.map +1 -0
  260. package/lib/typescript/src/store/message.d.ts +3 -0
  261. package/lib/typescript/src/store/message.d.ts.map +1 -0
  262. package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -1
  263. package/lib/typescript/src/types/attachment.d.ts +72 -0
  264. package/lib/typescript/src/types/attachment.d.ts.map +1 -0
  265. package/lib/typescript/src/types/attachmentHandler.d.ts +13 -0
  266. package/lib/typescript/src/types/attachmentHandler.d.ts.map +1 -0
  267. package/lib/typescript/src/types/chat.d.ts +28 -27
  268. package/lib/typescript/src/types/chat.d.ts.map +1 -1
  269. package/lib/typescript/src/types/common.d.ts +1 -0
  270. package/lib/typescript/src/types/common.d.ts.map +1 -1
  271. package/lib/typescript/src/types/imageUpload.d.ts +26 -0
  272. package/lib/typescript/src/types/imageUpload.d.ts.map +1 -0
  273. package/lib/typescript/src/types/message.d.ts +1 -0
  274. package/lib/typescript/src/types/message.d.ts.map +1 -1
  275. package/lib/typescript/src/utils/chatImageDimens.d.ts +34 -0
  276. package/lib/typescript/src/utils/chatImageDimens.d.ts.map +1 -0
  277. package/lib/typescript/src/utils/conversation.d.ts +3 -2
  278. package/lib/typescript/src/utils/conversation.d.ts.map +1 -1
  279. package/lib/typescript/src/utils/device.d.ts +7 -0
  280. package/lib/typescript/src/utils/device.d.ts.map +1 -0
  281. package/lib/typescript/src/utils/imageUrlOptimizer.d.ts +12 -0
  282. package/lib/typescript/src/utils/imageUrlOptimizer.d.ts.map +1 -0
  283. package/lib/typescript/src/utils/imageUtils.d.ts +9 -0
  284. package/lib/typescript/src/utils/imageUtils.d.ts.map +1 -0
  285. package/lib/typescript/src/utils/legendListMessage.d.ts +0 -2
  286. package/lib/typescript/src/utils/legendListMessage.d.ts.map +1 -1
  287. package/lib/typescript/src/utils/message.d.ts.map +1 -1
  288. package/lib/typescript/src/utils/resolveMessageType.d.ts.map +1 -1
  289. package/lib/typescript/src/utils/ui.d.ts +13 -0
  290. package/lib/typescript/src/utils/ui.d.ts.map +1 -0
  291. package/lib/typescript/src/utils/url.d.ts +1 -1
  292. package/lib/typescript/src/utils/url.d.ts.map +1 -1
  293. package/lib/typescript/src/utils/videoThumbnail.d.ts +16 -0
  294. package/lib/typescript/src/utils/videoThumbnail.d.ts.map +1 -0
  295. package/package.json +15 -3
  296. package/src/components/AttachmentPreview.tsx +304 -0
  297. package/src/components/MediaViewer/index.tsx +0 -0
  298. package/src/components/MediaViewerModal.tsx +70 -0
  299. package/src/components/MergedImageGrid.tsx +238 -0
  300. package/src/components/ThreadCard/AvatarSection.tsx +5 -8
  301. package/src/components/ThreadCard/NamePrefixIcon.tsx +27 -38
  302. package/src/components/ThreadCard/ThreadCard.tsx +16 -30
  303. package/src/components/ThreadCard/thread-card.utils.ts +95 -4
  304. package/src/components/messages/fileMessage/index.tsx +30 -0
  305. package/src/components/messages/imageMessage/index.tsx +137 -0
  306. package/src/components/messages/linkMessage/index.tsx +162 -0
  307. package/src/components/messages/mergedMessage/index.tsx +45 -0
  308. package/src/components/messages/styles.ts +39 -0
  309. package/src/components/messages/textMessage/index.tsx +53 -0
  310. package/src/components/messages/types.ts +22 -0
  311. package/src/components/messages/videoMessage/index.tsx +120 -0
  312. package/src/config/api-endpoints.ts +72 -0
  313. package/src/config/attachment-priority.ts +93 -0
  314. package/src/config/configuration.ts +50 -0
  315. package/src/config/index.ts +19 -0
  316. package/src/context/ChatContext.tsx +12 -4
  317. package/src/core/index.ts +25 -1
  318. package/src/core/useChatListener.ts +0 -21
  319. package/src/hooks/message/useSendMessage.ts +143 -0
  320. package/src/hooks/useChatMessages.ts +69 -161
  321. package/src/hooks/useConversationList.ts +34 -16
  322. package/src/hooks/useImageAttachment.ts +348 -0
  323. package/src/hooks/useLinkPreview/useLinkPreview.ts +3 -2
  324. package/src/hooks/useMediaViewer.ts +32 -0
  325. package/src/hooks/useSendAttachment.ts +295 -0
  326. package/src/hooks/useVideoAttachment.ts +334 -0
  327. package/src/index.tsx +13 -1
  328. package/src/screens/MediaView/VideoPlayer.tsx +211 -0
  329. package/src/screens/MediaView/index.tsx +327 -0
  330. package/src/screens/chat-detail/ChatComposer.tsx +206 -271
  331. package/src/screens/chat-detail/ChatDetail.tsx +164 -104
  332. package/src/screens/chat-detail/ChatDetailHeader.tsx +4 -10
  333. package/src/screens/chat-detail/ChatLinkPreview.tsx +1 -1
  334. package/src/screens/chat-detail/ChatListLegend.tsx +10 -13
  335. package/src/screens/chat-detail/components/ChatInputActions.tsx +71 -0
  336. package/src/screens/chat-detail/components/ChatMessageInput.tsx +127 -0
  337. package/src/screens/chat-detail/conversationHeader.utils.ts +11 -14
  338. package/src/screens/chat-detail/hooks/useAttachmentSendHandler.ts +291 -0
  339. package/src/screens/chat-detail/hooks/useChatComposerAnimation.ts +184 -0
  340. package/src/screens/chat-detail/hooks/useChatComposerState.ts +40 -0
  341. package/src/screens/chat-detail/index.ts +0 -2
  342. package/src/screens/chat-detail/legend/LegendChatMessage.tsx +44 -39
  343. package/src/screens/chat-detail/legend/messageTypes.tsx +13 -0
  344. package/src/screens/chat-detail/types.ts +11 -9
  345. package/src/screens/inbox/MessagesTab.tsx +1 -1
  346. package/src/services/attachmentHandlers/fileAttachmentHandler.ts +78 -0
  347. package/src/services/attachmentHandlers/imageAttachmentHandler.ts +54 -0
  348. package/src/services/attachmentHandlers/index.ts +10 -0
  349. package/src/services/attachmentHandlers/videoAttachmentHandler.ts +114 -0
  350. package/src/services/attachmentOrchestrator.ts +300 -0
  351. package/src/services/auth.ts +34 -0
  352. package/src/services/endpoints.ts +24 -1
  353. package/src/services/imageUpload.ts +162 -0
  354. package/src/store/conversation.ts +1 -1
  355. package/src/store/message.ts +44 -0
  356. package/src/translation/resources/i18n.ts +22 -2
  357. package/src/types/attachment.ts +85 -0
  358. package/src/types/attachmentHandler.ts +31 -0
  359. package/src/types/chat.ts +31 -30
  360. package/src/types/common.ts +1 -0
  361. package/src/types/imageUpload.ts +28 -0
  362. package/src/types/message.ts +1 -0
  363. package/src/utils/chatImageDimens.ts +178 -0
  364. package/src/utils/conversation.ts +44 -17
  365. package/src/utils/device.ts +73 -0
  366. package/src/utils/imageUrlOptimizer.ts +56 -0
  367. package/src/utils/imageUtils.ts +76 -0
  368. package/src/utils/legendListMessage.ts +0 -5
  369. package/src/utils/message.ts +10 -12
  370. package/src/utils/resolveMessageType.ts +2 -0
  371. package/src/utils/ui.ts +19 -0
  372. package/src/utils/url.ts +3 -3
  373. package/src/utils/videoThumbnail.ts +85 -0
  374. package/lib/module/screens/chat-detail/ChatList.js +0 -147
  375. package/lib/module/screens/chat-detail/ChatList.js.map +0 -1
  376. package/lib/module/screens/chat-detail/ChatTextBubble.js +0 -62
  377. package/lib/module/screens/chat-detail/ChatTextBubble.js.map +0 -1
  378. package/lib/module/screens/chat-detail/legend/message-types.js +0 -122
  379. package/lib/module/screens/chat-detail/legend/message-types.js.map +0 -1
  380. package/lib/module/screens/chat-detail/messages/ChatMessageBubble.js +0 -24
  381. package/lib/module/screens/chat-detail/messages/ChatMessageBubble.js.map +0 -1
  382. package/lib/module/screens/chat-detail/messages/types.js +0 -4
  383. package/lib/module/screens/chat-detail/messages/types.js.map +0 -1
  384. package/lib/typescript/src/screens/chat-detail/ChatList.d.ts +0 -3
  385. package/lib/typescript/src/screens/chat-detail/ChatList.d.ts.map +0 -1
  386. package/lib/typescript/src/screens/chat-detail/ChatTextBubble.d.ts +0 -3
  387. package/lib/typescript/src/screens/chat-detail/ChatTextBubble.d.ts.map +0 -1
  388. package/lib/typescript/src/screens/chat-detail/legend/message-types.d.ts +0 -12
  389. package/lib/typescript/src/screens/chat-detail/legend/message-types.d.ts.map +0 -1
  390. package/lib/typescript/src/screens/chat-detail/messages/ChatMessageBubble.d.ts +0 -3
  391. package/lib/typescript/src/screens/chat-detail/messages/ChatMessageBubble.d.ts.map +0 -1
  392. package/lib/typescript/src/screens/chat-detail/messages/types.d.ts +0 -13
  393. package/lib/typescript/src/screens/chat-detail/messages/types.d.ts.map +0 -1
  394. package/src/screens/chat-detail/ChatList.tsx +0 -190
  395. package/src/screens/chat-detail/ChatTextBubble.tsx +0 -73
  396. package/src/screens/chat-detail/legend/message-types.tsx +0 -149
  397. package/src/screens/chat-detail/messages/ChatMessageBubble.tsx +0 -23
  398. package/src/screens/chat-detail/messages/types.ts +0 -14
@@ -0,0 +1,348 @@
1
+ import { useCallback, useState } from 'react';
2
+ import RNFS from 'react-native-fs';
3
+ import type { Image as PickedImage } from 'react-native-image-crop-picker';
4
+ import DeviceUtils from '../utils/device';
5
+ import UIUtils from '../utils/ui';
6
+ import { trans } from '../translation';
7
+ import { getMimeType, isVideoFromPicker } from '../utils/imageUtils';
8
+ import { ImageUploadAPI } from '../services/imageUpload';
9
+ import VideoThumbnailUtil from '../utils/videoThumbnail';
10
+
11
+ interface ImageWithUploadStatus {
12
+ image: PickedImage;
13
+ uploadProgress: number;
14
+ error: string | null;
15
+ isUploading: boolean;
16
+ uploadedUrl?: string;
17
+ thumbnailUrl?: string;
18
+ thumbnailSize?: number;
19
+ }
20
+
21
+ export interface UseImageAttachmentReturn {
22
+ selectedImages: ImageWithUploadStatus[];
23
+ isImagePreviewVisible: boolean;
24
+ handleImageAttach: () => Promise<void>;
25
+ handleImageRemove: (index: number) => void;
26
+ clearAllImages: () => void;
27
+ }
28
+
29
+ export const useImageAttachment = (): UseImageAttachmentReturn => {
30
+ const [selectedImages, setSelectedImages] = useState<ImageWithUploadStatus[]>(
31
+ []
32
+ );
33
+ const [isImagePreviewVisible, setIsImagePreviewVisible] = useState(false);
34
+
35
+ const updateImageStatus = useCallback(
36
+ (index: number, updates: Partial<Omit<ImageWithUploadStatus, 'image'>>) => {
37
+ setSelectedImages((prev) => {
38
+ const newImages = [...prev];
39
+ if (newImages[index]) {
40
+ newImages[index] = { ...newImages[index], ...updates };
41
+ }
42
+ return newImages;
43
+ });
44
+ },
45
+ []
46
+ );
47
+
48
+ const uploadImage = useCallback(
49
+ async (index: number, image: PickedImage) => {
50
+ console.log('Upload', 'Step 1: Starting upload for image', { index });
51
+ let cachedFilePath: string | null = null;
52
+
53
+ try {
54
+ console.log('Upload', 'Step 2: Updating status to uploading...');
55
+ updateImageStatus(index, {
56
+ isUploading: true,
57
+ error: null,
58
+ uploadProgress: 0,
59
+ });
60
+
61
+ try {
62
+ console.log('Upload', 'Step 3: Preparing file...', {
63
+ originalPath: image.path,
64
+ filename: image.filename,
65
+ });
66
+ const ext = image.filename?.split('.').pop() || 'jpg';
67
+ const fileName = `temp-image-${Date.now()}-${index}.${ext}`;
68
+ cachedFilePath = `${RNFS.DocumentDirectoryPath}/${fileName}`;
69
+
70
+ console.log('Upload', 'Step 4: Copying file to cache...', {
71
+ from: image.path,
72
+ to: cachedFilePath,
73
+ });
74
+ await RNFS.copyFile(image.path, cachedFilePath);
75
+ console.log('Upload', 'Step 5: File copied successfully');
76
+ } catch (fileError: any) {
77
+ console.error('Upload', 'File copy error', fileError);
78
+ throw new Error(
79
+ `Failed to prepare file: ${fileError?.message || 'Unknown error'}`
80
+ );
81
+ }
82
+
83
+ const filename = image.filename || 'image.jpg';
84
+
85
+ const setProgress = (progress: number) => {
86
+ try {
87
+ console.log('Upload', 'Progress', { percentage: progress });
88
+ updateImageStatus(index, { uploadProgress: progress });
89
+ } catch (err) {
90
+ console.error('Upload', 'Progress update error', err);
91
+ }
92
+ };
93
+
94
+ let uploadedImage;
95
+ try {
96
+ console.log('Upload', 'Step 6: Uploading to server...', {
97
+ filename,
98
+ size: image.size,
99
+ });
100
+ uploadedImage = await ImageUploadAPI.uploadImageFile(
101
+ {
102
+ path: cachedFilePath || '',
103
+ filename,
104
+ size: image.size || 0,
105
+ width: image.width || 0,
106
+ height: image.height || 0,
107
+ mime: getMimeType(filename),
108
+ },
109
+ setProgress
110
+ );
111
+ console.log('Upload', 'Step 7: Upload successful!', {
112
+ url: uploadedImage?.url,
113
+ });
114
+ } catch (uploadError: any) {
115
+ console.error('Upload', 'Server upload error', uploadError);
116
+ throw new Error(
117
+ `Upload failed: ${uploadError?.message || 'Unknown error'}`
118
+ );
119
+ }
120
+
121
+ if (!uploadedImage?.url) {
122
+ throw new Error('No URL returned from server');
123
+ }
124
+
125
+ console.log('Upload', 'Step 8: Upload complete, preparing data...');
126
+ updateImageStatus(index, {
127
+ isUploading: false,
128
+ uploadProgress: 100,
129
+ uploadedUrl: uploadedImage.url,
130
+ });
131
+
132
+ console.log(
133
+ 'Upload',
134
+ 'Step 9: Ready to send - waiting for user confirmation'
135
+ );
136
+
137
+ console.log('Upload', 'Step 11: Cleaning up cache file...');
138
+ try {
139
+ if (cachedFilePath) {
140
+ await ImageUploadAPI.cleanupImageFile(cachedFilePath);
141
+ }
142
+ } catch (cleanupError) {
143
+ console.warn('Upload', 'Cleanup warning', cleanupError);
144
+ }
145
+ console.log('Upload', 'Step 12: COMPLETE ✓');
146
+ } catch (error: any) {
147
+ console.error('Upload', 'ERROR', error);
148
+ const errorMsg = error?.message || trans('chat:image_upload_failed');
149
+ updateImageStatus(index, {
150
+ isUploading: false,
151
+ error: errorMsg,
152
+ });
153
+
154
+ try {
155
+ if (cachedFilePath) {
156
+ await ImageUploadAPI.cleanupImageFile(cachedFilePath);
157
+ }
158
+ } catch (cleanupError) {
159
+ console.warn('Upload', 'Cleanup after error', cleanupError);
160
+ }
161
+
162
+ console.error('uploadImage', 'Full error', error);
163
+ }
164
+ },
165
+ [updateImageStatus]
166
+ );
167
+
168
+ const generateVideoThumbnail = useCallback(
169
+ async (index: number, image: PickedImage) => {
170
+ if (!isVideoFromPicker(image)) return; // Only for videos
171
+
172
+ try {
173
+ console.log('[ImageAttach] Generating thumbnail for video:', {
174
+ index,
175
+ filename: image.filename,
176
+ });
177
+
178
+ const videoDuration = (image as any).duration || 0;
179
+ const timeMs = Math.min(1000, Math.floor(videoDuration / 2));
180
+
181
+ // Copy video to safe location
182
+ const ext = image.filename?.split('.').pop() || 'mp4';
183
+ const cachedFileName = `thumb-cache-${Date.now()}-${index}.${ext}`;
184
+ const cachedVideoPath = `${RNFS.DocumentDirectoryPath}/${cachedFileName}`;
185
+
186
+ await RNFS.copyFile(image.path, cachedVideoPath);
187
+
188
+ // Generate thumbnail
189
+ const thumbnail = await VideoThumbnailUtil.generateThumbnail(
190
+ cachedVideoPath,
191
+ { time: timeMs, quality: 80 }
192
+ );
193
+
194
+ if (!thumbnail) throw new Error('Thumbnail generation failed');
195
+
196
+ // Upload thumbnail (force .jpg extension for image file)
197
+ const baseName = image.filename?.split('.')[0] || 'video';
198
+ const uploadedThumbnail = await ImageUploadAPI.uploadImageFile(
199
+ {
200
+ path: thumbnail.path,
201
+ filename: `thumb-${baseName}.jpg`,
202
+ size: 0,
203
+ width: thumbnail.width || 320,
204
+ height: thumbnail.height || 180,
205
+ mime: 'image/jpeg',
206
+ },
207
+ (progress) => {
208
+ console.log('[ImageAttach] Thumbnail upload progress:', {
209
+ index,
210
+ percentage: progress,
211
+ });
212
+ }
213
+ );
214
+
215
+ if (!uploadedThumbnail?.url) {
216
+ throw new Error('No thumbnail URL returned');
217
+ }
218
+
219
+ // Update state with thumbnail
220
+ updateImageStatus(index, {
221
+ thumbnailUrl: uploadedThumbnail.url,
222
+ thumbnailSize: uploadedThumbnail.size || 0,
223
+ });
224
+
225
+ console.log(
226
+ `✅ [ImageAttach] THUMBNAIL URL (picked): ${uploadedThumbnail.url}`
227
+ );
228
+
229
+ // Cleanup cached video
230
+ try {
231
+ await RNFS.unlink(cachedVideoPath);
232
+ } catch (err) {
233
+ console.warn('[ImageAttach] Failed to clean cached video:', err);
234
+ }
235
+ } catch (error) {
236
+ console.error('[ImageAttach] Video thumbnail generation error:', error);
237
+ UIUtils.toast.open({
238
+ title: 'Thumbnail Error',
239
+ message: `Failed to generate thumbnail for ${image.filename}`,
240
+ theme: 'danger',
241
+ });
242
+ }
243
+ },
244
+ [updateImageStatus]
245
+ );
246
+
247
+ const handleImageAttach = useCallback(
248
+ async (multipleMode: boolean = true) => {
249
+ console.log('ImageAttach', 'Step 1: User tapped attachment button', {
250
+ multipleMode,
251
+ });
252
+ try {
253
+ console.log('ImageAttach', 'Step 2: Opening image picker...');
254
+ const images = await DeviceUtils.openImagePicker(multipleMode);
255
+
256
+ if (!images || images.length === 0) {
257
+ console.log('ImageAttach', 'Step 3: User cancelled picker');
258
+ return;
259
+ }
260
+
261
+ console.log('ImageAttach', 'Step 4: Images picked', {
262
+ count: images.length,
263
+ });
264
+
265
+ const MAX_FILE_SIZE = 5 * 1024 * 1024;
266
+ const oversizedImages = images.filter(
267
+ (img) => (img.size || 0) > MAX_FILE_SIZE
268
+ );
269
+
270
+ if (oversizedImages.length > 0) {
271
+ console.warn('ImageAttach', 'Step 5: Some images too large', {
272
+ count: oversizedImages.length,
273
+ maxSize: MAX_FILE_SIZE,
274
+ });
275
+ UIUtils.toast.open({
276
+ title: trans('chat:image_too_large'),
277
+ theme: 'danger',
278
+ });
279
+ const validImages = images.filter(
280
+ (img) => (img.size || 0) <= MAX_FILE_SIZE
281
+ );
282
+ if (validImages.length === 0) return;
283
+ images.splice(0, images.length, ...validImages);
284
+ }
285
+
286
+ console.log('ImageAttach', 'Step 6: Size validation passed');
287
+ const startIndex = selectedImages.length;
288
+
289
+ console.log('ImageAttach', 'Step 7: Adding to preview list', {
290
+ count: images.length,
291
+ startIndex,
292
+ });
293
+
294
+ setSelectedImages((prev) => [
295
+ ...prev,
296
+ ...images.map((image) => ({
297
+ image,
298
+ uploadProgress: 0,
299
+ error: null,
300
+ isUploading: true,
301
+ })),
302
+ ]);
303
+ setIsImagePreviewVisible(true);
304
+
305
+ // Generate thumbnails for videos (parallel)
306
+ console.log(
307
+ 'ImageAttach',
308
+ 'Step 7.5: Generating thumbnails for videos...'
309
+ );
310
+ await Promise.all(
311
+ images.map((image, offset) =>
312
+ generateVideoThumbnail(startIndex + offset, image)
313
+ )
314
+ );
315
+
316
+ console.log('ImageAttach', 'Step 8: Starting uploads...');
317
+ await Promise.all(
318
+ images.map((image, offset) => uploadImage(startIndex + offset, image))
319
+ );
320
+ } catch (error) {
321
+ console.error('ImageAttach', 'ERROR', error);
322
+ UIUtils.toast.open({
323
+ title: trans('chat:image_pick_failed'),
324
+ message: error instanceof Error ? error.message : 'Unknown error',
325
+ theme: 'danger',
326
+ });
327
+ }
328
+ },
329
+ [selectedImages.length, uploadImage, generateVideoThumbnail]
330
+ );
331
+
332
+ const handleImageRemove = useCallback((index: number) => {
333
+ setSelectedImages((prev) => prev.filter((_, i) => i !== index));
334
+ }, []);
335
+
336
+ const clearAllImages = useCallback(() => {
337
+ setSelectedImages([]);
338
+ setIsImagePreviewVisible(false);
339
+ }, []);
340
+
341
+ return {
342
+ selectedImages,
343
+ isImagePreviewVisible,
344
+ handleImageAttach,
345
+ handleImageRemove,
346
+ clearAllImages,
347
+ };
348
+ };
@@ -1,6 +1,6 @@
1
1
  import { useCallback, useEffect, useRef, useState } from 'react';
2
2
  import { useFetchUrlMetadata } from './useFetchUrlMetadata';
3
- import { extractFirstUrl } from '../../utils/url';
3
+ import { extractUrls } from '../../utils/url';
4
4
 
5
5
  export function useLinkPreview(value: string | undefined) {
6
6
  const [detectedUrl, setDetectedUrl] = useState<string | undefined>(undefined);
@@ -10,7 +10,8 @@ export function useLinkPreview(value: string | undefined) {
10
10
  useEffect(() => {
11
11
  if (timerRef.current) clearTimeout(timerRef.current);
12
12
  timerRef.current = setTimeout(() => {
13
- const url = value ? extractFirstUrl(value) : undefined;
13
+ const urls = value ? extractUrls(value) : [];
14
+ const url = urls.length === 1 ? urls[0] : undefined;
14
15
  setDetectedUrl(url);
15
16
  if (url) setIsDismissed(false);
16
17
  }, 500);
@@ -0,0 +1,32 @@
1
+ import { useCallback, useState } from 'react';
2
+
3
+ export interface MediaItem {
4
+ url: string;
5
+ isVideo?: boolean;
6
+ }
7
+
8
+ export interface UseMediaViewerReturn {
9
+ visible: boolean;
10
+ mediaList: MediaItem[];
11
+ initIndex: number;
12
+ show: (items: MediaItem[], index?: number) => void;
13
+ hide: () => void;
14
+ }
15
+
16
+ export function useMediaViewer(): UseMediaViewerReturn {
17
+ const [visible, setVisible] = useState(false);
18
+ const [mediaList, setMediaList] = useState<MediaItem[]>([]);
19
+ const [initIndex, setInitIndex] = useState(0);
20
+
21
+ const show = useCallback((items: MediaItem[], index = 0) => {
22
+ setMediaList(items);
23
+ setInitIndex(index);
24
+ setVisible(true);
25
+ }, []);
26
+
27
+ const hide = useCallback(() => {
28
+ setVisible(false);
29
+ }, []);
30
+
31
+ return { visible, mediaList, initIndex, show, hide };
32
+ }
@@ -0,0 +1,295 @@
1
+ import { useCallback, useState } from 'react';
2
+ import { AttachmentOrchestratorService } from '../services/attachmentOrchestrator';
3
+ import type { Attachment, AttachmentSendOptions } from '../types/attachment';
4
+ import { getMimeType } from '../utils/imageUtils';
5
+
6
+ export interface ImageItem {
7
+ type: 'image';
8
+ image: {
9
+ path: string;
10
+ filename?: string;
11
+ size?: number;
12
+ width?: number;
13
+ height?: number;
14
+ };
15
+ uploadProgress: number;
16
+ error: string | null;
17
+ isUploading: boolean;
18
+ uploadedUrl?: string;
19
+ }
20
+
21
+ export interface VideoItem {
22
+ type: 'video';
23
+ video: {
24
+ path: string;
25
+ filename?: string;
26
+ size?: number;
27
+ width?: number;
28
+ height?: number;
29
+ duration?: number;
30
+ };
31
+ uploadProgress: number;
32
+ error: string | null;
33
+ isUploading: boolean;
34
+ uploadedUrl?: string;
35
+ thumbnailUrl?: string;
36
+ thumbnailSize?: number;
37
+ }
38
+
39
+ export interface FileItem {
40
+ type: 'file';
41
+ file: {
42
+ path: string;
43
+ filename?: string;
44
+ size?: number;
45
+ };
46
+ uploadProgress: number;
47
+ error: string | null;
48
+ isUploading: boolean;
49
+ uploadedUrl?: string;
50
+ }
51
+
52
+ export type AttachmentItem = ImageItem | VideoItem | FileItem;
53
+
54
+ export interface UseSendAttachmentReturn {
55
+ isLoading: boolean;
56
+ error: string | null;
57
+ send(
58
+ items: AttachmentItem[],
59
+ options?: Partial<AttachmentSendOptions>,
60
+ onMessageSent?: (message: any) => void
61
+ ): Promise<boolean>;
62
+ clearError(): void;
63
+ }
64
+
65
+ /**
66
+ * Smart hook để send mixed attachments (images + videos + files cùng lúc)
67
+ * - Detect type của từng item
68
+ * - Group by type
69
+ * - Send với handler riêng cho mỗi type
70
+ */
71
+ export function useSendAttachment(): UseSendAttachmentReturn {
72
+ const [isLoading, setIsLoading] = useState(false);
73
+ const [error, setError] = useState<string | null>(null);
74
+
75
+ const send = useCallback(
76
+ async (
77
+ items: AttachmentItem[],
78
+ options?: Partial<AttachmentSendOptions>,
79
+ onMessageSent?: (message: any) => void
80
+ ): Promise<boolean> => {
81
+ console.log('[useSendAttachment] Start sending', {
82
+ totalItems: items.length,
83
+ strategy: options?.strategy || 'individual',
84
+ });
85
+
86
+ // Filter uploaded items
87
+ const uploadedItems = items.filter((item) => item.uploadedUrl);
88
+ console.log('[useSendAttachment] Filtered uploaded items', {
89
+ uploadedCount: uploadedItems.length,
90
+ uploadedTypes: uploadedItems.map((i) => i.type),
91
+ });
92
+
93
+ if (uploadedItems.length === 0) {
94
+ setError('No uploaded attachments to send');
95
+ console.warn('[useSendAttachment] No uploaded items to send');
96
+ return false;
97
+ }
98
+
99
+ setIsLoading(true);
100
+ setError(null);
101
+
102
+ try {
103
+ // Convert items → Attachments (detect type)
104
+ console.log('[useSendAttachment] Converting to Attachment objects...', {
105
+ itemCount: uploadedItems.length,
106
+ itemTypes: uploadedItems.map((i) => i.type),
107
+ });
108
+ const attachments = uploadedItems.map((item) => {
109
+ const baseId = `att-${Date.now()}-${Math.random()}`;
110
+
111
+ // Auto-detect video from mime type if not explicitly typed
112
+ let itemType = item.type;
113
+ if (
114
+ itemType === 'image' &&
115
+ item.uploadedUrl &&
116
+ item.uploadedUrl.includes('.MP4')
117
+ ) {
118
+ itemType = 'video';
119
+ console.log('[useSendAttachment] Auto-detected video from URL');
120
+ }
121
+
122
+ console.log('[useSendAttachment] Converting item:', {
123
+ originalType: item.type,
124
+ detectedType: itemType,
125
+ });
126
+
127
+ if (itemType === 'image' && item.type === 'image') {
128
+ return {
129
+ id: baseId,
130
+ type: 'image' as const,
131
+ sourceFile: {
132
+ path: (item as ImageItem).image.path,
133
+ filename: (item as ImageItem).image.filename || 'image.jpg',
134
+ size: (item as ImageItem).image.size || 0,
135
+ mimeType: getMimeType(
136
+ (item as ImageItem).image.filename || 'jpg'
137
+ ),
138
+ },
139
+ metadata: {
140
+ width: (item as ImageItem).image.width || 0,
141
+ height: (item as ImageItem).image.height || 0,
142
+ },
143
+ };
144
+ }
145
+
146
+ if (itemType === 'video') {
147
+ // Handle both VideoItem and auto-detected video from ImageItem
148
+ const videoData =
149
+ item.type === 'video'
150
+ ? (item as VideoItem).video
151
+ : (item as ImageItem).image;
152
+ const duration =
153
+ item.type === 'video'
154
+ ? (item as VideoItem).video.duration || 0
155
+ : 0;
156
+ const thumbnailUrl =
157
+ item.type === 'video'
158
+ ? (item as VideoItem).thumbnailUrl
159
+ : undefined;
160
+ const thumbnailSize =
161
+ item.type === 'video'
162
+ ? (item as VideoItem).thumbnailSize
163
+ : undefined;
164
+
165
+ const metadata: any = {
166
+ width: videoData.width || 0,
167
+ height: videoData.height || 0,
168
+ duration,
169
+ };
170
+
171
+ if (thumbnailUrl) {
172
+ metadata.thumbnail = {
173
+ url: thumbnailUrl,
174
+ width: videoData.width || 320,
175
+ height: videoData.height || 180,
176
+ size: thumbnailSize,
177
+ };
178
+ }
179
+
180
+ return {
181
+ id: baseId,
182
+ type: 'video' as const,
183
+ sourceFile: {
184
+ path: videoData.path,
185
+ filename: videoData.filename || 'video.mp4',
186
+ size: videoData.size || 0,
187
+ mimeType: getMimeType(videoData.filename || 'mp4'),
188
+ },
189
+ metadata,
190
+ };
191
+ }
192
+
193
+ if (itemType === 'file' && item.type === 'file') {
194
+ return {
195
+ id: baseId,
196
+ type: 'file' as const,
197
+ sourceFile: {
198
+ path: (item as FileItem).file.path,
199
+ filename: (item as FileItem).file.filename || 'file.bin',
200
+ size: (item as FileItem).file.size || 0,
201
+ mimeType: getMimeType(
202
+ (item as FileItem).file.filename || 'bin'
203
+ ),
204
+ },
205
+ metadata: {
206
+ extension:
207
+ (item as FileItem).file.filename?.split('.').pop() || '',
208
+ category: 'other' as const,
209
+ },
210
+ };
211
+ }
212
+
213
+ // Fallback - shouldn't reach here
214
+ throw new Error(`Unknown attachment type: ${itemType}`);
215
+ });
216
+
217
+ // Map uploadedUrl
218
+ console.log('[useSendAttachment] Mapping uploaded URLs...');
219
+ const uploadedUrls: Record<string, string> = {};
220
+ uploadedItems.forEach((item) => {
221
+ let sourcePath = '';
222
+ if (item.type === 'image') sourcePath = item.image.path;
223
+ else if (item.type === 'video') sourcePath = item.video.path;
224
+ else sourcePath = item.file.path;
225
+
226
+ const att = attachments.find((a) => a.sourceFile.path === sourcePath);
227
+ if (att && item.uploadedUrl) {
228
+ uploadedUrls[att.id] = item.uploadedUrl;
229
+ console.log(`[useSendAttachment] Mapped ${att.type}:`, {
230
+ attachmentId: att.id,
231
+ url: item.uploadedUrl,
232
+ });
233
+ }
234
+ });
235
+
236
+ // Send all (mixed types)
237
+ console.log('[useSendAttachment] Calling sendPreparedAttachments...', {
238
+ attachmentCount: attachments.length,
239
+ strategy: options?.strategy || 'individual',
240
+ });
241
+ const result =
242
+ await AttachmentOrchestratorService.sendPreparedAttachments(
243
+ attachments as Attachment[],
244
+ uploadedUrls,
245
+ {
246
+ strategy: 'individual',
247
+ ...options,
248
+ },
249
+ onMessageSent
250
+ );
251
+
252
+ console.log('[useSendAttachment] Send result:', {
253
+ success: result.success,
254
+ sentCount: result.sentAttachmentIds.length,
255
+ failedCount: result.failedAttachmentIds.length,
256
+ mergedMessageId: result.mergedMessageId,
257
+ errors: result.errors,
258
+ });
259
+
260
+ if (!result.success) {
261
+ const errorMsg =
262
+ Object.values(result.errors).join(', ') ||
263
+ 'Failed to send attachments';
264
+ setError(errorMsg);
265
+ console.error('[useSendAttachment] Send failed:', errorMsg);
266
+ return false;
267
+ }
268
+
269
+ console.log('[useSendAttachment] Successfully sent attachments');
270
+ return true;
271
+ } catch (err) {
272
+ const errorMsg = err instanceof Error ? err.message : 'Unknown error';
273
+ setError(errorMsg);
274
+ console.error('[useSendAttachment] Exception:', {
275
+ error: errorMsg,
276
+ stack: err instanceof Error ? err.stack : undefined,
277
+ });
278
+ return false;
279
+ } finally {
280
+ setIsLoading(false);
281
+ console.log('[useSendAttachment] Finished');
282
+ }
283
+ },
284
+ []
285
+ );
286
+
287
+ const clearError = useCallback(() => setError(null), []);
288
+
289
+ return {
290
+ isLoading,
291
+ error,
292
+ send,
293
+ clearError,
294
+ };
295
+ }