@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
@@ -0,0 +1,38 @@
1
+ import { filenameFromUrl, isLikelyAudio } from './mimeToExtension';
2
+
3
+ export type FileKind = 'image' | 'video' | 'audio' | 'file';
4
+
5
+ interface FileKindInput {
6
+ mimetype?: string | null;
7
+ name?: string | null;
8
+ url?: string | null;
9
+ duration?: number | string | null;
10
+ waveForm?: string | null;
11
+ originalName?: string | null;
12
+ }
13
+
14
+ /**
15
+ * Which card a file gets in a bubble. Mirrors the branches MediaMessage has
16
+ * always used for a single file (image, video, audio, octet-stream as a voice
17
+ * note, then the voice-message heuristics) so an album, a card stack and a
18
+ * lone file all agree on what a given file is.
19
+ */
20
+ export const getFileKind = (input: FileKindInput): FileKind => {
21
+ const mime = (input.mimetype || '').toLowerCase();
22
+ if (mime.startsWith('image/')) {return 'image';}
23
+ if (mime.startsWith('video/')) {return 'video';}
24
+ if (mime.startsWith('audio/')) {return 'audio';}
25
+ if (mime.includes('application/octet-stream')) {return 'audio';}
26
+ const name = input.name || filenameFromUrl(input.url);
27
+ return isLikelyAudio(mime, name, input.url, {
28
+ duration: input.duration,
29
+ waveForm: input.waveForm,
30
+ originalName: input.originalName,
31
+ })
32
+ ? 'audio'
33
+ : 'file';
34
+ };
35
+
36
+ /** True for the kinds that sit in the picture/video album. */
37
+ export const isAlbumKind = (kind: FileKind): boolean =>
38
+ kind === 'image' || kind === 'video';
@@ -2,6 +2,7 @@ import { Element } from 'ltx';
2
2
  import { Iso639_1Codes, IUser } from '../types/types';
3
3
  import { transformArrayToObject } from './transformTranslatations';
4
4
  import { normalizeXmppUsername } from './xmppUsername';
5
+ import { normalizeMediaDataAttrs } from './mediaWireData';
5
6
 
6
7
  const getLocalpartFromJid = (jid?: string): string | undefined => {
7
8
  const value = String(jid || '').trim();
@@ -122,7 +123,10 @@ export const getDataFromXml = async (stanza: Element): Promise<DataXml | undefin
122
123
  photoURL,
123
124
  } as any;
124
125
 
125
- const dataAttrs = data?.attrs || {};
126
+ // `attachments` and `caption` ride on <data> as strings; decode them here
127
+ // so every consumer sees the typed values (sealed captions are lifted from
128
+ // the encrypted body later, see createMessageFromXml).
129
+ const dataAttrs = normalizeMediaDataAttrs(data?.attrs || {});
126
130
 
127
131
  return {
128
132
  data: dataAttrs,
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Runs `worker` over `items` with at most `limit` calls in flight, and
3
+ * resolves with the results in INPUT order, whatever order the calls finish
4
+ * in. Workers that throw are reported as rejected entries instead of
5
+ * aborting the rest of the batch, so one failed upload never stops its
6
+ * siblings.
7
+ */
8
+ export async function mapWithConcurrency<T, R>(
9
+ items: readonly T[],
10
+ limit: number,
11
+ worker: (item: T, index: number) => Promise<R>
12
+ ): Promise<PromiseSettledResult<R>[]> {
13
+ const results: PromiseSettledResult<R>[] = new Array(items.length);
14
+ const width = Math.max(1, Math.min(Math.floor(limit) || 1, items.length));
15
+ let next = 0;
16
+
17
+ const lane = async (): Promise<void> => {
18
+ while (next < items.length) {
19
+ const index = next;
20
+ next += 1;
21
+ try {
22
+ const value = await worker(items[index]!, index);
23
+ results[index] = { status: 'fulfilled', value };
24
+ } catch (reason) {
25
+ results[index] = { status: 'rejected', reason };
26
+ }
27
+ }
28
+ };
29
+
30
+ await Promise.all(Array.from({ length: width }, () => lane()));
31
+ return results;
32
+ }
@@ -0,0 +1,27 @@
1
+ import type { IConfig } from '../types/types';
2
+
3
+ /** Hard client-side cap on a single upload. Mirrors the backend's body limit. */
4
+ export const MAX_MEDIA_BYTES = 50 * 1024 * 1024;
5
+
6
+ /** The most files one message may carry (also the parser-side ceiling). */
7
+ export const MAX_MEDIA_FILES = 10;
8
+
9
+ /** Files per message the host allows: `attachments.maxFiles`, clamped to 1-10. */
10
+ export const mediaFileCap = (config?: Pick<IConfig, 'attachments'> | null): number => {
11
+ const configured = Number(config?.attachments?.maxFiles);
12
+ if (!Number.isFinite(configured)) {return MAX_MEDIA_FILES;}
13
+ return Math.min(MAX_MEDIA_FILES, Math.max(1, Math.floor(configured)));
14
+ };
15
+
16
+ /**
17
+ * Bytes allowed per file: the 50 MB upload cap, lowered by
18
+ * `attachments.maxSizeMB` when the host sets a smaller one.
19
+ */
20
+ export const mediaSizeLimit = (
21
+ config?: Pick<IConfig, 'attachments'> | null,
22
+ override?: number
23
+ ): number => {
24
+ const base = typeof override === 'number' && override > 0 ? Math.min(override, MAX_MEDIA_BYTES) : MAX_MEDIA_BYTES;
25
+ const mb = Number(config?.attachments?.maxSizeMB);
26
+ return Number.isFinite(mb) && mb > 0 ? Math.min(base, mb * 1024 * 1024) : base;
27
+ };
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Retry hooks for media messages whose uploads did not all land.
3
+ *
4
+ * The send closure lives inside useSendMessage, but the bubble that shows
5
+ * "Retry" on a tile is somewhere else in the tree and has no access to it.
6
+ * The send registers its retry runner under the message id while the
7
+ * message is unsent, and the bubble looks it up here. The runner is removed
8
+ * as soon as the message is delivered.
9
+ */
10
+
11
+ export type MediaRetryRunner = (indices?: number[]) => Promise<boolean>;
12
+
13
+ const runners = new Map<string, MediaRetryRunner>();
14
+
15
+ export const registerMediaRetry = (messageId: string, runner: MediaRetryRunner): void => {
16
+ runners.set(messageId, runner);
17
+ };
18
+
19
+ export const unregisterMediaRetry = (messageId: string): void => {
20
+ runners.delete(messageId);
21
+ };
22
+
23
+ export const hasMediaRetry = (messageId: string): boolean => runners.has(messageId);
24
+
25
+ /**
26
+ * Re-runs the uploads still missing for `messageId` (only `index` / `indices`
27
+ * when given), then sends the message once every file is in. Resolves to
28
+ * false when nothing is registered or the attempt did not deliver.
29
+ */
30
+ export const retryMediaSend = async (
31
+ messageId: string,
32
+ index?: number | number[]
33
+ ): Promise<boolean> => {
34
+ const runner = runners.get(messageId);
35
+ if (!runner) {return false;}
36
+ const indices = index === undefined ? undefined : Array.isArray(index) ? index : [index];
37
+ return runner(indices);
38
+ };
39
+
40
+ const pendingRetryIndices = new Map<string, Set<number>>();
41
+
42
+ /**
43
+ * Asks for one file of `messageId` to be retried. Requests made in the same
44
+ * tick are merged into one run: the runner takes a single run at a time, so
45
+ * a separate call per tile would drop every retry after the first.
46
+ */
47
+ export const requestMediaRetry = (messageId: string, index: number): void => {
48
+ const batch = pendingRetryIndices.get(messageId);
49
+ if (batch) {
50
+ batch.add(index);
51
+ return;
52
+ }
53
+ const next = new Set<number>([index]);
54
+ pendingRetryIndices.set(messageId, next);
55
+ queueMicrotask(() => {
56
+ pendingRetryIndices.delete(messageId);
57
+ retryMediaSend(messageId, [...next]).catch(() => false);
58
+ });
59
+ };
@@ -0,0 +1,57 @@
1
+ import { parseAttachments } from './attachments';
2
+ import { getMessageCaption, normalizeCaptionMentions } from './caption';
3
+
4
+ /**
5
+ * Normalises the media parts of a message's flat `<data>` attributes:
6
+ *
7
+ * - `attachments` (a JSON string on the wire) becomes the typed array, or is
8
+ * dropped when it decodes to nothing so the flat legacy fields win;
9
+ * - `caption` is kept only when it holds text, with `captionMentions` (JSON
10
+ * on the wire) checked against it;
11
+ * - in a sealed message (`clientEncrypted`) the caption attributes are
12
+ * always dropped: the only caption such a message may show is the one
13
+ * inside its encrypted body, which the caller lifts afterwards.
14
+ *
15
+ * Safe to apply twice, so the wrapped and the positional parsers can both
16
+ * call it.
17
+ */
18
+ export const normalizeMediaDataAttrs = (
19
+ attrs: Record<string, any>
20
+ ): Record<string, any> => {
21
+ const out: Record<string, any> = { ...attrs };
22
+
23
+ if ('attachments' in out) {
24
+ const attachments = parseAttachments(out.attachments);
25
+ if (attachments.length > 0) {
26
+ out.attachments = attachments;
27
+ } else {
28
+ delete out.attachments;
29
+ }
30
+ }
31
+
32
+ if (out.clientEncrypted === 'true') {
33
+ delete out.caption;
34
+ delete out.captionMentions;
35
+ return out;
36
+ }
37
+
38
+ if ('caption' in out) {
39
+ const caption = getMessageCaption({ caption: out.caption });
40
+ if (caption) {
41
+ out.caption = caption;
42
+ const spans = normalizeCaptionMentions(out.captionMentions, caption);
43
+ if (spans) {
44
+ out.captionMentions = spans;
45
+ } else {
46
+ delete out.captionMentions;
47
+ }
48
+ } else {
49
+ delete out.caption;
50
+ delete out.captionMentions;
51
+ }
52
+ } else if ('captionMentions' in out) {
53
+ delete out.captionMentions;
54
+ }
55
+
56
+ return out;
57
+ };
@@ -0,0 +1,17 @@
1
+ import type { IMentionSpan } from '../types/models/message.model';
2
+
3
+ /**
4
+ * Keeps only the spans that still read "@Name" at their offset in `text`.
5
+ * RN does not render mentions; a caption's spans are stored (and checked) so
6
+ * they survive a round trip to a client that does.
7
+ */
8
+ export const validateMentionSpans = (
9
+ text: string,
10
+ spans: IMentionSpan[]
11
+ ): IMentionSpan[] =>
12
+ spans.filter((span) => {
13
+ if (span.offset < 0 || span.offset + span.length > text.length) {
14
+ return false;
15
+ }
16
+ return text.slice(span.offset, span.offset + span.length) === `@${span.name}`;
17
+ });
@@ -64,6 +64,57 @@ const MIME_TO_EXT: Record<string, string> = {
64
64
 
65
65
  const VALID_EXT_RE = /\.[a-z0-9]{1,8}$/i;
66
66
 
67
+ // Extensions that stand for a picture, video, audio or PDF a bubble can
68
+ // show, read back from the table above (first entry per extension wins).
69
+ // `.webm` and `.svg` are left out on purpose: a recorded voice note is often
70
+ // a `.webm` and must stay generic (audio), and SVG is not a bubble picture.
71
+ const INFERRABLE_EXT_TO_MIME: Record<string, string> = {};
72
+ for (const [mime, ext] of Object.entries(MIME_TO_EXT)) {
73
+ const inferrable =
74
+ mime === 'application/pdf' ||
75
+ mime.startsWith('image/') ||
76
+ mime.startsWith('video/') ||
77
+ mime.startsWith('audio/');
78
+ const bare = ext.slice(1);
79
+ if (inferrable && bare !== 'webm' && bare !== 'svg' && !INFERRABLE_EXT_TO_MIME[bare]) {
80
+ INFERRABLE_EXT_TO_MIME[bare] = mime;
81
+ }
82
+ }
83
+ // Spellings the table above has no entry for.
84
+ Object.assign(INFERRABLE_EXT_TO_MIME, {
85
+ jpeg: 'image/jpeg',
86
+ jpe: 'image/jpeg',
87
+ tif: 'image/tiff',
88
+ avif: 'image/avif',
89
+ avi: 'video/x-msvideo',
90
+ oga: 'audio/ogg',
91
+ opus: 'audio/ogg',
92
+ amr: 'audio/amr',
93
+ });
94
+
95
+ /** A mimetype that says nothing: empty, or the storage's "could not sniff". */
96
+ export function isGenericMime(mime: string | undefined | null): boolean {
97
+ const normalized = (mime || '').toLowerCase().split(';')[0]!.trim();
98
+ return (
99
+ !normalized ||
100
+ normalized === 'application/octet-stream' ||
101
+ normalized === 'binary/octet-stream'
102
+ );
103
+ }
104
+
105
+ /**
106
+ * The mimetype a file name's extension stands for, for the pictures, videos,
107
+ * audio and PDFs a bubble can show. Undefined for anything else.
108
+ */
109
+ export function inferMimeFromName(
110
+ name: string | undefined | null
111
+ ): string | undefined {
112
+ const base = String(name || '').split(/[?#]/)[0]!;
113
+ const dot = base.lastIndexOf('.');
114
+ if (dot < 1) {return undefined;}
115
+ return INFERRABLE_EXT_TO_MIME[base.slice(dot + 1).toLowerCase()];
116
+ }
117
+
67
118
  // Audio file extensions we recognise across the SDK. Used for the
68
119
  // "looks like audio even when the mimetype is application/octet-stream"
69
120
  // heuristic in MediaMessage / FilePreviewModal. Voice-message senders
@@ -0,0 +1,219 @@
1
+ import type { IMessage, IRoom } from '../types/types';
2
+ import { msgSortableMs } from './msgSortableMs';
3
+ import { isOwnMessage } from './isOwnMessage';
4
+ import { createUserNameFromSetUser } from './createUserNameFromSetUser';
5
+
6
+ // Delivery (XEP-0184) and read (XEP-0333) state for the reader's own messages.
7
+ // Pure helpers: the reducers, the stanza handlers, the flush that sends the
8
+ // read watermark and the bubble all read the same rules from here.
9
+ //
10
+ // Receipts are session-only. receivedBy / lastDisplayedByUser are never
11
+ // written to disk (see persistence.ts), so after a reload every own message
12
+ // falls back to "sent" until the peers acknowledge again.
13
+
14
+ export type ReceiptStatus = 'sent' | 'delivered' | 'seen';
15
+
16
+ /** Names listed in a "Read by" / "Delivered to" sheet before the rest become "+N". */
17
+ export const RECEIPT_NAME_LIMIT = 5;
18
+
19
+ const NO_VIEWERS: string[] = [];
20
+
21
+ /** Stanza ids of the receipt and marker frames (never chat messages). */
22
+ export const isReceiptMarkerId = (id: unknown): boolean => {
23
+ const value = String(id ?? '');
24
+ return value.startsWith('message-received') || value.startsWith('message-displayed');
25
+ };
26
+
27
+ // A message the reader can be shown as displayed: not an optimistic send,
28
+ // not the "New messages" divider, not a client-only call-log row.
29
+ const isDisplayableForReceipts = (message: IMessage | undefined | null): boolean => {
30
+ if (!message) return false;
31
+ if (message.pending) return false;
32
+ if (message.id === 'delimiter-new') return false;
33
+ if (String(message.id || '').startsWith('calllog-')) return false;
34
+ return true;
35
+ };
36
+
37
+ /**
38
+ * The message id that becomes this reader's read watermark, or null when
39
+ * nothing should be sent: no displayable message, the newest message is the
40
+ * reader's own (nothing to report), or a read boundary (the reader scrolled
41
+ * up) sits before every message. With a boundary the watermark is the newest
42
+ * message at or before it, so a scrolled-up reader never claims messages they
43
+ * did not reach.
44
+ */
45
+ export const getReadWatermarkMessageId = (
46
+ room: Pick<IRoom, 'messages'> | null | undefined,
47
+ selfXmppUsername: string,
48
+ boundaryTs?: number | null
49
+ ): string | null => {
50
+ if (!selfXmppUsername) return null;
51
+ const messages = (room?.messages || []).filter(isDisplayableForReceipts);
52
+ if (messages.length === 0) return null;
53
+ const newest = messages[messages.length - 1];
54
+ if (isOwnMessage(newest, selfXmppUsername)) return null;
55
+ if (typeof boundaryTs === 'number' && boundaryTs > 0) {
56
+ for (let i = messages.length - 1; i >= 0; i--) {
57
+ if (msgSortableMs(messages[i]) <= boundaryTs) return String(messages[i].id);
58
+ }
59
+ return null;
60
+ }
61
+ return String(newest.id);
62
+ };
63
+
64
+ /** The slice of the redux state the receipt rules read. */
65
+ export interface ReceiptStoreState {
66
+ chatSettingStore?: {
67
+ config?: { disableLastRead?: boolean; disableReadReceipts?: boolean } | null;
68
+ user?: { xmppUsername?: string | null; walletAddress?: string | null } | null;
69
+ } | null;
70
+ rooms: {
71
+ rooms: Record<string, IRoom>;
72
+ visibleRoomJID: string | null;
73
+ jumpWindow: { roomJID: string } | null;
74
+ readBoundaries: Record<string, number>;
75
+ };
76
+ }
77
+
78
+ // The reader's identity as the message list resolves it (xmppUsername, then
79
+ // walletAddress), so the watermark and the bubble agree on "own".
80
+ export const selfIdentity = (state: ReceiptStoreState): string =>
81
+ state.chatSettingStore?.user?.xmppUsername || state.chatSettingStore?.user?.walletAddress || '';
82
+
83
+ /**
84
+ * Watermark to send for `roomJid` right now, or null. Sends only while the
85
+ * room is the one on screen, never while an old-history jump window is open,
86
+ * and never when read markers are switched off (`disableLastRead` or
87
+ * `disableReadReceipts`).
88
+ */
89
+ export const computeReadWatermark = (
90
+ state: ReceiptStoreState,
91
+ roomJid: string
92
+ ): string | null => {
93
+ if (!roomJid) return null;
94
+ const config = state.chatSettingStore?.config;
95
+ if (config?.disableLastRead || config?.disableReadReceipts) return null;
96
+ const rooms = state.rooms;
97
+ if (rooms.visibleRoomJID !== roomJid) return null;
98
+ if (rooms.jumpWindow?.roomJID === roomJid) return null;
99
+ return getReadWatermarkMessageId(
100
+ rooms.rooms?.[roomJid],
101
+ selfIdentity(state),
102
+ rooms.readBoundaries?.[roomJid] ?? null
103
+ );
104
+ };
105
+
106
+ /**
107
+ * Other occupants whose read watermark sits at or after `messageId` in the
108
+ * room's list. Returns the shared empty array when there are none, so the
109
+ * selector result stays shallow-equal between store updates.
110
+ */
111
+ export const viewersAtOrAfter = (
112
+ room: Pick<IRoom, 'messages' | 'lastDisplayedByUser'> | null | undefined,
113
+ messageId: string,
114
+ selfXmppUsername: string
115
+ ): string[] => {
116
+ const watermark = room?.lastDisplayedByUser;
117
+ const messages = room?.messages;
118
+ if (!watermark || !messages || messages.length === 0 || !messageId) return NO_VIEWERS;
119
+ const viewers = Object.keys(watermark).filter(
120
+ (xmppUsername) => xmppUsername !== selfXmppUsername
121
+ );
122
+ if (viewers.length === 0) return NO_VIEWERS;
123
+ const indexOf = (id: string): number => {
124
+ for (let i = messages.length - 1; i >= 0; i--) {
125
+ if (messages[i].id === id) return i;
126
+ }
127
+ return -1;
128
+ };
129
+ const thisIndex = indexOf(messageId);
130
+ if (thisIndex === -1) return NO_VIEWERS;
131
+ const seen = viewers.filter((xmppUsername) => {
132
+ const displayedIndex = indexOf(watermark[xmppUsername]);
133
+ return displayedIndex !== -1 && displayedIndex >= thisIndex;
134
+ });
135
+ return seen.length > 0 ? seen : NO_VIEWERS;
136
+ };
137
+
138
+ /** Selector form of viewersAtOrAfter, keyed by room and message. */
139
+ export const selectSeenBy = (
140
+ state: { rooms: { rooms: Record<string, IRoom> } },
141
+ roomJid: string,
142
+ messageId: string,
143
+ selfXmppUsername: string
144
+ ): string[] =>
145
+ viewersAtOrAfter(state.rooms.rooms?.[roomJid], messageId, selfXmppUsername);
146
+
147
+ /**
148
+ * sent / delivered / seen for one of the reader's own messages. "seen" wins
149
+ * over "delivered", which wins over "sent". Only other occupants count.
150
+ */
151
+ export const deliveredToOthers = (
152
+ message: Pick<IMessage, 'receivedBy'>,
153
+ selfXmppUsername: string
154
+ ): string[] =>
155
+ Object.keys(message.receivedBy || {}).filter(
156
+ (xmppUsername) => xmppUsername !== selfXmppUsername
157
+ );
158
+
159
+ export const statusOf = (seenBy: string[], deliveredTo: string[]): ReceiptStatus =>
160
+ seenBy.length > 0 ? 'seen' : deliveredTo.length > 0 ? 'delivered' : 'sent';
161
+
162
+ export const getReceiptStatus = (
163
+ message: Pick<IMessage, 'id' | 'receivedBy'>,
164
+ room: Pick<IRoom, 'messages' | 'lastDisplayedByUser'> | null | undefined,
165
+ selfXmppUsername: string
166
+ ): { status: ReceiptStatus; seenBy: string[]; deliveredTo: string[] } => {
167
+ const deliveredTo = deliveredToOthers(message, selfXmppUsername);
168
+ const seenBy = viewersAtOrAfter(room, String(message.id), selfXmppUsername);
169
+ return { status: statusOf(seenBy, deliveredTo), seenBy, deliveredTo };
170
+ };
171
+
172
+ /** Display name for a receipt entry: usersSet first, the raw username as the last resort. */
173
+ export const receiptNameFor = (
174
+ usersSet: Record<string, any> | undefined,
175
+ xmppUsername: string
176
+ ): string => {
177
+ const name = createUserNameFromSetUser(usersSet || {}, xmppUsername);
178
+ return name && name !== 'Deleted User' ? name : xmppUsername;
179
+ };
180
+
181
+ const formatNames = (names: string[]): string => {
182
+ const shown = names.slice(0, RECEIPT_NAME_LIMIT);
183
+ const extra = names.length - shown.length;
184
+ return extra > 0 ? `${shown.join(', ')} +${extra}` : shown.join(', ');
185
+ };
186
+
187
+ /**
188
+ * Text for the tick's details sheet. Groups name the readers or recipients
189
+ * (capped, then "+N"); a 1:1 room only says Read / Delivered.
190
+ */
191
+ export const describeReceipt = ({
192
+ status,
193
+ isGroup,
194
+ seenBy,
195
+ deliveredTo,
196
+ nameOf,
197
+ t,
198
+ }: {
199
+ status: ReceiptStatus;
200
+ isGroup: boolean;
201
+ seenBy: string[];
202
+ deliveredTo: string[];
203
+ nameOf: (xmppUsername: string) => string;
204
+ t: (key: string, vars?: Record<string, string | number>) => string;
205
+ }): string => {
206
+ if (status === 'seen') {
207
+ return isGroup
208
+ ? t('message.receipt.readBy', { names: formatNames(seenBy.map(nameOf)) })
209
+ : t('message.receipt.read');
210
+ }
211
+ if (status === 'delivered') {
212
+ return isGroup
213
+ ? t('message.receipt.deliveredTo', {
214
+ names: formatNames(deliveredTo.map(nameOf)),
215
+ })
216
+ : t('message.receipt.delivered');
217
+ }
218
+ return t('message.receipt.sent');
219
+ };
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Repairs file names the backend returns as UTF-8 bytes read as Latin-1
3
+ * (the classic multer/busboy `originalname` problem): "Список.pdf" arrives
4
+ * as "СпиÑок.pdf".
5
+ *
6
+ * Pure and conservative: a name is only replaced when every character fits
7
+ * in one byte, the bytes decode as strict UTF-8, the result differs, holds
8
+ * no replacement or control characters and contains at least one letter
9
+ * outside basic Latin. A correct name is returned untouched.
10
+ */
11
+
12
+ const hasMultiByteSignature = (value: string): boolean =>
13
+ // UTF-8 lead bytes C2-F4 followed by a continuation byte 80-BF.
14
+ /[Â-ô][\u0080-¿]/.test(value);
15
+
16
+ const looksLikePercentEncoded = (value: string): boolean =>
17
+ /%[0-9A-Fa-f]{2}/.test(value);
18
+
19
+ // C0 controls, DEL and the C1 range, checked per UTF-16 code unit.
20
+ const hasControlChars = (value: string): boolean => {
21
+ for (let i = 0; i < value.length; i += 1) {
22
+ const code = value.charCodeAt(i);
23
+ if (code < 0x20 || (code >= 0x7f && code <= 0x9f)) return true;
24
+ }
25
+ return false;
26
+ };
27
+
28
+ // Any code unit above ASCII (same as the former [^\u0000-\u007F] test).
29
+ const hasNonAscii = (value: string): boolean => /[\u0080-\uFFFF]/.test(value);
30
+
31
+ const decodeLatin1AsUtf8 = (value: string): string | null => {
32
+ if (typeof TextDecoder === 'undefined') return null;
33
+ const bytes = new Uint8Array(value.length);
34
+ for (let i = 0; i < value.length; i += 1) {
35
+ const code = value.charCodeAt(i);
36
+ if (code > 0xff) return null;
37
+ bytes[i] = code;
38
+ }
39
+ try {
40
+ return new TextDecoder('utf-8', { fatal: true }).decode(bytes);
41
+ } catch {
42
+ return null;
43
+ }
44
+ };
45
+
46
+ const isPlausibleRepair = (original: string, repaired: string): boolean =>
47
+ repaired !== original &&
48
+ !repaired.includes('�') &&
49
+ !hasControlChars(repaired) &&
50
+ // Must contain a letter from a non-Latin-1 script or a real accented
51
+ // letter, otherwise the "fix" is just noise.
52
+ hasNonAscii(repaired) &&
53
+ /\p{L}/u.test(repaired);
54
+
55
+ const tryPercentDecode = (value: string): string => {
56
+ if (!looksLikePercentEncoded(value)) return value;
57
+ try {
58
+ const decoded = decodeURIComponent(value);
59
+ return hasControlChars(decoded) || decoded.includes('�') ? value : decoded;
60
+ } catch {
61
+ return value;
62
+ }
63
+ };
64
+
65
+ export const repairFilenameEncoding = (name: string): string => {
66
+ if (typeof name !== 'string' || name === '') return name;
67
+
68
+ let current = name;
69
+
70
+ // Percent-encoded names ("%D0%A1...") first; they may also be mojibake
71
+ // after decoding, so run the byte repair on the result.
72
+ const unescaped = tryPercentDecode(current);
73
+ if (unescaped !== current && hasNonAscii(unescaped)) {
74
+ current = unescaped;
75
+ }
76
+
77
+ if (!hasMultiByteSignature(current)) return current;
78
+
79
+ const repaired = decodeLatin1AsUtf8(current);
80
+ if (repaired !== null && isPlausibleRepair(current, repaired)) return repaired;
81
+ return current;
82
+ };
@@ -0,0 +1,11 @@
1
+ export const safeJsonParse = <T>(rawValue: unknown, fallback: T): T => {
2
+ if (typeof rawValue !== 'string' || rawValue.trim() === '') {
3
+ return fallback;
4
+ }
5
+
6
+ try {
7
+ return JSON.parse(rawValue) as T;
8
+ } catch {
9
+ return fallback;
10
+ }
11
+ };
@@ -74,7 +74,8 @@ export const updateMessagesTillLast = async (
74
74
  { coalesceRoom: true, source: 'background', selfApplied: true }
75
75
  );
76
76
 
77
- if (!fetchedMessages.length) {break;}
77
+ // undefined when the stanza was dropped (e.g. reconnecting).
78
+ if (!fetchedMessages?.length) {break;}
78
79
 
79
80
  counter++;
80
81