@ethora/chat-component-rn 26.10.4 → 26.10.6

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 (451) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +15 -0
  3. package/app.plugin.js +2 -0
  4. package/lib/commonjs/assets/icons.js +2 -2
  5. package/lib/commonjs/assets/icons.js.map +1 -1
  6. package/lib/commonjs/components/MainComponents/AlbumGrid.js +152 -0
  7. package/lib/commonjs/components/MainComponents/AlbumGrid.js.map +1 -0
  8. package/lib/commonjs/components/MainComponents/ChatRoom.js +2 -2
  9. package/lib/commonjs/components/MainComponents/ChatRoom.js.map +1 -1
  10. package/lib/commonjs/components/MainComponents/MediaMessage.js +199 -123
  11. package/lib/commonjs/components/MainComponents/MediaMessage.js.map +1 -1
  12. package/lib/commonjs/components/MainComponents/MediaUploadOverlay.js +109 -0
  13. package/lib/commonjs/components/MainComponents/MediaUploadOverlay.js.map +1 -0
  14. package/lib/commonjs/components/MessageBubble/MediaCaption.js +51 -0
  15. package/lib/commonjs/components/MessageBubble/MediaCaption.js.map +1 -0
  16. package/lib/commonjs/components/MessageBubble/Message.js +40 -1
  17. package/lib/commonjs/components/MessageBubble/Message.js.map +1 -1
  18. package/lib/commonjs/components/MessageBubble/MessageInteractions.js +2 -1
  19. package/lib/commonjs/components/MessageBubble/MessageInteractions.js.map +1 -1
  20. package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js +75 -0
  21. package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
  22. package/lib/commonjs/components/RoomComponents/LastMessageItem.js +20 -4
  23. package/lib/commonjs/components/RoomComponents/LastMessageItem.js.map +1 -1
  24. package/lib/commonjs/components/RoomComponents/styled/LastAudioMessage.js +3 -2
  25. package/lib/commonjs/components/RoomComponents/styled/LastAudioMessage.js.map +1 -1
  26. package/lib/commonjs/components/RoomComponents/styled/LastMessageFile.js +3 -2
  27. package/lib/commonjs/components/RoomComponents/styled/LastMessageFile.js.map +1 -1
  28. package/lib/commonjs/components/RoomComponents/styled/LastMessagePhoto.js +3 -2
  29. package/lib/commonjs/components/RoomComponents/styled/LastMessagePhoto.js.map +1 -1
  30. package/lib/commonjs/components/RoomComponents/styled/LastMessageVideo.js +3 -2
  31. package/lib/commonjs/components/RoomComponents/styled/LastMessageVideo.js.map +1 -1
  32. package/lib/commonjs/components/Thread/ThreadWrapper.js +3 -2
  33. package/lib/commonjs/components/Thread/ThreadWrapper.js.map +1 -1
  34. package/lib/commonjs/components/VideoCalls/ActiveCallBar.js +61 -16
  35. package/lib/commonjs/components/VideoCalls/ActiveCallBar.js.map +1 -1
  36. package/lib/commonjs/components/VideoCalls/CallIcons.js +41 -16
  37. package/lib/commonjs/components/VideoCalls/CallIcons.js.map +1 -1
  38. package/lib/commonjs/components/VideoCalls/VideoCallOverlay.js +183 -9
  39. package/lib/commonjs/components/VideoCalls/VideoCallOverlay.js.map +1 -1
  40. package/lib/commonjs/components/VideoCalls/VideoCallSession.js +40 -15
  41. package/lib/commonjs/components/VideoCalls/VideoCallSession.js.map +1 -1
  42. package/lib/commonjs/components/VideoCalls/callSessionController.js +49 -26
  43. package/lib/commonjs/components/VideoCalls/callSessionController.js.map +1 -1
  44. package/lib/commonjs/components/VideoCalls/callUiStore.js +33 -4
  45. package/lib/commonjs/components/VideoCalls/callUiStore.js.map +1 -1
  46. package/lib/commonjs/components/VideoCalls/livekitRuntime.js +9 -1
  47. package/lib/commonjs/components/VideoCalls/livekitRuntime.js.map +1 -1
  48. package/lib/commonjs/components/VideoCalls/useCallKeep.js +197 -28
  49. package/lib/commonjs/components/VideoCalls/useCallKeep.js.map +1 -1
  50. package/lib/commonjs/components/VideoCalls/useOngoingCallNotification.js +88 -0
  51. package/lib/commonjs/components/VideoCalls/useOngoingCallNotification.js.map +1 -0
  52. package/lib/commonjs/components/VideoCalls/usePipTile.js +232 -0
  53. package/lib/commonjs/components/VideoCalls/usePipTile.js.map +1 -0
  54. package/lib/commonjs/components/styled/MediaFilePreview.js +60 -47
  55. package/lib/commonjs/components/styled/MediaFilePreview.js.map +1 -1
  56. package/lib/commonjs/components/styled/MessageImage.js +16 -6
  57. package/lib/commonjs/components/styled/MessageImage.js.map +1 -1
  58. package/lib/commonjs/components/styled/SendInput.js +99 -68
  59. package/lib/commonjs/components/styled/SendInput.js.map +1 -1
  60. package/lib/commonjs/components/styled/VideoMessage.js +24 -10
  61. package/lib/commonjs/components/styled/VideoMessage.js.map +1 -1
  62. package/lib/commonjs/context/xmppProvider.js +46 -0
  63. package/lib/commonjs/context/xmppProvider.js.map +1 -1
  64. package/lib/commonjs/e2ee/fileEnvelope.js +6 -0
  65. package/lib/commonjs/e2ee/fileEnvelope.js.map +1 -1
  66. package/lib/commonjs/e2ee/sealedBody.js +32 -2
  67. package/lib/commonjs/e2ee/sealedBody.js.map +1 -1
  68. package/lib/commonjs/helpers/albumLayout.js +150 -0
  69. package/lib/commonjs/helpers/albumLayout.js.map +1 -0
  70. package/lib/commonjs/helpers/attachments.js +255 -0
  71. package/lib/commonjs/helpers/attachments.js.map +1 -0
  72. package/lib/commonjs/helpers/caption.js +214 -0
  73. package/lib/commonjs/helpers/caption.js.map +1 -0
  74. package/lib/commonjs/helpers/createMainMessageForThread.js +5 -1
  75. package/lib/commonjs/helpers/createMainMessageForThread.js.map +1 -1
  76. package/lib/commonjs/helpers/createMessageFromXml.js +19 -5
  77. package/lib/commonjs/helpers/createMessageFromXml.js.map +1 -1
  78. package/lib/commonjs/helpers/fileKind.js +40 -0
  79. package/lib/commonjs/helpers/fileKind.js.map +1 -0
  80. package/lib/commonjs/helpers/getDataFromXml.js +6 -1
  81. package/lib/commonjs/helpers/getDataFromXml.js.map +1 -1
  82. package/lib/commonjs/helpers/mapWithConcurrency.js +41 -0
  83. package/lib/commonjs/helpers/mapWithConcurrency.js.map +1 -0
  84. package/lib/commonjs/helpers/mediaLimits.js +33 -0
  85. package/lib/commonjs/helpers/mediaLimits.js.map +1 -0
  86. package/lib/commonjs/helpers/mediaSendRetry.js +64 -0
  87. package/lib/commonjs/helpers/mediaSendRetry.js.map +1 -0
  88. package/lib/commonjs/helpers/mediaWireData.js +62 -0
  89. package/lib/commonjs/helpers/mediaWireData.js.map +1 -0
  90. package/lib/commonjs/helpers/mentionSpans.js +19 -0
  91. package/lib/commonjs/helpers/mentionSpans.js.map +1 -0
  92. package/lib/commonjs/helpers/mimeToExtension.js +45 -0
  93. package/lib/commonjs/helpers/mimeToExtension.js.map +1 -1
  94. package/lib/commonjs/helpers/readReceipts.js +173 -0
  95. package/lib/commonjs/helpers/readReceipts.js.map +1 -0
  96. package/lib/commonjs/helpers/repairFilenameEncoding.js +79 -0
  97. package/lib/commonjs/helpers/repairFilenameEncoding.js.map +1 -0
  98. package/lib/commonjs/helpers/safeJson.js +18 -0
  99. package/lib/commonjs/helpers/safeJson.js.map +1 -0
  100. package/lib/commonjs/helpers/updateMessagesTillLast.js +3 -1
  101. package/lib/commonjs/helpers/updateMessagesTillLast.js.map +1 -1
  102. package/lib/commonjs/hooks/useSendMessage.js +384 -193
  103. package/lib/commonjs/hooks/useSendMessage.js.map +1 -1
  104. package/lib/commonjs/i18n/strings.js +144 -0
  105. package/lib/commonjs/i18n/strings.js.map +1 -1
  106. package/lib/commonjs/main.js.map +1 -1
  107. package/lib/commonjs/networking/api-requests/auth.api.js +62 -3
  108. package/lib/commonjs/networking/api-requests/auth.api.js.map +1 -1
  109. package/lib/commonjs/networking/stanzaHandlers.js +100 -3
  110. package/lib/commonjs/networking/stanzaHandlers.js.map +1 -1
  111. package/lib/commonjs/networking/xmpp/handleStanzas.xmpp.js +4 -2
  112. package/lib/commonjs/networking/xmpp/handleStanzas.xmpp.js.map +1 -1
  113. package/lib/commonjs/networking/xmpp/sendDisplayedMarker.xmpp.js +28 -0
  114. package/lib/commonjs/networking/xmpp/sendDisplayedMarker.xmpp.js.map +1 -0
  115. package/lib/commonjs/networking/xmpp/sendMediaMessage.xmpp.js +53 -2
  116. package/lib/commonjs/networking/xmpp/sendMediaMessage.xmpp.js.map +1 -1
  117. package/lib/commonjs/networking/xmpp/sendReceivedReceipt.xmpp.js +28 -0
  118. package/lib/commonjs/networking/xmpp/sendReceivedReceipt.xmpp.js.map +1 -0
  119. package/lib/commonjs/networking/xmppClient.js +64 -0
  120. package/lib/commonjs/networking/xmppClient.js.map +1 -1
  121. package/lib/commonjs/roomStore/persistence.js +78 -5
  122. package/lib/commonjs/roomStore/persistence.js.map +1 -1
  123. package/lib/commonjs/roomStore/roomHeapSlice.js +4 -0
  124. package/lib/commonjs/roomStore/roomHeapSlice.js.map +1 -1
  125. package/lib/commonjs/roomStore/roomsSlice.js +114 -1
  126. package/lib/commonjs/roomStore/roomsSlice.js.map +1 -1
  127. package/lib/commonjs/types/models/attachment.model.js +2 -0
  128. package/lib/commonjs/types/models/attachment.model.js.map +1 -0
  129. package/lib/commonjs/version.js +1 -1
  130. package/lib/module/assets/icons.js +1 -1
  131. package/lib/module/assets/icons.js.map +1 -1
  132. package/lib/module/components/MainComponents/AlbumGrid.js +146 -0
  133. package/lib/module/components/MainComponents/AlbumGrid.js.map +1 -0
  134. package/lib/module/components/MainComponents/ChatRoom.js +2 -2
  135. package/lib/module/components/MainComponents/ChatRoom.js.map +1 -1
  136. package/lib/module/components/MainComponents/MediaMessage.js +200 -124
  137. package/lib/module/components/MainComponents/MediaMessage.js.map +1 -1
  138. package/lib/module/components/MainComponents/MediaUploadOverlay.js +104 -0
  139. package/lib/module/components/MainComponents/MediaUploadOverlay.js.map +1 -0
  140. package/lib/module/components/MessageBubble/MediaCaption.js +46 -0
  141. package/lib/module/components/MessageBubble/MediaCaption.js.map +1 -0
  142. package/lib/module/components/MessageBubble/Message.js +44 -5
  143. package/lib/module/components/MessageBubble/Message.js.map +1 -1
  144. package/lib/module/components/MessageBubble/MessageInteractions.js +2 -1
  145. package/lib/module/components/MessageBubble/MessageInteractions.js.map +1 -1
  146. package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js +75 -0
  147. package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
  148. package/lib/module/components/RoomComponents/LastMessageItem.js +20 -4
  149. package/lib/module/components/RoomComponents/LastMessageItem.js.map +1 -1
  150. package/lib/module/components/RoomComponents/styled/LastAudioMessage.js +3 -2
  151. package/lib/module/components/RoomComponents/styled/LastAudioMessage.js.map +1 -1
  152. package/lib/module/components/RoomComponents/styled/LastMessageFile.js +3 -2
  153. package/lib/module/components/RoomComponents/styled/LastMessageFile.js.map +1 -1
  154. package/lib/module/components/RoomComponents/styled/LastMessagePhoto.js +3 -2
  155. package/lib/module/components/RoomComponents/styled/LastMessagePhoto.js.map +1 -1
  156. package/lib/module/components/RoomComponents/styled/LastMessageVideo.js +3 -2
  157. package/lib/module/components/RoomComponents/styled/LastMessageVideo.js.map +1 -1
  158. package/lib/module/components/Thread/ThreadWrapper.js +3 -2
  159. package/lib/module/components/Thread/ThreadWrapper.js.map +1 -1
  160. package/lib/module/components/VideoCalls/ActiveCallBar.js +63 -18
  161. package/lib/module/components/VideoCalls/ActiveCallBar.js.map +1 -1
  162. package/lib/module/components/VideoCalls/CallIcons.js +38 -15
  163. package/lib/module/components/VideoCalls/CallIcons.js.map +1 -1
  164. package/lib/module/components/VideoCalls/VideoCallOverlay.js +185 -11
  165. package/lib/module/components/VideoCalls/VideoCallOverlay.js.map +1 -1
  166. package/lib/module/components/VideoCalls/VideoCallSession.js +41 -16
  167. package/lib/module/components/VideoCalls/VideoCallSession.js.map +1 -1
  168. package/lib/module/components/VideoCalls/callSessionController.js +46 -24
  169. package/lib/module/components/VideoCalls/callSessionController.js.map +1 -1
  170. package/lib/module/components/VideoCalls/callUiStore.js +30 -3
  171. package/lib/module/components/VideoCalls/callUiStore.js.map +1 -1
  172. package/lib/module/components/VideoCalls/livekitRuntime.js +9 -1
  173. package/lib/module/components/VideoCalls/livekitRuntime.js.map +1 -1
  174. package/lib/module/components/VideoCalls/useCallKeep.js +198 -29
  175. package/lib/module/components/VideoCalls/useCallKeep.js.map +1 -1
  176. package/lib/module/components/VideoCalls/useOngoingCallNotification.js +84 -0
  177. package/lib/module/components/VideoCalls/useOngoingCallNotification.js.map +1 -0
  178. package/lib/module/components/VideoCalls/usePipTile.js +228 -0
  179. package/lib/module/components/VideoCalls/usePipTile.js.map +1 -0
  180. package/lib/module/components/styled/MediaFilePreview.js +61 -48
  181. package/lib/module/components/styled/MediaFilePreview.js.map +1 -1
  182. package/lib/module/components/styled/MessageImage.js +16 -6
  183. package/lib/module/components/styled/MessageImage.js.map +1 -1
  184. package/lib/module/components/styled/SendInput.js +99 -68
  185. package/lib/module/components/styled/SendInput.js.map +1 -1
  186. package/lib/module/components/styled/VideoMessage.js +24 -10
  187. package/lib/module/components/styled/VideoMessage.js.map +1 -1
  188. package/lib/module/context/xmppProvider.js +46 -0
  189. package/lib/module/context/xmppProvider.js.map +1 -1
  190. package/lib/module/e2ee/fileEnvelope.js +1 -1
  191. package/lib/module/e2ee/fileEnvelope.js.map +1 -1
  192. package/lib/module/e2ee/sealedBody.js +32 -2
  193. package/lib/module/e2ee/sealedBody.js.map +1 -1
  194. package/lib/module/helpers/albumLayout.js +143 -0
  195. package/lib/module/helpers/albumLayout.js.map +1 -0
  196. package/lib/module/helpers/attachments.js +245 -0
  197. package/lib/module/helpers/attachments.js.map +1 -0
  198. package/lib/module/helpers/caption.js +207 -0
  199. package/lib/module/helpers/caption.js.map +1 -0
  200. package/lib/module/helpers/createMainMessageForThread.js +5 -1
  201. package/lib/module/helpers/createMainMessageForThread.js.map +1 -1
  202. package/lib/module/helpers/createMessageFromXml.js +20 -6
  203. package/lib/module/helpers/createMessageFromXml.js.map +1 -1
  204. package/lib/module/helpers/fileKind.js +34 -0
  205. package/lib/module/helpers/fileKind.js.map +1 -0
  206. package/lib/module/helpers/getDataFromXml.js +6 -1
  207. package/lib/module/helpers/getDataFromXml.js.map +1 -1
  208. package/lib/module/helpers/mapWithConcurrency.js +37 -0
  209. package/lib/module/helpers/mapWithConcurrency.js.map +1 -0
  210. package/lib/module/helpers/mediaLimits.js +27 -0
  211. package/lib/module/helpers/mediaLimits.js.map +1 -0
  212. package/lib/module/helpers/mediaSendRetry.js +55 -0
  213. package/lib/module/helpers/mediaSendRetry.js.map +1 -0
  214. package/lib/module/helpers/mediaWireData.js +58 -0
  215. package/lib/module/helpers/mediaWireData.js.map +1 -0
  216. package/lib/module/helpers/mentionSpans.js +14 -0
  217. package/lib/module/helpers/mentionSpans.js.map +1 -0
  218. package/lib/module/helpers/mimeToExtension.js +43 -0
  219. package/lib/module/helpers/mimeToExtension.js.map +1 -1
  220. package/lib/module/helpers/readReceipts.js +160 -0
  221. package/lib/module/helpers/readReceipts.js.map +1 -0
  222. package/lib/module/helpers/repairFilenameEncoding.js +74 -0
  223. package/lib/module/helpers/repairFilenameEncoding.js.map +1 -0
  224. package/lib/module/helpers/safeJson.js +13 -0
  225. package/lib/module/helpers/safeJson.js.map +1 -0
  226. package/lib/module/helpers/updateMessagesTillLast.js +3 -1
  227. package/lib/module/helpers/updateMessagesTillLast.js.map +1 -1
  228. package/lib/module/hooks/useSendMessage.js +387 -195
  229. package/lib/module/hooks/useSendMessage.js.map +1 -1
  230. package/lib/module/i18n/strings.js +144 -0
  231. package/lib/module/i18n/strings.js.map +1 -1
  232. package/lib/module/main.js.map +1 -1
  233. package/lib/module/networking/api-requests/auth.api.js +62 -3
  234. package/lib/module/networking/api-requests/auth.api.js.map +1 -1
  235. package/lib/module/networking/stanzaHandlers.js +97 -4
  236. package/lib/module/networking/stanzaHandlers.js.map +1 -1
  237. package/lib/module/networking/xmpp/handleStanzas.xmpp.js +5 -3
  238. package/lib/module/networking/xmpp/handleStanzas.xmpp.js.map +1 -1
  239. package/lib/module/networking/xmpp/sendDisplayedMarker.xmpp.js +25 -0
  240. package/lib/module/networking/xmpp/sendDisplayedMarker.xmpp.js.map +1 -0
  241. package/lib/module/networking/xmpp/sendMediaMessage.xmpp.js +53 -2
  242. package/lib/module/networking/xmpp/sendMediaMessage.xmpp.js.map +1 -1
  243. package/lib/module/networking/xmpp/sendReceivedReceipt.xmpp.js +25 -0
  244. package/lib/module/networking/xmpp/sendReceivedReceipt.xmpp.js.map +1 -0
  245. package/lib/module/networking/xmppClient.js +64 -0
  246. package/lib/module/networking/xmppClient.js.map +1 -1
  247. package/lib/module/roomStore/persistence.js +78 -5
  248. package/lib/module/roomStore/persistence.js.map +1 -1
  249. package/lib/module/roomStore/roomHeapSlice.js +4 -0
  250. package/lib/module/roomStore/roomHeapSlice.js.map +1 -1
  251. package/lib/module/roomStore/roomsSlice.js +109 -0
  252. package/lib/module/roomStore/roomsSlice.js.map +1 -1
  253. package/lib/module/types/models/attachment.model.js +2 -0
  254. package/lib/module/types/models/attachment.model.js.map +1 -0
  255. package/lib/module/version.js +1 -1
  256. package/lib/typescript/assets/icons.d.ts +6 -1
  257. package/lib/typescript/assets/icons.d.ts.map +1 -1
  258. package/lib/typescript/components/MainComponents/AlbumGrid.d.ts +21 -0
  259. package/lib/typescript/components/MainComponents/AlbumGrid.d.ts.map +1 -0
  260. package/lib/typescript/components/MainComponents/ChatRoom.d.ts.map +1 -1
  261. package/lib/typescript/components/MainComponents/MediaMessage.d.ts.map +1 -1
  262. package/lib/typescript/components/MainComponents/MediaUploadOverlay.d.ts +17 -0
  263. package/lib/typescript/components/MainComponents/MediaUploadOverlay.d.ts.map +1 -0
  264. package/lib/typescript/components/MessageBubble/MediaCaption.d.ts +12 -0
  265. package/lib/typescript/components/MessageBubble/MediaCaption.d.ts.map +1 -0
  266. package/lib/typescript/components/MessageBubble/Message.d.ts.map +1 -1
  267. package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts.map +1 -1
  268. package/lib/typescript/components/Modals/FilePreviewModal/FilePreviewModal.d.ts.map +1 -1
  269. package/lib/typescript/components/RoomComponents/LastMessageItem.d.ts.map +1 -1
  270. package/lib/typescript/components/RoomComponents/styled/LastAudioMessage.d.ts +2 -0
  271. package/lib/typescript/components/RoomComponents/styled/LastAudioMessage.d.ts.map +1 -1
  272. package/lib/typescript/components/RoomComponents/styled/LastMessageFile.d.ts +2 -0
  273. package/lib/typescript/components/RoomComponents/styled/LastMessageFile.d.ts.map +1 -1
  274. package/lib/typescript/components/RoomComponents/styled/LastMessagePhoto.d.ts +2 -0
  275. package/lib/typescript/components/RoomComponents/styled/LastMessagePhoto.d.ts.map +1 -1
  276. package/lib/typescript/components/RoomComponents/styled/LastMessageVideo.d.ts +2 -0
  277. package/lib/typescript/components/RoomComponents/styled/LastMessageVideo.d.ts.map +1 -1
  278. package/lib/typescript/components/Thread/ThreadWrapper.d.ts.map +1 -1
  279. package/lib/typescript/components/VideoCalls/ActiveCallBar.d.ts +9 -3
  280. package/lib/typescript/components/VideoCalls/ActiveCallBar.d.ts.map +1 -1
  281. package/lib/typescript/components/VideoCalls/CallIcons.d.ts +9 -0
  282. package/lib/typescript/components/VideoCalls/CallIcons.d.ts.map +1 -1
  283. package/lib/typescript/components/VideoCalls/VideoCallOverlay.d.ts.map +1 -1
  284. package/lib/typescript/components/VideoCalls/VideoCallSession.d.ts.map +1 -1
  285. package/lib/typescript/components/VideoCalls/callSessionController.d.ts +7 -0
  286. package/lib/typescript/components/VideoCalls/callSessionController.d.ts.map +1 -1
  287. package/lib/typescript/components/VideoCalls/callUiStore.d.ts +13 -0
  288. package/lib/typescript/components/VideoCalls/callUiStore.d.ts.map +1 -1
  289. package/lib/typescript/components/VideoCalls/livekitRuntime.d.ts +5 -0
  290. package/lib/typescript/components/VideoCalls/livekitRuntime.d.ts.map +1 -1
  291. package/lib/typescript/components/VideoCalls/useCallKeep.d.ts +12 -0
  292. package/lib/typescript/components/VideoCalls/useCallKeep.d.ts.map +1 -1
  293. package/lib/typescript/components/VideoCalls/useOngoingCallNotification.d.ts +10 -0
  294. package/lib/typescript/components/VideoCalls/useOngoingCallNotification.d.ts.map +1 -0
  295. package/lib/typescript/components/VideoCalls/usePipTile.d.ts +46 -0
  296. package/lib/typescript/components/VideoCalls/usePipTile.d.ts.map +1 -0
  297. package/lib/typescript/components/styled/MediaFilePreview.d.ts.map +1 -1
  298. package/lib/typescript/components/styled/MessageImage.d.ts +10 -0
  299. package/lib/typescript/components/styled/MessageImage.d.ts.map +1 -1
  300. package/lib/typescript/components/styled/SendInput.d.ts +7 -1
  301. package/lib/typescript/components/styled/SendInput.d.ts.map +1 -1
  302. package/lib/typescript/components/styled/VideoMessage.d.ts +7 -0
  303. package/lib/typescript/components/styled/VideoMessage.d.ts.map +1 -1
  304. package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
  305. package/lib/typescript/e2ee/fileEnvelope.d.ts +1 -1
  306. package/lib/typescript/e2ee/fileEnvelope.d.ts.map +1 -1
  307. package/lib/typescript/e2ee/sealedBody.d.ts +16 -0
  308. package/lib/typescript/e2ee/sealedBody.d.ts.map +1 -1
  309. package/lib/typescript/helpers/albumLayout.d.ts +66 -0
  310. package/lib/typescript/helpers/albumLayout.d.ts.map +1 -0
  311. package/lib/typescript/helpers/attachments.d.ts +55 -0
  312. package/lib/typescript/helpers/attachments.d.ts.map +1 -0
  313. package/lib/typescript/helpers/caption.d.ts +64 -0
  314. package/lib/typescript/helpers/caption.d.ts.map +1 -0
  315. package/lib/typescript/helpers/createMainMessageForThread.d.ts.map +1 -1
  316. package/lib/typescript/helpers/createMessageFromXml.d.ts.map +1 -1
  317. package/lib/typescript/helpers/fileKind.d.ts +20 -0
  318. package/lib/typescript/helpers/fileKind.d.ts.map +1 -0
  319. package/lib/typescript/helpers/getDataFromXml.d.ts.map +1 -1
  320. package/lib/typescript/helpers/mapWithConcurrency.d.ts +9 -0
  321. package/lib/typescript/helpers/mapWithConcurrency.d.ts.map +1 -0
  322. package/lib/typescript/helpers/mediaLimits.d.ts +13 -0
  323. package/lib/typescript/helpers/mediaLimits.d.ts.map +1 -0
  324. package/lib/typescript/helpers/mediaSendRetry.d.ts +26 -0
  325. package/lib/typescript/helpers/mediaSendRetry.d.ts.map +1 -0
  326. package/lib/typescript/helpers/mediaWireData.d.ts +16 -0
  327. package/lib/typescript/helpers/mediaWireData.d.ts.map +1 -0
  328. package/lib/typescript/helpers/mentionSpans.d.ts +8 -0
  329. package/lib/typescript/helpers/mentionSpans.d.ts.map +1 -0
  330. package/lib/typescript/helpers/mimeToExtension.d.ts +7 -0
  331. package/lib/typescript/helpers/mimeToExtension.d.ts.map +1 -1
  332. package/lib/typescript/helpers/readReceipts.d.ts +82 -0
  333. package/lib/typescript/helpers/readReceipts.d.ts.map +1 -0
  334. package/lib/typescript/helpers/repairFilenameEncoding.d.ts +12 -0
  335. package/lib/typescript/helpers/repairFilenameEncoding.d.ts.map +1 -0
  336. package/lib/typescript/helpers/safeJson.d.ts +2 -0
  337. package/lib/typescript/helpers/safeJson.d.ts.map +1 -0
  338. package/lib/typescript/helpers/updateMessagesTillLast.d.ts.map +1 -1
  339. package/lib/typescript/hooks/useSendMessage.d.ts +2 -1
  340. package/lib/typescript/hooks/useSendMessage.d.ts.map +1 -1
  341. package/lib/typescript/i18n/strings.d.ts.map +1 -1
  342. package/lib/typescript/main.d.ts +1 -1
  343. package/lib/typescript/main.d.ts.map +1 -1
  344. package/lib/typescript/networking/api-requests/auth.api.d.ts +2 -0
  345. package/lib/typescript/networking/api-requests/auth.api.d.ts.map +1 -1
  346. package/lib/typescript/networking/stanzaHandlers.d.ts +7 -3
  347. package/lib/typescript/networking/stanzaHandlers.d.ts.map +1 -1
  348. package/lib/typescript/networking/xmpp/handleStanzas.xmpp.d.ts.map +1 -1
  349. package/lib/typescript/networking/xmpp/sendDisplayedMarker.xmpp.d.ts +3 -0
  350. package/lib/typescript/networking/xmpp/sendDisplayedMarker.xmpp.d.ts.map +1 -0
  351. package/lib/typescript/networking/xmpp/sendMediaMessage.xmpp.d.ts +12 -0
  352. package/lib/typescript/networking/xmpp/sendMediaMessage.xmpp.d.ts.map +1 -1
  353. package/lib/typescript/networking/xmpp/sendReceivedReceipt.xmpp.d.ts +3 -0
  354. package/lib/typescript/networking/xmpp/sendReceivedReceipt.xmpp.d.ts.map +1 -0
  355. package/lib/typescript/networking/xmppClient.d.ts +15 -0
  356. package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
  357. package/lib/typescript/roomStore/persistence.d.ts.map +1 -1
  358. package/lib/typescript/roomStore/roomHeapSlice.d.ts +22 -1
  359. package/lib/typescript/roomStore/roomHeapSlice.d.ts.map +1 -1
  360. package/lib/typescript/roomStore/roomsSlice.d.ts +67 -3
  361. package/lib/typescript/roomStore/roomsSlice.d.ts.map +1 -1
  362. package/lib/typescript/types/models/attachment.model.d.ts +52 -0
  363. package/lib/typescript/types/models/attachment.model.d.ts.map +1 -0
  364. package/lib/typescript/types/models/message.model.d.ts +26 -0
  365. package/lib/typescript/types/models/message.model.d.ts.map +1 -1
  366. package/lib/typescript/types/models/modal.model.d.ts +7 -0
  367. package/lib/typescript/types/models/modal.model.d.ts.map +1 -1
  368. package/lib/typescript/types/models/room.model.d.ts +2 -0
  369. package/lib/typescript/types/models/room.model.d.ts.map +1 -1
  370. package/lib/typescript/types/types.d.ts +69 -1
  371. package/lib/typescript/types/types.d.ts.map +1 -1
  372. package/lib/typescript/version.d.ts +1 -1
  373. package/package.json +4 -1
  374. package/plugins/notification-service/Info.plist +31 -0
  375. package/plugins/notification-service/NotificationService.swift +82 -0
  376. package/plugins/withNotificationServiceExtension.js +137 -0
  377. package/src/assets/icons.tsx +20 -2
  378. package/src/components/MainComponents/AlbumGrid.tsx +183 -0
  379. package/src/components/MainComponents/ChatRoom.tsx +4 -2
  380. package/src/components/MainComponents/MediaMessage.tsx +246 -130
  381. package/src/components/MainComponents/MediaUploadOverlay.tsx +102 -0
  382. package/src/components/MessageBubble/MediaCaption.tsx +45 -0
  383. package/src/components/MessageBubble/Message.tsx +71 -3
  384. package/src/components/MessageBubble/MessageInteractions.tsx +2 -1
  385. package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +64 -0
  386. package/src/components/RoomComponents/LastMessageItem.tsx +15 -4
  387. package/src/components/RoomComponents/styled/LastAudioMessage.tsx +6 -3
  388. package/src/components/RoomComponents/styled/LastMessageFile.tsx +6 -2
  389. package/src/components/RoomComponents/styled/LastMessagePhoto.tsx +6 -2
  390. package/src/components/RoomComponents/styled/LastMessageVideo.tsx +6 -2
  391. package/src/components/Thread/ThreadWrapper.tsx +5 -9
  392. package/src/components/VideoCalls/ActiveCallBar.tsx +56 -10
  393. package/src/components/VideoCalls/CallIcons.tsx +36 -12
  394. package/src/components/VideoCalls/VideoCallOverlay.tsx +205 -8
  395. package/src/components/VideoCalls/VideoCallSession.tsx +45 -13
  396. package/src/components/VideoCalls/callSessionController.ts +51 -20
  397. package/src/components/VideoCalls/callUiStore.ts +33 -3
  398. package/src/components/VideoCalls/livekitRuntime.ts +13 -0
  399. package/src/components/VideoCalls/useCallKeep.ts +204 -33
  400. package/src/components/VideoCalls/useOngoingCallNotification.ts +113 -0
  401. package/src/components/VideoCalls/usePipTile.ts +214 -0
  402. package/src/components/styled/MediaFilePreview.tsx +49 -34
  403. package/src/components/styled/MessageImage.tsx +21 -5
  404. package/src/components/styled/SendInput.tsx +101 -87
  405. package/src/components/styled/VideoMessage.tsx +34 -9
  406. package/src/context/xmppProvider.tsx +32 -0
  407. package/src/e2ee/fileEnvelope.ts +1 -1
  408. package/src/e2ee/sealedBody.ts +44 -5
  409. package/src/helpers/albumLayout.ts +207 -0
  410. package/src/helpers/attachments.ts +316 -0
  411. package/src/helpers/caption.ts +283 -0
  412. package/src/helpers/createMainMessageForThread.ts +6 -1
  413. package/src/helpers/createMessageFromXml.ts +21 -5
  414. package/src/helpers/fileKind.ts +38 -0
  415. package/src/helpers/getDataFromXml.ts +5 -1
  416. package/src/helpers/mapWithConcurrency.ts +32 -0
  417. package/src/helpers/mediaLimits.ts +27 -0
  418. package/src/helpers/mediaSendRetry.ts +59 -0
  419. package/src/helpers/mediaWireData.ts +57 -0
  420. package/src/helpers/mentionSpans.ts +17 -0
  421. package/src/helpers/mimeToExtension.ts +51 -0
  422. package/src/helpers/readReceipts.ts +219 -0
  423. package/src/helpers/repairFilenameEncoding.ts +82 -0
  424. package/src/helpers/safeJson.ts +11 -0
  425. package/src/helpers/updateMessagesTillLast.tsx +2 -1
  426. package/src/hooks/useSendMessage.tsx +448 -249
  427. package/src/i18n/strings.ts +144 -0
  428. package/src/main.ts +3 -0
  429. package/src/networking/api-requests/auth.api.ts +60 -3
  430. package/src/networking/stanzaHandlers.ts +91 -2
  431. package/src/networking/xmpp/handleStanzas.xmpp.ts +6 -2
  432. package/src/networking/xmpp/sendDisplayedMarker.xmpp.ts +31 -0
  433. package/src/networking/xmpp/sendMediaMessage.xmpp.ts +60 -3
  434. package/src/networking/xmpp/sendReceivedReceipt.xmpp.ts +31 -0
  435. package/src/networking/xmppClient.ts +48 -0
  436. package/src/roomStore/persistence.ts +94 -3
  437. package/src/roomStore/roomHeapSlice.ts +23 -1
  438. package/src/roomStore/roomsSlice.ts +112 -1
  439. package/src/types/models/attachment.model.ts +51 -0
  440. package/src/types/models/message.model.ts +29 -0
  441. package/src/types/models/modal.model.ts +7 -0
  442. package/src/types/models/room.model.ts +2 -0
  443. package/src/types/types.ts +83 -1
  444. package/src/version.ts +1 -1
  445. package/lib/commonjs/components/VideoCalls/useDraggable.js +0 -59
  446. package/lib/commonjs/components/VideoCalls/useDraggable.js.map +0 -1
  447. package/lib/module/components/VideoCalls/useDraggable.js +0 -55
  448. package/lib/module/components/VideoCalls/useDraggable.js.map +0 -1
  449. package/lib/typescript/components/VideoCalls/useDraggable.d.ts +0 -19
  450. package/lib/typescript/components/VideoCalls/useDraggable.d.ts.map +0 -1
  451. package/src/components/VideoCalls/useDraggable.ts +0 -46
@@ -720,7 +720,25 @@ export const ArowDownIcon = ({
720
720
  </Svg>
721
721
  );
722
722
 
723
- export const DoubleTick = ({ fill = '#fff', color = '#0052CD', ...props }) => (
723
+ export const SingleTick = ({ stroke = '#16A34A', ...props }) => (
724
+ <Svg
725
+ width="16"
726
+ height="16"
727
+ viewBox="0 0 16 16"
728
+ fill="none"
729
+ {...props}
730
+ >
731
+ <Path
732
+ d="M4 8.5L7 11.5L12 5.5"
733
+ stroke={stroke}
734
+ strokeWidth="1.5"
735
+ strokeLinecap="round"
736
+ strokeLinejoin="round"
737
+ />
738
+ </Svg>
739
+ );
740
+
741
+ export const DoubleTick = ({ fill = '#fff', color = '#0052CD', stroke = '#16A34A', ...props }) => (
724
742
  <Svg
725
743
  width="16"
726
744
  height="16"
@@ -730,7 +748,7 @@ export const DoubleTick = ({ fill = '#fff', color = '#0052CD', ...props }) => (
730
748
  >
731
749
  <Path
732
750
  d="M1.75 9.75L4.25 12.25M7.75 8.25L10.25 5.75M5.75 9.75L8.25 12.25L14.25 5.75"
733
- stroke="#16A34A"
751
+ stroke={stroke}
734
752
  strokeWidth="1.5"
735
753
  strokeLinecap="round"
736
754
  strokeLinejoin="round"
@@ -0,0 +1,183 @@
1
+ import React, { useCallback, useMemo } from 'react';
2
+ import { Pressable, StyleSheet, Text, View } from 'react-native';
3
+ import { useDispatch } from 'react-redux';
4
+ import { IAttachment, ModalFile } from '../../types/types';
5
+ import CustomMessageImage from '../styled/MessageImage';
6
+ import CustomMessageVideo from '../styled/VideoMessage';
7
+ import { getAttachmentName } from '../../helpers/attachments';
8
+ import { getFileKind } from '../../helpers/fileKind';
9
+ import { getAlbumFrame, getAlbumLayout } from '../../helpers/albumLayout';
10
+ import { MEDIA_MAX_W } from '../../helpers/mediaDimensions';
11
+ import { appendFileToken } from '../../helpers/secureFileUrl';
12
+ import { useFileToken } from '../../hooks/useFileToken';
13
+ import { useT } from '../../i18n/useT';
14
+ import {
15
+ setActiveFile,
16
+ setActiveModal,
17
+ } from '../../roomStore/chatSettingsSlice';
18
+ import { MODAL_TYPES } from '../../helpers/constants/MODAL_TYPES';
19
+ import MediaUploadOverlay from './MediaUploadOverlay';
20
+
21
+ interface AlbumGridProps {
22
+ /** The pictures and videos of one message, in order. */
23
+ attachments: IAttachment[];
24
+ /** Width of the whole grid; the bubble's media width by default. */
25
+ width?: number;
26
+ /** Re-runs the upload of the tile at this position (a failed outgoing file). */
27
+ onRetryTile?: (position: number) => void;
28
+ }
29
+
30
+ /**
31
+ * Photo and video album of a message with several media files. The geometry
32
+ * comes from getAlbumLayout / getAlbumFrame; every tile is the ordinary
33
+ * picture or video component sized to its cell. A tap opens the viewer on
34
+ * that file with the whole album as its siblings, so the viewer can page
35
+ * through files the grid does not show. The last tile of a long album carries
36
+ * a "+N" overlay for the files folded behind it.
37
+ */
38
+ const AlbumGrid: React.FC<AlbumGridProps> = ({
39
+ attachments,
40
+ width = MEDIA_MAX_W,
41
+ onRetryTile,
42
+ }) => {
43
+ const t = useT();
44
+ const dispatch = useDispatch();
45
+ const fileToken = useFileToken();
46
+
47
+ const layout = useMemo(
48
+ () => getAlbumLayout(attachments.length),
49
+ [attachments.length]
50
+ );
51
+ const frame = useMemo(() => getAlbumFrame(layout, width), [layout, width]);
52
+
53
+ const siblings: ModalFile[] = useMemo(
54
+ () =>
55
+ attachments.map((attachment) => ({
56
+ fileName: getAttachmentName(attachment),
57
+ originalName: attachment.originalName,
58
+ fileURL: appendFileToken(attachment.location, fileToken) || '',
59
+ mimetype: attachment.mimetype || '',
60
+ duration: attachment.duration,
61
+ })),
62
+ [attachments, fileToken]
63
+ );
64
+
65
+ const openAt = useCallback(
66
+ (index: number) => {
67
+ const target = siblings[index];
68
+ // A file still on its way up has no server copy to open yet.
69
+ if (!target || !target.fileURL || attachments[index]?.uploadStatus) {return;}
70
+ dispatch(setActiveFile({ ...target, siblings, index }));
71
+ dispatch(setActiveModal(MODAL_TYPES.FILE_PREVIEW));
72
+ },
73
+ [attachments, dispatch, siblings]
74
+ );
75
+
76
+ return (
77
+ <View
78
+ testID="album-grid"
79
+ style={[styles.grid, { width: frame.width, height: frame.height }]}
80
+ >
81
+ {attachments.slice(0, layout.visible).map((attachment, position) => {
82
+ const style = frame.tiles[position]!;
83
+ const tile = {
84
+ width: Number(style.width),
85
+ height: Number(style.height),
86
+ };
87
+ const fileName = getAttachmentName(attachment);
88
+ const kind = getFileKind({
89
+ mimetype: attachment.mimetype,
90
+ name: fileName,
91
+ url: attachment.location,
92
+ duration: attachment.duration,
93
+ });
94
+ // An outgoing tile shows the file on this device until its upload
95
+ // has given it a server copy.
96
+ const outgoing = Boolean(attachment.uploadStatus);
97
+ const fileURL = outgoing
98
+ ? attachment.localUrl || ''
99
+ : appendFileToken(attachment.location, fileToken) || '';
100
+ const preview = outgoing
101
+ ? attachment.localUrl || undefined
102
+ : appendFileToken(attachment.locationPreview, fileToken);
103
+ const showMore = position === layout.visible - 1 && layout.hidden > 0;
104
+ const open = () => openAt(position);
105
+
106
+ return (
107
+ <View
108
+ key={attachment.attachmentId || `${position}-${fileName}`}
109
+ testID={`album-tile-${position}`}
110
+ style={style}
111
+ >
112
+ {kind === 'video' ? (
113
+ <CustomMessageVideo
114
+ fileName={fileName}
115
+ originalName={attachment.originalName}
116
+ fileURL={fileURL}
117
+ previewURL={preview}
118
+ mimetype={attachment.mimetype || ''}
119
+ tile={tile}
120
+ onOpen={outgoing ? undefined : open}
121
+ />
122
+ ) : (
123
+ <CustomMessageImage
124
+ fileName={fileName}
125
+ originalName={attachment.originalName}
126
+ fileURL={fileURL}
127
+ locationPreview={preview}
128
+ mimetype={attachment.mimetype || ''}
129
+ tile={tile}
130
+ onOpen={outgoing ? undefined : open}
131
+ />
132
+ )}
133
+ {outgoing && attachment.uploadStatus && (
134
+ <MediaUploadOverlay
135
+ status={attachment.uploadStatus}
136
+ progress={attachment.uploadProgress}
137
+ onRetry={onRetryTile ? () => onRetryTile(position) : undefined}
138
+ />
139
+ )}
140
+ {showMore && (
141
+ <Pressable
142
+ testID="album-more"
143
+ accessibilityRole="button"
144
+ accessibilityLabel={t('attachment.moreLabel', {
145
+ count: layout.hidden,
146
+ })}
147
+ onPress={open}
148
+ style={styles.more}
149
+ >
150
+ <Text style={styles.moreText}>
151
+ {t('attachment.moreCount', { count: layout.hidden })}
152
+ </Text>
153
+ </Pressable>
154
+ )}
155
+ </View>
156
+ );
157
+ })}
158
+ </View>
159
+ );
160
+ };
161
+
162
+ export default AlbumGrid;
163
+
164
+ const styles = StyleSheet.create({
165
+ grid: {
166
+ position: 'relative',
167
+ overflow: 'hidden',
168
+ borderRadius: 10,
169
+ },
170
+ more: {
171
+ ...StyleSheet.absoluteFill,
172
+ alignItems: 'center',
173
+ justifyContent: 'center',
174
+ // Sits on the photo itself, so it stays hard-coded: it must read on the
175
+ // picture, not on the theme.
176
+ backgroundColor: 'rgba(8, 12, 24, 0.5)',
177
+ },
178
+ moreText: {
179
+ color: '#FFFFFF',
180
+ fontSize: 28,
181
+ fontWeight: '600',
182
+ },
183
+ });
@@ -201,14 +201,16 @@ const ChatRoom: React.FC<ChatRoomProps> = React.memo(
201
201
  );
202
202
 
203
203
  const sendMedia = useCallback(
204
- (data: any, type: string) => {
204
+ (data: any, type: string, caption?: { text: string }) => {
205
205
  // Return the promise so callers can sequence the follow-up
206
206
  // text send AFTER the upload finishes (consumer expectation:
207
207
  // media first, text second — never interleaved).
208
208
  if (store.getState().rooms.jumpWindow) {
209
209
  dispatch(clearJumpWindow());
210
210
  }
211
- return sendMessageMedia(data, type, activeRoomJID || '');
211
+ return caption
212
+ ? sendMessageMedia(data, type, activeRoomJID || '', false, false, '', undefined, caption)
213
+ : sendMessageMedia(data, type, activeRoomJID || '');
212
214
  },
213
215
  [activeRoomJID, dispatch],
214
216
  );
@@ -1,5 +1,5 @@
1
1
  import React, { useMemo } from 'react';
2
- import { IMessage } from '../../types/types';
2
+ import { IAttachment, IMessage } from '../../types/types';
3
3
  import FileDownload from '../styled/UnsupportedType';
4
4
  import CustomMessageImage from '../styled/MessageImage';
5
5
  import CustomMessageVideo from '../styled/VideoMessage';
@@ -11,7 +11,9 @@ import {
11
11
  Text,
12
12
  View,
13
13
  } from 'react-native';
14
- import { isLikelyAudio } from '../../helpers/mimeToExtension';
14
+ import { getAttachmentName, getMessageAttachments } from '../../helpers/attachments';
15
+ import { getMessageCaption } from '../../helpers/caption';
16
+ import { getFileKind, isAlbumKind } from '../../helpers/fileKind';
15
17
  import { getDisplayFileName } from '../../helpers/getDisplayFileName';
16
18
  import { FileIcon, PlayIcon } from '../../assets/icons';
17
19
  import { defaultMediaDims } from '../../helpers/mediaDimensions';
@@ -19,6 +21,10 @@ import { useFileToken } from '../../hooks/useFileToken';
19
21
  import { appendFileToken } from '../../helpers/secureFileUrl';
20
22
  import { useTheme } from '../../hooks/useTheme';
21
23
  import SealedAttachment from './SealedAttachment';
24
+ import AlbumGrid from './AlbumGrid';
25
+ import MediaCaption from '../MessageBubble/MediaCaption';
26
+ import MediaUploadOverlay from './MediaUploadOverlay';
27
+ import { requestMediaRetry } from '../../helpers/mediaSendRetry';
22
28
 
23
29
  interface MediaMessageProps {
24
30
  mimeType?: string;
@@ -156,160 +162,270 @@ const PendingMediaMessage: React.FC<{
156
162
  );
157
163
  };
158
164
 
159
- const MediaMessage: React.FC<MediaMessageProps> = ({
160
- mimeType,
161
- location: rawLocation,
162
- messageText: rawMessageText,
165
+ interface AttachmentCardProps {
166
+ attachment: IAttachment;
167
+ /** Position in the message; the voice waveform belongs to #0 only. */
168
+ index: number;
169
+ message?: IMessage;
170
+ isUser: boolean;
171
+ fileToken?: string | null;
172
+ }
173
+
174
+ /** One file of a message: the picture, video, player or file card it gets. */
175
+ const AttachmentCard: React.FC<AttachmentCardProps> = ({
176
+ attachment,
177
+ index,
163
178
  message,
164
179
  isUser,
180
+ fileToken,
165
181
  }) => {
166
- const fileToken = useFileToken();
167
- const theme = useTheme();
182
+ const mimeType = attachment.mimetype || '';
183
+ const rawLocation = attachment.location;
184
+ const rawPreview = attachment.locationPreview;
168
185
  const location = appendFileToken(rawLocation, fileToken);
169
- const messageText = appendFileToken(rawMessageText, fileToken);
186
+ const previewText = appendFileToken(rawPreview, fileToken);
187
+ const originalName = attachment.originalName;
188
+ const duration = attachment.duration ?? (message as any)?.duration;
189
+ const waveForm = index === 0 ? (message as any)?.waveForm : undefined;
170
190
 
171
- if (message?.clientEncrypted === 'true') {
172
- return <SealedAttachment message={message} isUser={isUser} />;
173
- }
191
+ const displayName = getDisplayFileName({
192
+ originalName,
193
+ fileName: attachment.fileName,
194
+ location: rawLocation || rawPreview,
195
+ mimetype: mimeType,
196
+ });
197
+ const kind = getFileKind({
198
+ mimetype: mimeType,
199
+ name: displayName,
200
+ url: rawLocation,
201
+ duration,
202
+ waveForm,
203
+ originalName,
204
+ });
174
205
 
175
- if (mimeType) {
176
- const displayName = getDisplayFileName({
177
- originalName: (message as any)?.originalName,
178
- fileName: message?.fileName,
179
- location: rawLocation || rawMessageText,
180
- mimetype: mimeType,
181
- });
182
- const isAudioPayload = isLikelyAudio(
183
- mimeType,
184
- displayName,
185
- rawLocation,
186
- {
187
- duration: (message as any)?.duration,
188
- waveForm: (message as any)?.waveForm,
189
- originalName: (message as any)?.originalName,
190
- }
206
+ // The sender's own voice note plays from its local file while it
207
+ // uploads, waveform and all - no placeholder card in between.
208
+ const localPreview = attachment.localUrl;
209
+ if (message?.pending && kind === 'audio' && (localPreview || previewText || location)) {
210
+ return (
211
+ <AudioMessage
212
+ src={localPreview || previewText || location || ''}
213
+ mimeType={mimeType}
214
+ fileName={displayName}
215
+ duration={duration}
216
+ waveForm={waveForm}
217
+ isUser={isUser}
218
+ />
191
219
  );
192
- // The sender's own voice note plays from its local file while it
193
- // uploads, waveform and all — no placeholder card in between.
194
- if (message?.pending && isAudioPayload && (messageText || location)) {
220
+ }
221
+ if (message?.pending) {
222
+ return (
223
+ <PendingMediaMessage
224
+ fileName={displayName}
225
+ mimeType={mimeType}
226
+ previewUri={localPreview || previewText || location || ''}
227
+ isUser={isUser}
228
+ size={index === 0 ? message?.size : attachment.size}
229
+ />
230
+ );
231
+ }
232
+
233
+ switch (kind) {
234
+ case 'image':
235
+ return (
236
+ <CustomMessageImage
237
+ fileName={displayName}
238
+ originalName={originalName}
239
+ // Full-size original: opened by the preview modal.
240
+ fileURL={location || previewText || ''}
241
+ // ...and the thumbnail the bubble actually renders, which is
242
+ // what the web client shows too.
243
+ locationPreview={previewText}
244
+ mimetype={mimeType}
245
+ />
246
+ );
247
+ case 'video':
248
+ return (
249
+ <CustomMessageVideo
250
+ fileName={displayName}
251
+ originalName={originalName}
252
+ fileURL={location || ''}
253
+ // The message's locationPreview is the poster frame the backend
254
+ // generated for this video. Handing it over lets the bubble render
255
+ // as a still + play badge instead of mounting a video surface per
256
+ // row (see VideoMessage).
257
+ previewURL={previewText}
258
+ mimetype={mimeType}
259
+ />
260
+ );
261
+ case 'audio':
262
+ // Covers audio/*, octet-stream voice notes and the voice-message
263
+ // heuristics of isLikelyAudio (customer-reported #9 voicemail fix):
264
+ // a voicemail shipped as octet-stream with no audio extension still
265
+ // gets the player instead of a broken `.bin` file card.
195
266
  return (
196
267
  <AudioMessage
197
- src={messageText || location || ''}
268
+ src={location || ''}
198
269
  mimeType={mimeType}
199
270
  fileName={displayName}
200
- duration={(message as any)?.duration}
201
- waveForm={(message as any)?.waveForm}
271
+ originalName={originalName}
272
+ duration={duration}
273
+ waveForm={waveForm}
202
274
  isUser={isUser}
203
275
  />
204
276
  );
205
- }
206
- if (message?.pending) {
277
+ default:
207
278
  return (
208
- <PendingMediaMessage
279
+ <FileDownload
280
+ fileURL={location ? location : ''}
209
281
  fileName={displayName}
210
- mimeType={mimeType}
211
- previewUri={messageText || location || ''}
282
+ mimetype={mimeType}
212
283
  isUser={isUser}
213
- size={message?.size}
284
+ originalName={originalName}
285
+ duration={duration}
286
+ waveForm={waveForm}
214
287
  />
215
288
  );
216
- }
217
- switch (true) {
218
- case mimeType.startsWith('image/'):
219
- return (
220
- <CustomMessageImage
221
- fileName={displayName}
222
- originalName={(message as any)?.originalName}
223
- // Full-size original: opened by the preview modal.
224
- fileURL={location || messageText || ''}
225
- // ...and the thumbnail the bubble actually renders, which is
226
- // what the web client shows too.
227
- locationPreview={messageText}
228
- mimetype={mimeType}
229
- />
230
- );
231
- case mimeType.startsWith('video/'):
232
- return (
233
- <CustomMessageVideo
234
- fileName={displayName}
235
- originalName={(message as any)?.originalName}
236
- fileURL={location || ''}
237
- // `messageText` is the message's locationPreview — the poster
238
- // frame the backend generated for this video. Handing it over
239
- // lets the bubble render as a still + play badge instead of
240
- // mounting a video surface per row (see VideoMessage).
241
- previewURL={messageText}
242
- mimetype={mimeType}
243
- />
244
- );
245
- case mimeType.startsWith('audio/'): {
246
- return (
247
- <AudioMessage
248
- src={location || ''}
249
- mimeType={mimeType}
250
- fileName={displayName}
251
- originalName={(message as any)?.originalName}
252
- duration={(message as any)?.duration}
253
- waveForm={(message as any)?.waveForm}
254
- isUser={isUser}
255
- />
256
- );
257
- }
258
- case mimeType.includes('application/octet-stream'): {
259
- return (
260
- <AudioMessage
261
- src={location || ''}
262
- mimeType={mimeType}
263
- fileName={displayName}
264
- originalName={(message as any)?.originalName}
265
- duration={(message as any)?.duration}
266
- waveForm={(message as any)?.waveForm}
267
- isUser={isUser}
268
- />
269
- );
270
- }
271
- default: {
272
- // Some senders (notably the web app's voice-message uploader)
273
- // ship audio as `application/octet-stream` with no audio file
274
- // extension. `isLikelyAudio` recognises those via the URL path,
275
- // filename, or voice-message naming hints (voicemail-,
276
- // voice-note-, recording-, …) so we still render the AudioMessage
277
- // player instead of falling through to the generic FileDownload
278
- // card (where the voicemail rendered as a broken `.bin`
279
- // attachment). Customer-reported #9 voicemail fix.
280
- if (isAudioPayload) {
281
- return (
282
- <AudioMessage
283
- src={location || ''}
284
- mimeType={mimeType}
285
- fileName={displayName}
286
- originalName={(message as any)?.originalName}
287
- duration={(message as any)?.duration}
288
- waveForm={(message as any)?.waveForm}
289
- isUser={isUser}
290
- />
291
- );
289
+ }
290
+ };
291
+
292
+ const MediaMessage: React.FC<MediaMessageProps> = ({
293
+ mimeType,
294
+ location: rawLocation,
295
+ messageText: rawMessageText,
296
+ message,
297
+ isUser,
298
+ }) => {
299
+ const fileToken = useFileToken();
300
+ const theme = useTheme();
301
+
302
+ // Order of truth: the message's own attachments, else its flat fields, else
303
+ // the props the bubble was handed (a bare file with no message around it).
304
+ const attachments = useMemo(
305
+ () =>
306
+ getMessageAttachments(
307
+ message ?? {
308
+ location: rawLocation,
309
+ locationPreview: rawMessageText,
310
+ mimetype: mimeType,
292
311
  }
293
- return (
294
- <FileDownload
295
- fileURL={location ? location : ''}
296
- fileName={displayName}
297
- mimetype={mimeType}
298
- isUser={isUser}
299
- originalName={(message as any)?.originalName}
300
- duration={(message as any)?.duration}
301
- waveForm={(message as any)?.waveForm}
302
- />
303
- );
304
- }
305
- }
312
+ ),
313
+ [message, rawLocation, rawMessageText, mimeType]
314
+ );
315
+ const caption = message ? getMessageCaption(message) : undefined;
316
+
317
+ if (message?.clientEncrypted === 'true') {
318
+ // A sealed file has nothing to show until it is opened, so each one is a
319
+ // card of its own (SealedAttachment); the caption came out of the
320
+ // encrypted body.
321
+ return (
322
+ <>
323
+ <SealedAttachment message={message} isUser={isUser} />
324
+ <MediaCaption text={caption} isUser={isUser} />
325
+ </>
326
+ );
327
+ }
328
+
329
+ if (attachments.length === 0) {
330
+ return <Text style={{ color: theme.text }}>Unsupported media type</Text>;
306
331
  }
307
- return <Text style={{ color: theme.text }}>Unsupported media type</Text>;
332
+
333
+ // A file of an outgoing multi-file message carries its upload state. It is
334
+ // "reachable" through its local copy, not a server one, until the send lands.
335
+ const isOutgoingFile = (attachment: IAttachment) =>
336
+ Boolean(message?.pending) &&
337
+ Boolean(attachment.uploadStatus || attachment.localUrl);
338
+
339
+ const card = (attachment: IAttachment, index: number) => {
340
+ const body = (
341
+ <AttachmentCard
342
+ key={attachment.attachmentId || `${index}-${attachment.location}`}
343
+ attachment={attachment}
344
+ index={index}
345
+ message={message}
346
+ isUser={isUser}
347
+ fileToken={fileToken}
348
+ />
349
+ );
350
+ // In a multi-file message each file gets its own progress and Retry.
351
+ if (!attachment.uploadStatus || attachments.length < 2) {return body;}
352
+ return (
353
+ <View
354
+ key={`tile-${attachment.attachmentId || index}`}
355
+ style={styles.uploadTile}
356
+ testID={`media-upload-tile-${index}`}
357
+ >
358
+ {body}
359
+ <MediaUploadOverlay
360
+ status={attachment.uploadStatus}
361
+ progress={attachment.uploadProgress}
362
+ onRetry={message?.id ? () => requestMediaRetry(message.id, index) : undefined}
363
+ />
364
+ </View>
365
+ );
366
+ };
367
+
368
+ // Pictures and videos share one album when there are two or more of them;
369
+ // documents and voice notes follow as one card each. A lone picture is the
370
+ // ordinary bubble picture, exactly as a single-file message always was.
371
+ const album = attachments.filter(
372
+ (attachment) =>
373
+ (Boolean(attachment.location) || isOutgoingFile(attachment)) &&
374
+ isAlbumKind(
375
+ getFileKind({
376
+ mimetype: attachment.mimetype,
377
+ name: getAttachmentName(attachment),
378
+ url: attachment.location,
379
+ duration: attachment.duration,
380
+ })
381
+ )
382
+ );
383
+ const useAlbum = album.length >= 2;
384
+ const rest = useAlbum
385
+ ? attachments.filter((attachment) => !album.includes(attachment))
386
+ : attachments;
387
+
388
+ // One file and no text: the card alone, no wrapper around it.
389
+ if (!useAlbum && attachments.length === 1 && !caption) {
390
+ return card(attachments[0]!, 0);
391
+ }
392
+
393
+ return (
394
+ <View style={styles.stack}>
395
+ {useAlbum && (
396
+ <AlbumGrid
397
+ attachments={album}
398
+ onRetryTile={
399
+ message?.id
400
+ ? (position) =>
401
+ requestMediaRetry(
402
+ message.id,
403
+ attachments.indexOf(album[position]!)
404
+ )
405
+ : undefined
406
+ }
407
+ />
408
+ )}
409
+ {rest.map((attachment) => card(attachment, attachments.indexOf(attachment)))}
410
+ <MediaCaption text={caption} isUser={isUser} />
411
+ </View>
412
+ );
308
413
  };
309
414
 
310
415
  export default MediaMessage;
311
416
 
312
417
  const styles = StyleSheet.create({
418
+ stack: {
419
+ alignItems: 'flex-start',
420
+ gap: 4,
421
+ },
422
+ // Frames one file of a multi-file message so its upload overlay covers it.
423
+ uploadTile: {
424
+ position: 'relative',
425
+ alignSelf: 'stretch',
426
+ borderRadius: 10,
427
+ overflow: 'hidden',
428
+ },
313
429
  pendingImageWrapper: {
314
430
  width: pendingImageDims.width,
315
431
  height: pendingImageDims.height,