@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
@@ -29,6 +29,10 @@ interface CustomMessageVideoProps {
29
29
  mimetype: string;
30
30
  /** The backend-generated poster frame (message.locationPreview). */
31
31
  previewURL?: string;
32
+ /** Album mode: fill a tile of exactly this size (square corners; the tile clips). */
33
+ tile?: { width: number; height: number };
34
+ /** Replaces the default "open this one file" tap, e.g. to open an album at an index. */
35
+ onOpen?: () => void;
32
36
  }
33
37
 
34
38
  const PlayBadge: React.FC = () => (
@@ -51,11 +55,13 @@ const PosterVideo: React.FC<{
51
55
  previewURL: string;
52
56
  onOpen: () => void;
53
57
  onPosterError: () => void;
54
- }> = ({ previewURL, onOpen, onPosterError }) => {
55
- const [dims, setDims] = useState<MediaDims>(defaultMediaDims());
58
+ tile?: { width: number; height: number };
59
+ }> = ({ previewURL, onOpen, onPosterError, tile }) => {
60
+ const [measured, setDims] = useState<MediaDims>(defaultMediaDims());
61
+ const dims: MediaDims = tile ?? measured;
56
62
 
57
63
  useEffect(() => {
58
- if (!previewURL) {
64
+ if (!previewURL || tile) {
59
65
  return;
60
66
  }
61
67
  let active = true;
@@ -73,12 +79,16 @@ const PosterVideo: React.FC<{
73
79
  return () => {
74
80
  active = false;
75
81
  };
76
- }, [previewURL]);
82
+ }, [previewURL, tile]);
77
83
 
78
84
  return (
79
85
  <Pressable
80
86
  onPress={onOpen}
81
- style={[styles.wrapper, { width: dims.width, height: dims.height }]}
87
+ style={[
88
+ styles.wrapper,
89
+ { width: dims.width, height: dims.height },
90
+ tile && styles.tileWrapper,
91
+ ]}
82
92
  accessibilityRole="button"
83
93
  accessibilityLabel="Play video"
84
94
  >
@@ -115,13 +125,18 @@ const PosterVideo: React.FC<{
115
125
  */
116
126
  const PlaceholderVideo: React.FC<{
117
127
  onOpen: () => void;
118
- }> = ({ onOpen }) => {
119
- const dims = defaultMediaDims();
128
+ tile?: { width: number; height: number };
129
+ }> = ({ onOpen, tile }) => {
130
+ const dims = tile ?? defaultMediaDims();
120
131
 
121
132
  return (
122
133
  <Pressable
123
134
  onPress={onOpen}
124
- style={[styles.wrapper, { width: dims.width, height: dims.height }]}
135
+ style={[
136
+ styles.wrapper,
137
+ { width: dims.width, height: dims.height },
138
+ tile && styles.tileWrapper,
139
+ ]}
125
140
  accessibilityRole="button"
126
141
  accessibilityLabel="Play video"
127
142
  >
@@ -136,11 +151,17 @@ const CustomMessageVideo: React.FC<CustomMessageVideoProps> = ({
136
151
  fileURL,
137
152
  mimetype,
138
153
  previewURL,
154
+ tile,
155
+ onOpen,
139
156
  }) => {
140
157
  const dispatch = useDispatch();
141
158
  const [posterFailed, setPosterFailed] = useState(false);
142
159
 
143
160
  const handleOpen = () => {
161
+ if (onOpen) {
162
+ onOpen();
163
+ return;
164
+ }
144
165
  dispatch(setActiveFile({ fileName, fileURL, mimetype, originalName }));
145
166
  dispatch(setActiveModal(MODAL_TYPES.FILE_PREVIEW));
146
167
  };
@@ -150,6 +171,7 @@ const CustomMessageVideo: React.FC<CustomMessageVideoProps> = ({
150
171
  <PosterVideo
151
172
  previewURL={previewURL}
152
173
  onOpen={handleOpen}
174
+ tile={tile}
153
175
  onPosterError={() => {
154
176
  if (isSecureFileUrl(previewURL)) {
155
177
  requestFileTokenRecovery();
@@ -160,7 +182,7 @@ const CustomMessageVideo: React.FC<CustomMessageVideoProps> = ({
160
182
  );
161
183
  }
162
184
 
163
- return <PlaceholderVideo onOpen={handleOpen} />;
185
+ return <PlaceholderVideo onOpen={handleOpen} tile={tile} />;
164
186
  };
165
187
 
166
188
  export default CustomMessageVideo;
@@ -171,6 +193,9 @@ const styles = StyleSheet.create({
171
193
  overflow: 'hidden',
172
194
  backgroundColor: '#000',
173
195
  },
196
+ tileWrapper: {
197
+ borderRadius: 0,
198
+ },
174
199
  video: {
175
200
  width: '100%',
176
201
  height: '100%',
@@ -63,6 +63,7 @@ import { updateMessagesTillLast } from '../helpers/updateMessagesTillLast';
63
63
  import { secureUserStorage } from '../helpers/secureUserStorage';
64
64
  import { clearPersistedState } from '../roomStore/persistence';
65
65
  import { pushLog as devPushLog } from '../utils/devLogger';
66
+ import { computeReadWatermark } from '../helpers/readReceipts';
66
67
 
67
68
  interface InitMode {
68
69
  current: 'provider' | 'chat';
@@ -794,6 +795,26 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config, is
794
795
  let lastMessagesRef: any = null;
795
796
  let lastStampedMs = 0;
796
797
  let flushTimer: ReturnType<typeof setTimeout> | null = null;
798
+ // Room whose XEP-0333 marker is waiting on the 2 s debounce below. Set
799
+ // only while that room is the one on screen.
800
+ let pendingMarkerJid: string | null = null;
801
+
802
+ // Sends the waiting marker now: the debounce is cut short because the
803
+ // user left the room or the app, so it would otherwise be lost. The room
804
+ // counts as on screen for this one call (it was when the marker was
805
+ // queued); the jump window, the scroll boundary and the disable flags
806
+ // still apply inside computeReadWatermark.
807
+ const flushPendingMarker = () => {
808
+ const jid = pendingMarkerJid;
809
+ pendingMarkerJid = null;
810
+ if (!jid) {return;}
811
+ const st = store.getState();
812
+ const watermark = computeReadWatermark(
813
+ { ...st, rooms: { ...st.rooms, visibleRoomJID: jid } } as any,
814
+ jid
815
+ );
816
+ if (watermark) {client?.sendDisplayedMarkerStanza?.(watermark, jid);}
817
+ };
797
818
 
798
819
  const advance = () => {
799
820
  const s = store.getState();
@@ -826,9 +847,13 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config, is
826
847
  setLastViewedTimestamp({ chatJID: jid, timestamp: newest })
827
848
  );
828
849
 
850
+ // Another room took over the debounce: send the old room's marker first.
851
+ if (pendingMarkerJid && pendingMarkerJid !== jid) {flushPendingMarker();}
829
852
  if (flushTimer) {clearTimeout(flushTimer);}
830
853
  if (client?.flushLastViewedToPrivateStoreStanza) {
854
+ if (s.rooms?.visibleRoomJID === jid) {pendingMarkerJid = jid;}
831
855
  flushTimer = setTimeout(() => {
856
+ pendingMarkerJid = null;
832
857
  // Re-read fresh state rather than closing over `boundaryTs`:
833
858
  // the user may have scrolled back to the bottom (clearing the
834
859
  // boundary) in the 2s between the stamp and this flush, and
@@ -845,6 +870,12 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config, is
845
870
  ),
846
871
  })
847
872
  .catch(() => {});
873
+ // XEP-0333 read marker, on the same debounce as the private-store
874
+ // write above. computeReadWatermark sends nothing unless the room is
875
+ // still on screen, no jump window is open and the newest message
876
+ // is someone else's.
877
+ const watermark = computeReadWatermark(freshState, jid);
878
+ if (watermark) {client.sendDisplayedMarkerStanza?.(watermark, jid);}
848
879
  }, 2000);
849
880
  }
850
881
  };
@@ -854,6 +885,7 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config, is
854
885
  return () => {
855
886
  unsub();
856
887
  if (flushTimer) {clearTimeout(flushTimer);}
888
+ flushPendingMarker();
857
889
  };
858
890
  }, [isVisible, client]);
859
891
 
@@ -12,7 +12,7 @@ import {
12
12
  type Progress,
13
13
  } from './fileCipher';
14
14
 
15
- export { parseSealedMediaBody } from './sealedBody';
15
+ export { parseSealedMediaBody, parseSealedMediaPayload } from './sealedBody';
16
16
 
17
17
  const MAGIC = Uint8Array.from([0x45, 0x54, 0x48, 0x4f, 0x46, 0x49, 0x4c, 0x45]);
18
18
  const VERSION = 1;
@@ -1,17 +1,56 @@
1
- export function parseSealedMediaBody(body?: string): string[] | undefined {
1
+ import type { IMentionSpan } from '../types/models/message.model';
2
+ import { normalizeCaptionInput } from '../helpers/caption';
3
+
4
+ /** What a sealed media <body> carries besides the keys. */
5
+ export interface SealedMediaPayload {
6
+ keys: string[];
7
+ /** The text typed with the files. Sealed here, never on <data>. */
8
+ caption?: string;
9
+ captionMentions?: IMentionSpan[];
10
+ }
11
+
12
+ /**
13
+ * Pulls the attachment keys (and the caption, when there is one) out of a
14
+ * decrypted media <body>: `{v:1,keys:[...],caption?,captionMentions?}`, which
15
+ * the sender writes in place of the usual literal "media". Undefined for
16
+ * anything else, so an ordinary media message falls through untouched.
17
+ */
18
+ export function parseSealedMediaPayload(
19
+ body?: string
20
+ ): SealedMediaPayload | undefined {
2
21
  const text = String(body || '').trim();
3
22
  if (!text.startsWith('{')) {return undefined;}
4
23
 
5
24
  try {
6
- const parsed = JSON.parse(text) as { v?: unknown; keys?: unknown };
25
+ const parsed = JSON.parse(text) as {
26
+ v?: unknown;
27
+ keys?: unknown;
28
+ caption?: unknown;
29
+ captionMentions?: unknown;
30
+ };
7
31
  if (parsed?.v !== 1 || !Array.isArray(parsed.keys)) {return undefined;}
8
32
  const keys = parsed.keys.filter(
9
33
  (k): k is string => typeof k === 'string' && k.length > 0
10
34
  );
11
- return keys.length === parsed.keys.length && keys.length > 0
12
- ? keys
13
- : undefined;
35
+ if (!(keys.length === parsed.keys.length && keys.length > 0)) {
36
+ return undefined;
37
+ }
38
+
39
+ const caption = normalizeCaptionInput({
40
+ text: typeof parsed.caption === 'string' ? parsed.caption : '',
41
+ mentions: parsed.captionMentions as IMentionSpan[] | undefined,
42
+ });
43
+ return {
44
+ keys,
45
+ ...(caption ? { caption: caption.caption } : {}),
46
+ ...(caption?.mentions ? { captionMentions: caption.mentions } : {}),
47
+ };
14
48
  } catch {
15
49
  return undefined;
16
50
  }
17
51
  }
52
+
53
+ /** The attachment keys out of a sealed media <body>; see parseSealedMediaPayload. */
54
+ export function parseSealedMediaBody(body?: string): string[] | undefined {
55
+ return parseSealedMediaPayload(body)?.keys;
56
+ }
@@ -0,0 +1,207 @@
1
+ import type { ViewStyle } from 'react-native';
2
+
3
+ /**
4
+ * Geometry for a multi-media album bubble. Pure numbers and RN style objects,
5
+ * so AlbumGrid stays a dumb renderer and the rules are testable without a
6
+ * renderer.
7
+ *
8
+ * 1 item full width, 4:3
9
+ * 2 items side by side, 2:1 (square cells)
10
+ * 3 items one large cell on the left, two stacked on the right
11
+ * 4 items 2 x 2
12
+ * 5+ items 2 x 2 showing the first four, the last cell carries "+N"
13
+ */
14
+
15
+ /** Gap between neighbouring tiles. */
16
+ export const ALBUM_GAP = 3;
17
+ /** Radius of a tile corner that sits on the bubble's own corner. */
18
+ export const ALBUM_OUTER_RADIUS = 10;
19
+ /** Radius of every other tile corner, the ones that face a neighbour. */
20
+ export const ALBUM_INNER_RADIUS = 3;
21
+
22
+ export interface AlbumCellPlacement {
23
+ /** 0-based grid column and row of the tile's top-left cell. */
24
+ column: number;
25
+ row: number;
26
+ columnSpan: number;
27
+ rowSpan: number;
28
+ }
29
+
30
+ export type AlbumKind = 'full' | 'pair' | 'hero' | 'quad';
31
+
32
+ export interface AlbumLayout {
33
+ kind: AlbumKind;
34
+ /** Number of tiles actually drawn. */
35
+ visible: number;
36
+ /** Items folded into the "+N" overlay on the last visible tile. */
37
+ hidden: number;
38
+ /** Relative width of each grid column. */
39
+ columns: number[];
40
+ /** Relative height of each grid row. */
41
+ rows: number[];
42
+ /** Width divided by height of the whole grid. */
43
+ aspectRatio: number;
44
+ /** One entry per visible tile, in order. */
45
+ placements: AlbumCellPlacement[];
46
+ }
47
+
48
+ const cell = (
49
+ column: number,
50
+ row: number,
51
+ rowSpan = 1
52
+ ): AlbumCellPlacement => ({ column, row, columnSpan: 1, rowSpan });
53
+
54
+ const LAYOUTS: Record<AlbumKind, Omit<AlbumLayout, 'visible' | 'hidden'>> = {
55
+ full: {
56
+ kind: 'full',
57
+ columns: [1],
58
+ rows: [1],
59
+ aspectRatio: 4 / 3,
60
+ placements: [cell(0, 0)],
61
+ },
62
+ pair: {
63
+ kind: 'pair',
64
+ columns: [1, 1],
65
+ rows: [1],
66
+ aspectRatio: 2,
67
+ placements: [cell(0, 0), cell(1, 0)],
68
+ },
69
+ hero: {
70
+ kind: 'hero',
71
+ columns: [2, 1],
72
+ rows: [1, 1],
73
+ aspectRatio: 4 / 3,
74
+ placements: [cell(0, 0, 2), cell(1, 0), cell(1, 1)],
75
+ },
76
+ quad: {
77
+ kind: 'quad',
78
+ columns: [1, 1],
79
+ rows: [1, 1],
80
+ aspectRatio: 1,
81
+ placements: [cell(0, 0), cell(1, 0), cell(0, 1), cell(1, 1)],
82
+ },
83
+ };
84
+
85
+ /** Layout for `count` media tiles. Counts below 1 are treated as 1. */
86
+ export const getAlbumLayout = (count: number): AlbumLayout => {
87
+ const total = Math.max(1, Math.floor(Number.isFinite(count) ? count : 1));
88
+ const kind: AlbumKind =
89
+ total === 1 ? 'full' : total === 2 ? 'pair' : total === 3 ? 'hero' : 'quad';
90
+ const base = LAYOUTS[kind];
91
+ const visible = base.placements.length;
92
+ return {
93
+ ...base,
94
+ columns: [...base.columns],
95
+ rows: [...base.rows],
96
+ placements: base.placements.map((placement) => ({ ...placement })),
97
+ visible,
98
+ hidden: Math.max(0, total - visible),
99
+ };
100
+ };
101
+
102
+ /** Splits `total` across weighted tracks, leaving `gap` between neighbours. */
103
+ const trackSizes = (weights: number[], total: number, gap: number): number[] => {
104
+ const room = total - gap * (weights.length - 1);
105
+ const sum = weights.reduce((acc, weight) => acc + weight, 0);
106
+ return weights.map((weight) => (room * weight) / sum);
107
+ };
108
+
109
+ export interface AlbumFrame {
110
+ width: number;
111
+ height: number;
112
+ /** One absolutely positioned style per visible tile, in order. */
113
+ tiles: ViewStyle[];
114
+ }
115
+
116
+ /** Pixel frame of the grid and of every tile for a grid `width` wide. */
117
+ export const getAlbumFrame = (layout: AlbumLayout, width: number): AlbumFrame => {
118
+ const height = Math.round(width / layout.aspectRatio);
119
+ const colWidths = trackSizes(layout.columns, width, ALBUM_GAP);
120
+ const rowHeights = trackSizes(layout.rows, height, ALBUM_GAP);
121
+ const offset = (sizes: number[], index: number) =>
122
+ sizes.slice(0, index).reduce((acc, size) => acc + size + ALBUM_GAP, 0);
123
+ const span = (sizes: number[], index: number, count: number) =>
124
+ sizes.slice(index, index + count).reduce((acc, size) => acc + size, 0) +
125
+ ALBUM_GAP * (count - 1);
126
+
127
+ return {
128
+ width,
129
+ height,
130
+ tiles: layout.placements.map((placement, position) => ({
131
+ position: 'absolute',
132
+ left: Math.round(offset(colWidths, placement.column)),
133
+ top: Math.round(offset(rowHeights, placement.row)),
134
+ width: Math.round(span(colWidths, placement.column, placement.columnSpan)),
135
+ height: Math.round(span(rowHeights, placement.row, placement.rowSpan)),
136
+ overflow: 'hidden',
137
+ ...getTileBorderRadius(layout, position),
138
+ })),
139
+ };
140
+ };
141
+
142
+ /**
143
+ * Which corners of one tile lie on the bubble's outer edge. Only those get
144
+ * the large radius; corners that face a neighbour get the small inner one.
145
+ */
146
+ export interface TileCorners {
147
+ topLeft: boolean;
148
+ topRight: boolean;
149
+ bottomRight: boolean;
150
+ bottomLeft: boolean;
151
+ }
152
+
153
+ const corners = (
154
+ topLeft: boolean,
155
+ topRight: boolean,
156
+ bottomRight: boolean,
157
+ bottomLeft: boolean
158
+ ): TileCorners => ({ topLeft, topRight, bottomRight, bottomLeft });
159
+
160
+ const OUTER_CORNERS: Record<AlbumKind, TileCorners[]> = {
161
+ full: [corners(true, true, true, true)],
162
+ pair: [corners(true, false, false, true), corners(false, true, true, false)],
163
+ // Hero: the tall left cell owns both left corners; the right column owns
164
+ // its top-right and bottom-right corners respectively.
165
+ hero: [
166
+ corners(true, false, false, true),
167
+ corners(false, true, false, false),
168
+ corners(false, false, true, false),
169
+ ],
170
+ quad: [
171
+ corners(true, false, false, false),
172
+ corners(false, true, false, false),
173
+ corners(false, false, false, true),
174
+ corners(false, false, true, false),
175
+ ],
176
+ };
177
+
178
+ /** Outer-corner flags for the tile at `position` (0-based) of `layout`. */
179
+ export const getTileCorners = (
180
+ layout: AlbumLayout,
181
+ position: number
182
+ ): TileCorners => {
183
+ const found = OUTER_CORNERS[layout.kind][position];
184
+ return found ? { ...found } : corners(false, false, false, false);
185
+ };
186
+
187
+ /** The four border radii of one tile, as RN style props. */
188
+ export const getTileBorderRadius = (
189
+ layout: AlbumLayout,
190
+ position: number
191
+ ): Pick<
192
+ ViewStyle,
193
+ | 'borderTopLeftRadius'
194
+ | 'borderTopRightRadius'
195
+ | 'borderBottomRightRadius'
196
+ | 'borderBottomLeftRadius'
197
+ > => {
198
+ const c = getTileCorners(layout, position);
199
+ const radius = (outer: boolean) =>
200
+ outer ? ALBUM_OUTER_RADIUS : ALBUM_INNER_RADIUS;
201
+ return {
202
+ borderTopLeftRadius: radius(c.topLeft),
203
+ borderTopRightRadius: radius(c.topRight),
204
+ borderBottomRightRadius: radius(c.bottomRight),
205
+ borderBottomLeftRadius: radius(c.bottomLeft),
206
+ };
207
+ };