@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
@@ -153,26 +153,50 @@ export const VideoCallIcon: React.FC<IconProps> = ({
153
153
  </Svg>
154
154
  );
155
155
 
156
+ /**
157
+ * Front/back camera flip: a camera between two arrows turning around it
158
+ * (Material "cameraswitch", the glyph Telegram and the Android camera use),
159
+ * so it reads as "use the other camera", not as "reload".
160
+ */
156
161
  export const SwitchCameraIcon: React.FC<IconProps> = ({
157
162
  color = '#FFFFFF',
158
163
  size = 24,
159
164
  }) => (
160
- <Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
165
+ <Svg width={size} height={size} viewBox="0 0 24 24">
161
166
  <Path
162
- d="M9 4h6l1.2 2H20a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h3.8L9 4z"
163
- stroke={color}
164
- strokeWidth={1.8}
165
- fill="none"
166
- strokeLinejoin="round"
167
+ d="M16 7h-1l-1-1h-4L9 7H8c-1.1 0-2 .9-2 2v6c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V9c0-1.1-.9-2-2-2zm-4 7c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"
168
+ fill={color}
167
169
  />
168
170
  <Path
169
- d="M9.5 12.5A2.5 2.5 0 0 1 14 11m.5 2.5A2.5 2.5 0 0 1 10 15"
170
- stroke={color}
171
- strokeWidth={1.8}
172
- strokeLinecap="round"
173
- fill="none"
171
+ d="M8.57.51l4.48 4.48V2.04c4.72.47 8.48 4.23 8.95 8.95h2C23.34 3.02 15.49-1.59 8.57.51zm2.38 21.45c-4.72-.47-8.48-4.23-8.95-8.95H0c.66 7.97 8.51 12.58 15.43 10.48l-4.48-4.48v2.95z"
172
+ fill={color}
173
+ />
174
+ </Svg>
175
+ );
176
+
177
+ /** Stretch the video to fill the screen (Material "fullscreen"). */
178
+ export const FillVideoIcon: React.FC<IconProps> = ({
179
+ color = '#FFFFFF',
180
+ size = 24,
181
+ }) => (
182
+ <Svg width={size} height={size} viewBox="0 0 24 24">
183
+ <Path
184
+ d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"
185
+ fill={color}
186
+ />
187
+ </Svg>
188
+ );
189
+
190
+ /** Show the whole video, letterboxed (Material "fit_screen"). */
191
+ export const FitVideoIcon: React.FC<IconProps> = ({
192
+ color = '#FFFFFF',
193
+ size = 24,
194
+ }) => (
195
+ <Svg width={size} height={size} viewBox="0 0 24 24">
196
+ <Path
197
+ d="M17 4h3c1.1 0 2 .9 2 2v2h-2V6h-3V4zM4 8V6h3V4H4c-1.1 0-2 .9-2 2v2h2zm16 8v2h-3v2h3c1.1 0 2-.9 2-2v-2h-2zM7 18H4v-2H2v2c0 1.1.9 2 2 2h3v-2zM18 8H6v8h12V8z"
198
+ fill={color}
174
199
  />
175
- <Path d="M14 9.5V11h-1.5M10 16.5V15h1.5" stroke={color} strokeWidth={1.8} />
176
200
  </Svg>
177
201
  );
178
202
 
@@ -12,14 +12,17 @@ import {
12
12
  BackHandler,
13
13
  Easing,
14
14
  Modal,
15
+ PanResponder,
15
16
  Platform,
16
17
  StyleSheet,
17
18
  Text,
18
19
  TouchableOpacity,
20
+ useWindowDimensions,
19
21
  Vibration,
20
22
  View,
21
23
  } from 'react-native';
22
24
  import { useDispatch, useSelector } from 'react-redux';
25
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
23
26
  import { RootState } from '../../roomStore';
24
27
  import {
25
28
  acceptIncomingCall,
@@ -33,7 +36,13 @@ import type { ChatTheme } from '../../theme/theme';
33
36
  import { VideoCallSession } from './VideoCallSession';
34
37
  import { startCallSession } from './callSessionController';
35
38
  import { declineActiveCall, terminateActiveCall } from './callActions';
36
- import { DockedCallBar } from './ActiveCallBar';
39
+ import {
40
+ CALL_BAR_HEIGHT,
41
+ CALL_BAR_MARGIN_TOP,
42
+ CALL_BAR_MARGIN_X,
43
+ CALL_BAR_RADIUS,
44
+ DockedCallBar,
45
+ } from './ActiveCallBar';
37
46
  import { setCallMinimized, useCallUi } from './callUiStore';
38
47
  import { ProfileImagePlaceholder } from '../MainComponents/ProfileImagePlaceholder';
39
48
  import {
@@ -44,6 +53,7 @@ import {
44
53
  import { VideoCallIcons } from '../../types/types';
45
54
  import { useT } from '../../i18n/useT';
46
55
  import { useCallKeep } from './useCallKeep';
56
+ import { useOngoingCallNotification } from './useOngoingCallNotification';
47
57
 
48
58
  const DEFAULT_PRIMARY = '#0052CD';
49
59
  const DANGER = '#E53935';
@@ -250,6 +260,160 @@ const RingingCard: FC<RingingCardProps> = ({
250
260
  );
251
261
  };
252
262
 
263
+ /**
264
+ * The full-screen call, collapsible into the header strip, Telegram-style:
265
+ * pulling it up morphs the whole screen into the green bubble under the
266
+ * header (position, width, height and corner radius all travel together,
267
+ * and the bubble's colour fades in on the way), so it visibly *becomes* the
268
+ * strip rather than leaving and the strip popping in. Let go past a quarter
269
+ * of the way, or flick, and it finishes; released early it springs back.
270
+ * The minimize button runs the same morph; expanding from the strip runs it
271
+ * backwards.
272
+ *
273
+ * Buttons and the draggable video tile handle their own touches; a drag
274
+ * anywhere else on the screen is ours, and only its upward part counts.
275
+ */
276
+ const CollapsibleCallSurface: FC<{
277
+ enterFromTop: boolean;
278
+ barColor: string;
279
+ onCollapsed: () => void;
280
+ children: (collapse: () => void) => ReactNode;
281
+ }> = ({ enterFromTop, barColor, onCollapsed, children }) => {
282
+ const { width, height } = useWindowDimensions();
283
+ const insets = useSafeAreaInsets();
284
+ const { barAnchorY } = useCallUi();
285
+ // Where the bubble sits: under the hosting header, or under the status
286
+ // bar when no header is on screen (the docked fallback).
287
+ const target = {
288
+ top: (barAnchorY ?? insets.top) + CALL_BAR_MARGIN_TOP,
289
+ side: CALL_BAR_MARGIN_X,
290
+ height: CALL_BAR_HEIGHT,
291
+ };
292
+ // 0 = full screen, 1 = the bubble.
293
+ const progress = useRef(new Animated.Value(enterFromTop ? 1 : 0)).current;
294
+ const collapsingRef = useRef(false);
295
+ const onCollapsedRef = useRef(onCollapsed);
296
+ onCollapsedRef.current = onCollapsed;
297
+ const heightRef = useRef(height);
298
+ heightRef.current = height;
299
+
300
+ useEffect(() => {
301
+ if (!enterFromTop) {return;}
302
+ Animated.timing(progress, {
303
+ toValue: 0,
304
+ duration: 300,
305
+ easing: Easing.out(Easing.cubic),
306
+ useNativeDriver: false,
307
+ }).start();
308
+ // Mount-only: the entrance plays once.
309
+ // eslint-disable-next-line react-hooks/exhaustive-deps
310
+ }, []);
311
+
312
+ const collapse = useCallback(() => {
313
+ if (collapsingRef.current) {return;}
314
+ collapsingRef.current = true;
315
+ Animated.timing(progress, {
316
+ toValue: 1,
317
+ duration: 280,
318
+ easing: Easing.out(Easing.cubic),
319
+ useNativeDriver: false,
320
+ }).start(() => onCollapsedRef.current());
321
+ }, [progress]);
322
+
323
+ const panHandlers = useMemo(() => {
324
+ const springBack = () =>
325
+ Animated.spring(progress, {
326
+ toValue: 0,
327
+ useNativeDriver: false,
328
+ damping: 22,
329
+ stiffness: 260,
330
+ }).start();
331
+ // How far the finger travels for the full morph.
332
+ const travel = () => heightRef.current * 0.6;
333
+ return PanResponder.create({
334
+ // Claim the touch as soon as it starts on the call screen itself
335
+ // (buttons and the video tile are deeper and win their own touches).
336
+ // Waiting for a move does not work here: the Modal's content sits
337
+ // under the host's screens in the React tree, and an ancestor there
338
+ // (a Pressable / keyboard-dismiss wrapper) takes the touch on start,
339
+ // after which no descendant is ever asked again.
340
+ onStartShouldSetPanResponder: () => !collapsingRef.current,
341
+ onMoveShouldSetPanResponder: () => !collapsingRef.current,
342
+ onPanResponderTerminationRequest: () => false,
343
+ onPanResponderMove: (_event, gesture) => {
344
+ progress.setValue(Math.min(1, Math.max(0, -gesture.dy / travel())));
345
+ },
346
+ onPanResponderRelease: (_event, gesture) => {
347
+ if (-gesture.dy / travel() > 0.25 || gesture.vy < -0.8) {
348
+ collapse();
349
+ } else {
350
+ springBack();
351
+ }
352
+ },
353
+ onPanResponderTerminate: springBack,
354
+ }).panHandlers;
355
+ }, [progress, collapse]);
356
+
357
+ const at = (from: number, to: number) =>
358
+ progress.interpolate({ inputRange: [0, 1], outputRange: [from, to] });
359
+
360
+ return (
361
+ <Animated.View
362
+ {...panHandlers}
363
+ style={[
364
+ styles.collapsible,
365
+ {
366
+ top: at(0, target.top),
367
+ left: at(0, target.side),
368
+ width: at(width, width - target.side * 2),
369
+ height: at(height, target.height),
370
+ // Rounded as soon as it starts moving (a card, like the screen's
371
+ // own corners), then tightening to the bubble's radius.
372
+ borderRadius: progress.interpolate({
373
+ inputRange: [0, 0.08, 0.7, 1],
374
+ outputRange: [0, 36, 28, CALL_BAR_RADIUS],
375
+ extrapolate: 'clamp',
376
+ }),
377
+ },
378
+ ]}
379
+ >
380
+ {/* Fixed full-screen size: the content is clipped, never re-laid out
381
+ per frame (the video views would stutter). It fades as the bubble's
382
+ colour takes over. */}
383
+ <Animated.View
384
+ style={[
385
+ styles.sessionFixed,
386
+ {
387
+ width,
388
+ height,
389
+ opacity: progress.interpolate({
390
+ inputRange: [0, 0.55, 0.85],
391
+ outputRange: [1, 0.85, 0],
392
+ extrapolate: 'clamp',
393
+ }),
394
+ },
395
+ ]}
396
+ >
397
+ {children(collapse)}
398
+ </Animated.View>
399
+ <Animated.View
400
+ pointerEvents="none"
401
+ style={[
402
+ StyleSheet.absoluteFill,
403
+ {
404
+ backgroundColor: barColor,
405
+ opacity: progress.interpolate({
406
+ inputRange: [0, 0.35, 0.9],
407
+ outputRange: [0, 0, 1],
408
+ extrapolate: 'clamp',
409
+ }),
410
+ },
411
+ ]}
412
+ />
413
+ </Animated.View>
414
+ );
415
+ };
416
+
253
417
  /**
254
418
  * A crash in the call UI must not take the chat down with it (web wraps
255
419
  * the overlay in a silent ChatErrorBoundary). Renders nothing on error and
@@ -308,15 +472,25 @@ const VideoCallOverlayInner: FC = () => {
308
472
  // callkeep. No-op otherwise, the in-app ring screen below is the
309
473
  // fallback and always renders.
310
474
  useCallKeep();
475
+ // Host-provided system notification (Android) with hang up / mute /
476
+ // camera buttons while the call runs.
477
+ useOngoingCallNotification(enabled ? videoCallsConfig?.ongoingCallNotification : undefined);
311
478
 
312
479
  // Shared with the header strip (ActiveCallBar), which expands it again.
313
- const { minimized } = useCallUi();
480
+ const { minimized, systemRinging } = useCallUi();
314
481
 
315
482
  const canRenderSession =
316
483
  !!call.token &&
317
484
  (call.phase === 'connecting' || call.phase === 'in-call') &&
318
485
  !!livekitUrl;
319
486
  const isMinimized = minimized && canRenderSession;
487
+ // Expanding from the header strip slides the call down from the top
488
+ // instead of fading it in.
489
+ const wasMinimizedRef = useRef(false);
490
+ const enterFromTop = wasMinimizedRef.current && !isMinimized;
491
+ useEffect(() => {
492
+ wasMinimizedRef.current = isMinimized;
493
+ }, [isMinimized]);
320
494
 
321
495
  // Join the LiveKit room once the call has a token. Idempotent, and the
322
496
  // controller tears itself down when the call slice leaves these phases.
@@ -352,10 +526,11 @@ const VideoCallOverlayInner: FC = () => {
352
526
  useEffect(() => {
353
527
  const isRinging =
354
528
  call.direction === 'incoming' && call.phase === 'ringing-incoming';
355
- if (!isRinging) {return;}
529
+ // CallKit rings (and vibrates) on its own.
530
+ if (!isRinging || systemRinging) {return;}
356
531
  Vibration.vibrate(RING_VIBRATION_PATTERN, true);
357
532
  return () => Vibration.cancel();
358
- }, [call.direction, call.phase]);
533
+ }, [call.direction, call.phase, systemRinging]);
359
534
 
360
535
  const hangupCurrent = useCallback(() => {
361
536
  if (call.direction === 'incoming' && call.phase === 'ringing-incoming') {
@@ -410,13 +585,19 @@ const VideoCallOverlayInner: FC = () => {
410
585
  const showIncomingDecision =
411
586
  call.direction === 'incoming' && call.phase === 'ringing-incoming';
412
587
 
413
- const session = (
588
+ // The system's incoming-call banner is up (iOS CallKit); answering or
589
+ // declining it drives the call from there.
590
+ if (showIncomingDecision && systemRinging) {
591
+ return null;
592
+ }
593
+
594
+ const renderSession = (collapse: () => void) => (
414
595
  <VideoCallSession
415
596
  kind={call.kind}
416
597
  peerName={call.roomName}
417
598
  primaryColor={primaryColor}
418
599
  icons={videoCallsConfig?.icons}
419
- onToggleMinimize={() => setCallMinimized(!minimized)}
600
+ onToggleMinimize={collapse}
420
601
  onHangup={() => terminateActiveCall('ended')}
421
602
  />
422
603
  );
@@ -432,7 +613,7 @@ const VideoCallOverlayInner: FC = () => {
432
613
  <Modal
433
614
  visible
434
615
  transparent
435
- animationType="fade"
616
+ animationType={enterFromTop ? 'none' : 'fade'}
436
617
  statusBarTranslucent
437
618
  supportedOrientations={['portrait', 'landscape']}
438
619
  onRequestClose={hangupCurrent}
@@ -440,7 +621,13 @@ const VideoCallOverlayInner: FC = () => {
440
621
  {canRenderSession ? (
441
622
  // Full-bleed: on a phone the call owns the screen, like the native
442
623
  // dialer. The audio call keeps the same dark canvas as video.
443
- <View style={styles.sessionFull}>{session}</View>
624
+ <CollapsibleCallSurface
625
+ enterFromTop={enterFromTop}
626
+ barColor={theme.success}
627
+ onCollapsed={() => setCallMinimized(true)}
628
+ >
629
+ {renderSession}
630
+ </CollapsibleCallSurface>
444
631
  ) : (
445
632
  <View style={[styles.backdrop, { backgroundColor: theme.overlay }]}>
446
633
  {showIncomingDecision ? (
@@ -512,6 +699,16 @@ const styles = StyleSheet.create({
512
699
  flex: 1,
513
700
  backgroundColor: '#0B1220',
514
701
  },
702
+ // No flex here: `flex: 1` would override the fixed height and re-lay the
703
+ // call out on every frame of the morph.
704
+ sessionFixed: {
705
+ backgroundColor: '#0B1220',
706
+ },
707
+ collapsible: {
708
+ position: 'absolute',
709
+ overflow: 'hidden',
710
+ backgroundColor: '#0B1220',
711
+ },
515
712
  ringingCard: {
516
713
  width: '100%',
517
714
  maxWidth: 420,
@@ -14,6 +14,8 @@ import { ProfileImagePlaceholder } from '../MainComponents/ProfileImagePlacehold
14
14
  import {
15
15
  CameraOffIcon,
16
16
  CameraOnIcon,
17
+ FillVideoIcon,
18
+ FitVideoIcon,
17
19
  HangUpIcon,
18
20
  MicOffIcon,
19
21
  MicOnIcon,
@@ -31,7 +33,7 @@ import {
31
33
  toggleCallSpeaker,
32
34
  useCallSession,
33
35
  } from './callSessionController';
34
- import { useDraggable } from './useDraggable';
36
+ import { usePipTile } from './usePipTile';
35
37
  import { useT } from '../../i18n/useT';
36
38
 
37
39
  const DEFAULT_PRIMARY = '#0052CD';
@@ -99,10 +101,14 @@ export const VideoCallSession: FC<VideoCallSessionProps> = ({
99
101
  const insets = useSafeAreaInsets();
100
102
  const runtime = loadLiveKit();
101
103
  const session = useCallSession();
102
- const selfView = useDraggable();
104
+ const pipTile = usePipTile({ topInset: insets.top });
103
105
  const elapsed = useSharedCallElapsed(session.anchorAt);
104
106
  // Telegram-style: tap the small tile to swap it with the big one.
105
107
  const [swapped, setSwapped] = useState(false);
108
+ // The peer's video is shown whole by default: a web camera is landscape,
109
+ // and filling a portrait phone crops most of it away. The button next to
110
+ // minimize stretches it to fill (the old behaviour) and back.
111
+ const [fillRemote, setFillRemote] = useState(false);
106
112
 
107
113
  if (!runtime) {
108
114
  return (
@@ -124,6 +130,7 @@ export const VideoCallSession: FC<VideoCallSessionProps> = ({
124
130
  deviceError,
125
131
  remoteMicMuted,
126
132
  facingMode,
133
+ localVideoEpoch,
127
134
  } = session;
128
135
  // The call slice's name (the chat title) first, as web does: LiveKit's
129
136
  // participant name is often just the opaque xmpp identity.
@@ -137,8 +144,14 @@ export const VideoCallSession: FC<VideoCallSessionProps> = ({
137
144
  const isSwapped = swapped && canSwap;
138
145
  const mainVideo = isSwapped ? localVideo : showRemoteVideo ? remoteVideo : null;
139
146
  const pipVideo = isSwapped ? remoteVideo : localLive ? localVideo : null;
147
+ const mainIsRemote = !!mainVideo && mainVideo === remoteVideo;
148
+ const mainFit = mainIsRemote && !fillRemote ? 'contain' : 'cover';
140
149
  // Only the front camera is mirrored, as in every camera app.
141
150
  const selfMirror = facingMode === 'user';
151
+ // The local view remounts when the camera is re-acquired (switching
152
+ // front/back), so it never keeps rendering the stopped stream.
153
+ const videoKey = (ref: any) =>
154
+ ref === localVideo ? `local-${localVideoEpoch}` : 'remote';
142
155
 
143
156
  const statusText =
144
157
  elapsed !== null
@@ -154,12 +167,13 @@ export const VideoCallSession: FC<VideoCallSessionProps> = ({
154
167
  return (
155
168
  <View style={styles.canvas}>
156
169
  {/* Stage */}
157
- <View style={styles.stage}>
170
+ <View style={styles.stage} onLayout={pipTile.onStageLayout}>
158
171
  {mainVideo ? (
159
172
  <VideoTrack
173
+ key={videoKey(mainVideo)}
160
174
  trackRef={mainVideo}
161
175
  style={styles.remoteVideo}
162
- objectFit="cover"
176
+ objectFit={mainFit}
163
177
  mirror={isSwapped && selfMirror}
164
178
  zOrder={0}
165
179
  />
@@ -204,12 +218,12 @@ export const VideoCallSession: FC<VideoCallSessionProps> = ({
204
218
  </View>
205
219
  )}
206
220
 
207
- {/* Picture-in-picture: draggable like web's self-view, tap to swap
208
- it with the main video. */}
221
+ {/* Picture-in-picture: drag it and it snaps to a corner, pinch it
222
+ for the large size, tap it to swap it with the main video. */}
209
223
  {pipVideo && (
210
224
  <Animated.View
211
- {...selfView.panHandlers}
212
- style={[styles.pip, { top: 16 + insets.top }, selfView.style]}
225
+ {...pipTile.panHandlers}
226
+ style={[styles.pip, pipTile.style]}
213
227
  >
214
228
  <Pressable
215
229
  style={styles.pipVideo}
@@ -219,6 +233,7 @@ export const VideoCallSession: FC<VideoCallSessionProps> = ({
219
233
  accessibilityLabel={t('call.action.swapVideo')}
220
234
  >
221
235
  <VideoTrack
236
+ key={videoKey(pipVideo)}
222
237
  trackRef={pipVideo}
223
238
  style={styles.pipVideo}
224
239
  objectFit="cover"
@@ -240,6 +255,23 @@ export const VideoCallSession: FC<VideoCallSessionProps> = ({
240
255
  </TouchableOpacity>
241
256
  )}
242
257
 
258
+ {mainIsRemote && (
259
+ <TouchableOpacity
260
+ accessibilityRole="button"
261
+ accessibilityLabel={
262
+ fillRemote ? t('call.action.fitVideo') : t('call.action.fillVideo')
263
+ }
264
+ onPress={() => setFillRemote((value) => !value)}
265
+ style={[styles.minimizeButton, styles.fitButton, { top: 16 + insets.top }]}
266
+ >
267
+ {fillRemote ? (
268
+ <FitVideoIcon color={TEXT_ON_DARK} size={20} />
269
+ ) : (
270
+ <FillVideoIcon color={TEXT_ON_DARK} size={20} />
271
+ )}
272
+ </TouchableOpacity>
273
+ )}
274
+
243
275
  {!!deviceError && (
244
276
  <TouchableOpacity
245
277
  style={[styles.deviceErrorBanner, { top: 64 + insets.top }]}
@@ -354,7 +386,7 @@ const styles = StyleSheet.create({
354
386
  top: 0,
355
387
  left: 0,
356
388
  right: 0,
357
- paddingHorizontal: 64,
389
+ paddingHorizontal: 108,
358
390
  paddingBottom: 14,
359
391
  alignItems: 'center',
360
392
  backgroundColor: 'rgba(0,0,0,0.35)',
@@ -397,10 +429,6 @@ const styles = StyleSheet.create({
397
429
  fontSize: 12,
398
430
  },
399
431
  pip: {
400
- position: 'absolute',
401
- right: 16,
402
- width: 104,
403
- height: 148,
404
432
  borderRadius: 12,
405
433
  overflow: 'hidden',
406
434
  backgroundColor: '#000',
@@ -422,6 +450,10 @@ const styles = StyleSheet.create({
422
450
  backgroundColor: 'rgba(0,0,0,0.45)',
423
451
  zIndex: 6,
424
452
  },
453
+ // Next to minimize, top-left: the top-right corner belongs to the tile.
454
+ fitButton: {
455
+ left: 60,
456
+ },
425
457
  controls: {
426
458
  flexDirection: 'row',
427
459
  alignItems: 'center',
@@ -33,6 +33,11 @@ export interface CallSessionSnapshot {
33
33
  speakerOn: boolean;
34
34
  /** Which camera the local video comes from; only the front one is mirrored. */
35
35
  facingMode: 'user' | 'environment';
36
+ /**
37
+ * Bumped whenever the local camera track is re-acquired, so the self view
38
+ * can remount onto the new stream instead of holding the stopped one.
39
+ */
40
+ localVideoEpoch: number;
36
41
  /** `{ participant, publication, source }` for `<VideoTrack />`, live and unmuted only. */
37
42
  remoteVideo: any;
38
43
  localVideo: any;
@@ -56,6 +61,7 @@ const IDLE: CallSessionSnapshot = {
56
61
  cameraOn: false,
57
62
  speakerOn: false,
58
63
  facingMode: 'user',
64
+ localVideoEpoch: 0,
59
65
  remoteVideo: null,
60
66
  localVideo: null,
61
67
  remoteName: null,
@@ -299,7 +305,14 @@ export const startCallSession = ({
299
305
  // Claims audio focus and sets the right native audio mode. Must
300
306
  // start BEFORE connecting or the first seconds route to the earpiece
301
307
  // on Android even for a video call.
302
- await AudioSession.startAudioSession();
308
+ try {
309
+ await AudioSession.startAudioSession();
310
+ } catch {
311
+ // A call answered from the CallKit banner: CallKit owns the iOS
312
+ // audio session and activates it itself (useCallKeep forwards that
313
+ // to WebRTC), so our own activation is refused. Not a reason to
314
+ // fail the call.
315
+ }
303
316
  await room.connect(livekitUrl, token);
304
317
  } catch (error: any) {
305
318
  if (current.disposed) {return;}
@@ -350,10 +363,12 @@ export const startCallSession = ({
350
363
 
351
364
  // ---- controls -----------------------------------------------------------
352
365
 
353
- export const toggleCallMic = async (): Promise<void> => {
366
+ export const toggleCallMic = (): Promise<void> => setCallMicEnabled(!snapshot.micOn);
367
+
368
+ /** Set the mic to a given state (system call UI reports absolute values). */
369
+ export const setCallMicEnabled = async (next: boolean): Promise<void> => {
354
370
  const current = session;
355
- if (!current) {return;}
356
- const next = !snapshot.micOn;
371
+ if (!current || snapshot.micOn === next) {return;}
357
372
  emit({ micOn: next });
358
373
  try {
359
374
  await current.room.localParticipant.setMicrophoneEnabled(next);
@@ -379,29 +394,45 @@ export const toggleCallCamera = async (): Promise<void> => {
379
394
  }
380
395
  };
381
396
 
397
+ let switchingCamera = false;
398
+
382
399
  export const switchCallCamera = async (): Promise<void> => {
383
400
  const current = session;
384
401
  const runtime = loadLiveKit();
385
- if (!current || !runtime) {return;}
386
- const mediaTrack = current.room.localParticipant.getTrackPublication?.(
402
+ if (!current || !runtime || switchingCamera) {return;}
403
+ const track = current.room.localParticipant.getTrackPublication?.(
387
404
  runtime.Track.Source.Camera
388
- )?.track?.mediaStreamTrack;
389
- if (!mediaTrack?.applyConstraints) {return;}
405
+ )?.track;
406
+ if (!track?.restartTrack) {return;}
390
407
  const next = snapshot.facingMode === 'user' ? 'environment' : 'user';
408
+ switchingCamera = true;
391
409
  try {
392
- // Swap the capture device inside the SAME track (react-native-webrtc's
393
- // own camera switch). `restartTrack` stopped the published track and
394
- // made a new one, and the native view kept showing the stopped stream:
395
- // the picture froze on the switch.
396
- // Keep the current capture settings (resolution, frame rate) and only
397
- // flip the camera, exactly like the library's `_switchCamera`.
398
- const constraints = { ...(mediaTrack.getSettings?.() || {}) };
399
- delete constraints.deviceId;
400
- await mediaTrack.applyConstraints({ ...constraints, facingMode: next });
401
- if (!current.disposed) {emit({ facingMode: next });}
410
+ // A real device change needs a new capture: react-native-webrtc's
411
+ // native applyConstraints only applies width/height/frameRate and
412
+ // ignores facingMode on both platforms, so the old approach kept the
413
+ // front camera and merely flipped our mirror flag (the picture looked
414
+ // swapped left to right). restartTrack re-acquires the camera with
415
+ // the new facing mode and swaps it into the published sender; the
416
+ // <VideoTrack /> view follows it through TrackEvent.Restarted.
417
+ await track.restartTrack({
418
+ facingMode: next,
419
+ ...(runtime.VideoPresets
420
+ ? { resolution: runtime.VideoPresets.h720.resolution }
421
+ : {}),
422
+ });
423
+ if (current.disposed) {return;}
424
+ // Trust what the camera reports: a device with a single camera stays
425
+ // on it, and the mirror must follow the real camera, not the request.
426
+ const actual = track.mediaStreamTrack?.getSettings?.()?.facingMode;
427
+ emit({
428
+ facingMode: actual === 'user' || actual === 'environment' ? actual : next,
429
+ localVideoEpoch: snapshot.localVideoEpoch + 1,
430
+ });
402
431
  resync();
403
- } catch {
404
- // Single camera, or the switch is still in flight.
432
+ } catch (error) {
433
+ if (!current.disposed) {emit({ deviceError: humanizeDeviceError(error) });}
434
+ } finally {
435
+ switchingCamera = false;
405
436
  }
406
437
  };
407
438