@ethora/chat-component-rn 26.8.6 → 26.8.7

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 (409) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +40 -0
  3. package/lib/commonjs/assets/icons.js +4 -2
  4. package/lib/commonjs/assets/icons.js.map +1 -1
  5. package/lib/commonjs/components/MainComponents/ChatHeader.js +28 -6
  6. package/lib/commonjs/components/MainComponents/ChatHeader.js.map +1 -1
  7. package/lib/commonjs/components/MainComponents/ChatRoom.js +9 -1
  8. package/lib/commonjs/components/MainComponents/ChatRoom.js.map +1 -1
  9. package/lib/commonjs/components/MainComponents/EncryptedSendNotice.js +47 -0
  10. package/lib/commonjs/components/MainComponents/EncryptedSendNotice.js.map +1 -0
  11. package/lib/commonjs/components/MainComponents/EncryptionCard.js +286 -0
  12. package/lib/commonjs/components/MainComponents/EncryptionCard.js.map +1 -0
  13. package/lib/commonjs/components/MainComponents/MediaMessage.js +7 -0
  14. package/lib/commonjs/components/MainComponents/MediaMessage.js.map +1 -1
  15. package/lib/commonjs/components/MainComponents/ReduxWrapper.js +5 -0
  16. package/lib/commonjs/components/MainComponents/ReduxWrapper.js.map +1 -1
  17. package/lib/commonjs/components/MainComponents/SealedAttachment.js +231 -0
  18. package/lib/commonjs/components/MainComponents/SealedAttachment.js.map +1 -0
  19. package/lib/commonjs/components/MessageBubble/BottomReplyContainer.js +2 -1
  20. package/lib/commonjs/components/MessageBubble/BottomReplyContainer.js.map +1 -1
  21. package/lib/commonjs/components/MessageBubble/Message.js +66 -6
  22. package/lib/commonjs/components/MessageBubble/Message.js.map +1 -1
  23. package/lib/commonjs/components/MessageBubble/MessageInteractions.js +2 -1
  24. package/lib/commonjs/components/MessageBubble/MessageInteractions.js.map +1 -1
  25. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js +7 -4
  26. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  27. package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js +159 -16
  28. package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
  29. package/lib/commonjs/components/Modals/FilePreviewModal/PdfView.js +4 -0
  30. package/lib/commonjs/components/Modals/FilePreviewModal/PdfView.js.map +1 -1
  31. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js +29 -6
  32. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  33. package/lib/commonjs/components/Modals/UserSettingsModal/UserSettingsModal.js +5 -1
  34. package/lib/commonjs/components/Modals/UserSettingsModal/UserSettingsModal.js.map +1 -1
  35. package/lib/commonjs/components/RoomComponents/ChatRoomItem.js +32 -9
  36. package/lib/commonjs/components/RoomComponents/ChatRoomItem.js.map +1 -1
  37. package/lib/commonjs/components/RoomComponents/LastMessageItem.js +6 -0
  38. package/lib/commonjs/components/RoomComponents/LastMessageItem.js.map +1 -1
  39. package/lib/commonjs/components/RoomComponents/styled/LastAudioMessage.js +2 -2
  40. package/lib/commonjs/components/RoomComponents/styled/LastAudioMessage.js.map +1 -1
  41. package/lib/commonjs/components/RoomComponents/styled/LastMessageFile.js +4 -6
  42. package/lib/commonjs/components/RoomComponents/styled/LastMessageFile.js.map +1 -1
  43. package/lib/commonjs/components/RoomComponents/styled/LastMessagePhoto.js +2 -2
  44. package/lib/commonjs/components/RoomComponents/styled/LastMessagePhoto.js.map +1 -1
  45. package/lib/commonjs/components/RoomComponents/styled/LastMessageSealed.js +41 -0
  46. package/lib/commonjs/components/RoomComponents/styled/LastMessageSealed.js.map +1 -0
  47. package/lib/commonjs/components/RoomComponents/styled/LastMessageVideo.js +2 -2
  48. package/lib/commonjs/components/RoomComponents/styled/LastMessageVideo.js.map +1 -1
  49. package/lib/commonjs/components/Thread/ThreadWrapper.js +53 -45
  50. package/lib/commonjs/components/Thread/ThreadWrapper.js.map +1 -1
  51. package/lib/commonjs/components/UsersList/UsersList.js +2 -1
  52. package/lib/commonjs/components/UsersList/UsersList.js.map +1 -1
  53. package/lib/commonjs/components/styled/SendInput.js +4 -2
  54. package/lib/commonjs/components/styled/SendInput.js.map +1 -1
  55. package/lib/commonjs/e2ee/crypto.js +207 -0
  56. package/lib/commonjs/e2ee/crypto.js.map +1 -0
  57. package/lib/commonjs/e2ee/deviceStore.js +84 -0
  58. package/lib/commonjs/e2ee/deviceStore.js.map +1 -0
  59. package/lib/commonjs/e2ee/fileCipher.js +95 -0
  60. package/lib/commonjs/e2ee/fileCipher.js.map +1 -0
  61. package/lib/commonjs/e2ee/fileEnvelope.js +79 -0
  62. package/lib/commonjs/e2ee/fileEnvelope.js.map +1 -0
  63. package/lib/commonjs/e2ee/index.js +210 -0
  64. package/lib/commonjs/e2ee/index.js.map +1 -0
  65. package/lib/commonjs/e2ee/limits.js +9 -0
  66. package/lib/commonjs/e2ee/limits.js.map +1 -0
  67. package/lib/commonjs/e2ee/ns.js +8 -0
  68. package/lib/commonjs/e2ee/ns.js.map +1 -0
  69. package/lib/commonjs/e2ee/omemo.js +622 -0
  70. package/lib/commonjs/e2ee/omemo.js.map +1 -0
  71. package/lib/commonjs/e2ee/protobuf.js +149 -0
  72. package/lib/commonjs/e2ee/protobuf.js.map +1 -0
  73. package/lib/commonjs/e2ee/ratchet.js +238 -0
  74. package/lib/commonjs/e2ee/ratchet.js.map +1 -0
  75. package/lib/commonjs/e2ee/sealedBody.js +23 -0
  76. package/lib/commonjs/e2ee/sealedBody.js.map +1 -0
  77. package/lib/commonjs/e2ee/sealedFiles.js +174 -0
  78. package/lib/commonjs/e2ee/sealedFiles.js.map +1 -0
  79. package/lib/commonjs/e2ee/send.js +54 -0
  80. package/lib/commonjs/e2ee/send.js.map +1 -0
  81. package/lib/commonjs/e2ee/stanza.js +114 -0
  82. package/lib/commonjs/e2ee/stanza.js.map +1 -0
  83. package/lib/commonjs/e2ee/store.js +43 -0
  84. package/lib/commonjs/e2ee/store.js.map +1 -0
  85. package/lib/commonjs/helpers/accountName.js +23 -0
  86. package/lib/commonjs/helpers/accountName.js.map +1 -0
  87. package/lib/commonjs/helpers/createMessageFromXml.js +19 -4
  88. package/lib/commonjs/helpers/createMessageFromXml.js.map +1 -1
  89. package/lib/commonjs/helpers/createUserNameFromSetUser.js +2 -1
  90. package/lib/commonjs/helpers/createUserNameFromSetUser.js.map +1 -1
  91. package/lib/commonjs/helpers/displayName.js +16 -0
  92. package/lib/commonjs/helpers/displayName.js.map +1 -0
  93. package/lib/commonjs/helpers/persistCrypto.js +5 -0
  94. package/lib/commonjs/helpers/persistCrypto.js.map +1 -1
  95. package/lib/commonjs/helpers/secureKeyValue.js +20 -0
  96. package/lib/commonjs/helpers/secureKeyValue.js.map +1 -1
  97. package/lib/commonjs/helpers/videoCache.js +136 -0
  98. package/lib/commonjs/helpers/videoCache.js.map +1 -0
  99. package/lib/commonjs/hooks/useLogout.js +8 -0
  100. package/lib/commonjs/hooks/useLogout.js.map +1 -1
  101. package/lib/commonjs/hooks/useSendMessage.js +71 -7
  102. package/lib/commonjs/hooks/useSendMessage.js.map +1 -1
  103. package/lib/commonjs/i18n/strings.js +180 -0
  104. package/lib/commonjs/i18n/strings.js.map +1 -1
  105. package/lib/commonjs/main.js +13 -0
  106. package/lib/commonjs/main.js.map +1 -1
  107. package/lib/commonjs/networking/api-requests/auth.api.js +4 -1
  108. package/lib/commonjs/networking/api-requests/auth.api.js.map +1 -1
  109. package/lib/commonjs/networking/api-requests/rooms.api.js +111 -17
  110. package/lib/commonjs/networking/api-requests/rooms.api.js.map +1 -1
  111. package/lib/commonjs/networking/xmpp/handleStanzas.xmpp.js +40 -0
  112. package/lib/commonjs/networking/xmpp/handleStanzas.xmpp.js.map +1 -1
  113. package/lib/commonjs/networking/xmpp/sendMediaMessage.xmpp.js +16 -3
  114. package/lib/commonjs/networking/xmpp/sendMediaMessage.xmpp.js.map +1 -1
  115. package/lib/commonjs/networking/xmpp/sendTextMessage.xmpp.js +13 -6
  116. package/lib/commonjs/networking/xmpp/sendTextMessage.xmpp.js.map +1 -1
  117. package/lib/commonjs/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.js +14 -6
  118. package/lib/commonjs/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.js.map +1 -1
  119. package/lib/commonjs/networking/xmppClient.js +6 -0
  120. package/lib/commonjs/networking/xmppClient.js.map +1 -1
  121. package/lib/commonjs/roomStore/persistBackend.js +9 -12
  122. package/lib/commonjs/roomStore/persistBackend.js.map +1 -1
  123. package/lib/commonjs/roomStore/persistence.js +8 -1
  124. package/lib/commonjs/roomStore/persistence.js.map +1 -1
  125. package/lib/commonjs/version.js +1 -1
  126. package/lib/module/assets/icons.js +3 -1
  127. package/lib/module/assets/icons.js.map +1 -1
  128. package/lib/module/components/MainComponents/ChatHeader.js +29 -7
  129. package/lib/module/components/MainComponents/ChatHeader.js.map +1 -1
  130. package/lib/module/components/MainComponents/ChatRoom.js +9 -1
  131. package/lib/module/components/MainComponents/ChatRoom.js.map +1 -1
  132. package/lib/module/components/MainComponents/EncryptedSendNotice.js +42 -0
  133. package/lib/module/components/MainComponents/EncryptedSendNotice.js.map +1 -0
  134. package/lib/module/components/MainComponents/EncryptionCard.js +281 -0
  135. package/lib/module/components/MainComponents/EncryptionCard.js.map +1 -0
  136. package/lib/module/components/MainComponents/MediaMessage.js +7 -0
  137. package/lib/module/components/MainComponents/MediaMessage.js.map +1 -1
  138. package/lib/module/components/MainComponents/ReduxWrapper.js +5 -0
  139. package/lib/module/components/MainComponents/ReduxWrapper.js.map +1 -1
  140. package/lib/module/components/MainComponents/SealedAttachment.js +224 -0
  141. package/lib/module/components/MainComponents/SealedAttachment.js.map +1 -0
  142. package/lib/module/components/MessageBubble/BottomReplyContainer.js +2 -1
  143. package/lib/module/components/MessageBubble/BottomReplyContainer.js.map +1 -1
  144. package/lib/module/components/MessageBubble/Message.js +69 -9
  145. package/lib/module/components/MessageBubble/Message.js.map +1 -1
  146. package/lib/module/components/MessageBubble/MessageInteractions.js +2 -1
  147. package/lib/module/components/MessageBubble/MessageInteractions.js.map +1 -1
  148. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js +7 -4
  149. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  150. package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js +161 -18
  151. package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
  152. package/lib/module/components/Modals/FilePreviewModal/PdfView.js +4 -0
  153. package/lib/module/components/Modals/FilePreviewModal/PdfView.js.map +1 -1
  154. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js +32 -8
  155. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  156. package/lib/module/components/Modals/UserSettingsModal/UserSettingsModal.js +4 -0
  157. package/lib/module/components/Modals/UserSettingsModal/UserSettingsModal.js.map +1 -1
  158. package/lib/module/components/RoomComponents/ChatRoomItem.js +32 -9
  159. package/lib/module/components/RoomComponents/ChatRoomItem.js.map +1 -1
  160. package/lib/module/components/RoomComponents/LastMessageItem.js +6 -0
  161. package/lib/module/components/RoomComponents/LastMessageItem.js.map +1 -1
  162. package/lib/module/components/RoomComponents/styled/LastAudioMessage.js +2 -2
  163. package/lib/module/components/RoomComponents/styled/LastAudioMessage.js.map +1 -1
  164. package/lib/module/components/RoomComponents/styled/LastMessageFile.js +4 -6
  165. package/lib/module/components/RoomComponents/styled/LastMessageFile.js.map +1 -1
  166. package/lib/module/components/RoomComponents/styled/LastMessagePhoto.js +2 -2
  167. package/lib/module/components/RoomComponents/styled/LastMessagePhoto.js.map +1 -1
  168. package/lib/module/components/RoomComponents/styled/LastMessageSealed.js +36 -0
  169. package/lib/module/components/RoomComponents/styled/LastMessageSealed.js.map +1 -0
  170. package/lib/module/components/RoomComponents/styled/LastMessageVideo.js +2 -2
  171. package/lib/module/components/RoomComponents/styled/LastMessageVideo.js.map +1 -1
  172. package/lib/module/components/Thread/ThreadWrapper.js +54 -46
  173. package/lib/module/components/Thread/ThreadWrapper.js.map +1 -1
  174. package/lib/module/components/UsersList/UsersList.js +2 -1
  175. package/lib/module/components/UsersList/UsersList.js.map +1 -1
  176. package/lib/module/components/styled/SendInput.js +4 -2
  177. package/lib/module/components/styled/SendInput.js.map +1 -1
  178. package/lib/module/e2ee/crypto.js +170 -0
  179. package/lib/module/e2ee/crypto.js.map +1 -0
  180. package/lib/module/e2ee/deviceStore.js +77 -0
  181. package/lib/module/e2ee/deviceStore.js.map +1 -0
  182. package/lib/module/e2ee/fileCipher.js +90 -0
  183. package/lib/module/e2ee/fileCipher.js.map +1 -0
  184. package/lib/module/e2ee/fileEnvelope.js +66 -0
  185. package/lib/module/e2ee/fileEnvelope.js.map +1 -0
  186. package/lib/module/e2ee/index.js +185 -0
  187. package/lib/module/e2ee/index.js.map +1 -0
  188. package/lib/module/e2ee/limits.js +5 -0
  189. package/lib/module/e2ee/limits.js.map +1 -0
  190. package/lib/module/e2ee/ns.js +4 -0
  191. package/lib/module/e2ee/ns.js.map +1 -0
  192. package/lib/module/e2ee/omemo.js +610 -0
  193. package/lib/module/e2ee/omemo.js.map +1 -0
  194. package/lib/module/e2ee/protobuf.js +140 -0
  195. package/lib/module/e2ee/protobuf.js.map +1 -0
  196. package/lib/module/e2ee/ratchet.js +228 -0
  197. package/lib/module/e2ee/ratchet.js.map +1 -0
  198. package/lib/module/e2ee/sealedBody.js +19 -0
  199. package/lib/module/e2ee/sealedBody.js.map +1 -0
  200. package/lib/module/e2ee/sealedFiles.js +154 -0
  201. package/lib/module/e2ee/sealedFiles.js.map +1 -0
  202. package/lib/module/e2ee/send.js +48 -0
  203. package/lib/module/e2ee/send.js.map +1 -0
  204. package/lib/module/e2ee/stanza.js +107 -0
  205. package/lib/module/e2ee/stanza.js.map +1 -0
  206. package/lib/module/e2ee/store.js +35 -0
  207. package/lib/module/e2ee/store.js.map +1 -0
  208. package/lib/module/helpers/accountName.js +18 -0
  209. package/lib/module/helpers/accountName.js.map +1 -0
  210. package/lib/module/helpers/createMessageFromXml.js +19 -4
  211. package/lib/module/helpers/createMessageFromXml.js.map +1 -1
  212. package/lib/module/helpers/createUserNameFromSetUser.js +3 -1
  213. package/lib/module/helpers/createUserNameFromSetUser.js.map +1 -1
  214. package/lib/module/helpers/displayName.js +11 -0
  215. package/lib/module/helpers/displayName.js.map +1 -0
  216. package/lib/module/helpers/persistCrypto.js +4 -0
  217. package/lib/module/helpers/persistCrypto.js.map +1 -1
  218. package/lib/module/helpers/secureKeyValue.js +19 -0
  219. package/lib/module/helpers/secureKeyValue.js.map +1 -1
  220. package/lib/module/helpers/videoCache.js +125 -0
  221. package/lib/module/helpers/videoCache.js.map +1 -0
  222. package/lib/module/hooks/useLogout.js +8 -0
  223. package/lib/module/hooks/useLogout.js.map +1 -1
  224. package/lib/module/hooks/useSendMessage.js +71 -7
  225. package/lib/module/hooks/useSendMessage.js.map +1 -1
  226. package/lib/module/i18n/strings.js +180 -0
  227. package/lib/module/i18n/strings.js.map +1 -1
  228. package/lib/module/main.js +1 -0
  229. package/lib/module/main.js.map +1 -1
  230. package/lib/module/networking/api-requests/auth.api.js +4 -1
  231. package/lib/module/networking/api-requests/auth.api.js.map +1 -1
  232. package/lib/module/networking/api-requests/rooms.api.js +109 -18
  233. package/lib/module/networking/api-requests/rooms.api.js.map +1 -1
  234. package/lib/module/networking/xmpp/handleStanzas.xmpp.js +38 -0
  235. package/lib/module/networking/xmpp/handleStanzas.xmpp.js.map +1 -1
  236. package/lib/module/networking/xmpp/sendMediaMessage.xmpp.js +16 -3
  237. package/lib/module/networking/xmpp/sendMediaMessage.xmpp.js.map +1 -1
  238. package/lib/module/networking/xmpp/sendTextMessage.xmpp.js +13 -6
  239. package/lib/module/networking/xmpp/sendTextMessage.xmpp.js.map +1 -1
  240. package/lib/module/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.js +14 -6
  241. package/lib/module/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.js.map +1 -1
  242. package/lib/module/networking/xmppClient.js +6 -0
  243. package/lib/module/networking/xmppClient.js.map +1 -1
  244. package/lib/module/roomStore/persistBackend.js +10 -13
  245. package/lib/module/roomStore/persistBackend.js.map +1 -1
  246. package/lib/module/roomStore/persistence.js +8 -1
  247. package/lib/module/roomStore/persistence.js.map +1 -1
  248. package/lib/module/version.js +1 -1
  249. package/lib/typescript/assets/icons.d.ts +8 -0
  250. package/lib/typescript/assets/icons.d.ts.map +1 -1
  251. package/lib/typescript/components/MainComponents/ChatHeader.d.ts.map +1 -1
  252. package/lib/typescript/components/MainComponents/ChatRoom.d.ts.map +1 -1
  253. package/lib/typescript/components/MainComponents/EncryptedSendNotice.d.ts +4 -0
  254. package/lib/typescript/components/MainComponents/EncryptedSendNotice.d.ts.map +1 -0
  255. package/lib/typescript/components/MainComponents/EncryptionCard.d.ts +10 -0
  256. package/lib/typescript/components/MainComponents/EncryptionCard.d.ts.map +1 -0
  257. package/lib/typescript/components/MainComponents/MediaMessage.d.ts.map +1 -1
  258. package/lib/typescript/components/MainComponents/ReduxWrapper.d.ts.map +1 -1
  259. package/lib/typescript/components/MainComponents/SealedAttachment.d.ts +13 -0
  260. package/lib/typescript/components/MainComponents/SealedAttachment.d.ts.map +1 -0
  261. package/lib/typescript/components/MessageBubble/BottomReplyContainer.d.ts.map +1 -1
  262. package/lib/typescript/components/MessageBubble/Message.d.ts.map +1 -1
  263. package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts.map +1 -1
  264. package/lib/typescript/components/Modals/ChatProfileModal/ChatProfileModal.d.ts.map +1 -1
  265. package/lib/typescript/components/Modals/FilePreviewModal/FilePreviewModal.d.ts.map +1 -1
  266. package/lib/typescript/components/Modals/FilePreviewModal/PdfView.d.ts.map +1 -1
  267. package/lib/typescript/components/Modals/UserProfileModal/UserProfileModal.d.ts.map +1 -1
  268. package/lib/typescript/components/Modals/UserSettingsModal/UserSettingsModal.d.ts.map +1 -1
  269. package/lib/typescript/components/RoomComponents/ChatRoomItem.d.ts.map +1 -1
  270. package/lib/typescript/components/RoomComponents/LastMessageItem.d.ts.map +1 -1
  271. package/lib/typescript/components/RoomComponents/styled/LastAudioMessage.d.ts.map +1 -1
  272. package/lib/typescript/components/RoomComponents/styled/LastMessageSealed.d.ts +7 -0
  273. package/lib/typescript/components/RoomComponents/styled/LastMessageSealed.d.ts.map +1 -0
  274. package/lib/typescript/components/Thread/ThreadWrapper.d.ts.map +1 -1
  275. package/lib/typescript/components/UsersList/UsersList.d.ts.map +1 -1
  276. package/lib/typescript/components/styled/SendInput.d.ts +1 -0
  277. package/lib/typescript/components/styled/SendInput.d.ts.map +1 -1
  278. package/lib/typescript/e2ee/crypto.d.ts +28 -0
  279. package/lib/typescript/e2ee/crypto.d.ts.map +1 -0
  280. package/lib/typescript/e2ee/deviceStore.d.ts +4 -0
  281. package/lib/typescript/e2ee/deviceStore.d.ts.map +1 -0
  282. package/lib/typescript/e2ee/fileCipher.d.ts +7 -0
  283. package/lib/typescript/e2ee/fileCipher.d.ts.map +1 -0
  284. package/lib/typescript/e2ee/fileEnvelope.d.ts +27 -0
  285. package/lib/typescript/e2ee/fileEnvelope.d.ts.map +1 -0
  286. package/lib/typescript/e2ee/index.d.ts +23 -0
  287. package/lib/typescript/e2ee/index.d.ts.map +1 -0
  288. package/lib/typescript/e2ee/limits.d.ts +3 -0
  289. package/lib/typescript/e2ee/limits.d.ts.map +1 -0
  290. package/lib/typescript/e2ee/ns.d.ts +2 -0
  291. package/lib/typescript/e2ee/ns.d.ts.map +1 -0
  292. package/lib/typescript/e2ee/omemo.d.ts +64 -0
  293. package/lib/typescript/e2ee/omemo.d.ts.map +1 -0
  294. package/lib/typescript/e2ee/protobuf.d.ts +25 -0
  295. package/lib/typescript/e2ee/protobuf.d.ts.map +1 -0
  296. package/lib/typescript/e2ee/ratchet.d.ts +39 -0
  297. package/lib/typescript/e2ee/ratchet.d.ts.map +1 -0
  298. package/lib/typescript/e2ee/sealedBody.d.ts +2 -0
  299. package/lib/typescript/e2ee/sealedBody.d.ts.map +1 -0
  300. package/lib/typescript/e2ee/sealedFiles.d.ts +31 -0
  301. package/lib/typescript/e2ee/sealedFiles.d.ts.map +1 -0
  302. package/lib/typescript/e2ee/send.d.ts +6 -0
  303. package/lib/typescript/e2ee/send.d.ts.map +1 -0
  304. package/lib/typescript/e2ee/stanza.d.ts +8 -0
  305. package/lib/typescript/e2ee/stanza.d.ts.map +1 -0
  306. package/lib/typescript/e2ee/store.d.ts +16 -0
  307. package/lib/typescript/e2ee/store.d.ts.map +1 -0
  308. package/lib/typescript/helpers/accountName.d.ts +7 -0
  309. package/lib/typescript/helpers/accountName.d.ts.map +1 -0
  310. package/lib/typescript/helpers/createMessageFromXml.d.ts.map +1 -1
  311. package/lib/typescript/helpers/createUserNameFromSetUser.d.ts.map +1 -1
  312. package/lib/typescript/helpers/displayName.d.ts +2 -0
  313. package/lib/typescript/helpers/displayName.d.ts.map +1 -0
  314. package/lib/typescript/helpers/persistCrypto.d.ts +1 -0
  315. package/lib/typescript/helpers/persistCrypto.d.ts.map +1 -1
  316. package/lib/typescript/helpers/secureKeyValue.d.ts +4 -0
  317. package/lib/typescript/helpers/secureKeyValue.d.ts.map +1 -1
  318. package/lib/typescript/helpers/videoCache.d.ts +19 -0
  319. package/lib/typescript/helpers/videoCache.d.ts.map +1 -0
  320. package/lib/typescript/hooks/useLogout.d.ts.map +1 -1
  321. package/lib/typescript/hooks/useSendMessage.d.ts.map +1 -1
  322. package/lib/typescript/i18n/strings.d.ts.map +1 -1
  323. package/lib/typescript/main.d.ts +2 -0
  324. package/lib/typescript/main.d.ts.map +1 -1
  325. package/lib/typescript/networking/api-requests/auth.api.d.ts +4 -1
  326. package/lib/typescript/networking/api-requests/auth.api.d.ts.map +1 -1
  327. package/lib/typescript/networking/api-requests/rooms.api.d.ts +22 -1
  328. package/lib/typescript/networking/api-requests/rooms.api.d.ts.map +1 -1
  329. package/lib/typescript/networking/xmpp/handleStanzas.xmpp.d.ts +1 -0
  330. package/lib/typescript/networking/xmpp/handleStanzas.xmpp.d.ts.map +1 -1
  331. package/lib/typescript/networking/xmpp/sendMediaMessage.xmpp.d.ts.map +1 -1
  332. package/lib/typescript/networking/xmpp/sendTextMessage.xmpp.d.ts.map +1 -1
  333. package/lib/typescript/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.d.ts.map +1 -1
  334. package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
  335. package/lib/typescript/roomStore/persistBackend.d.ts.map +1 -1
  336. package/lib/typescript/roomStore/persistence.d.ts.map +1 -1
  337. package/lib/typescript/types/models/message.model.d.ts +5 -0
  338. package/lib/typescript/types/models/message.model.d.ts.map +1 -1
  339. package/lib/typescript/types/models/room.model.d.ts +5 -0
  340. package/lib/typescript/types/models/room.model.d.ts.map +1 -1
  341. package/lib/typescript/types/types.d.ts +4 -0
  342. package/lib/typescript/types/types.d.ts.map +1 -1
  343. package/lib/typescript/version.d.ts +1 -1
  344. package/package.json +8 -4
  345. package/src/assets/icons.tsx +27 -0
  346. package/src/components/MainComponents/ChatHeader.tsx +30 -9
  347. package/src/components/MainComponents/ChatRoom.tsx +11 -1
  348. package/src/components/MainComponents/EncryptedSendNotice.tsx +35 -0
  349. package/src/components/MainComponents/EncryptionCard.tsx +287 -0
  350. package/src/components/MainComponents/MediaMessage.tsx +5 -0
  351. package/src/components/MainComponents/ReduxWrapper.tsx +5 -0
  352. package/src/components/MainComponents/SealedAttachment.tsx +251 -0
  353. package/src/components/MessageBubble/BottomReplyContainer.tsx +2 -1
  354. package/src/components/MessageBubble/Message.tsx +96 -6
  355. package/src/components/MessageBubble/MessageInteractions.tsx +2 -3
  356. package/src/components/Modals/ChatProfileModal/ChatProfileModal.tsx +7 -4
  357. package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +181 -35
  358. package/src/components/Modals/FilePreviewModal/PdfView.tsx +4 -0
  359. package/src/components/Modals/UserProfileModal/UserProfileModal.tsx +53 -8
  360. package/src/components/Modals/UserSettingsModal/UserSettingsModal.tsx +5 -0
  361. package/src/components/RoomComponents/ChatRoomItem.tsx +23 -11
  362. package/src/components/RoomComponents/LastMessageItem.tsx +5 -0
  363. package/src/components/RoomComponents/styled/LastAudioMessage.tsx +3 -1
  364. package/src/components/RoomComponents/styled/LastMessageFile.tsx +2 -2
  365. package/src/components/RoomComponents/styled/LastMessagePhoto.tsx +1 -1
  366. package/src/components/RoomComponents/styled/LastMessageSealed.tsx +31 -0
  367. package/src/components/RoomComponents/styled/LastMessageVideo.tsx +1 -1
  368. package/src/components/Thread/ThreadWrapper.tsx +13 -0
  369. package/src/components/UsersList/UsersList.tsx +2 -1
  370. package/src/components/styled/SendInput.tsx +5 -2
  371. package/src/e2ee/crypto.ts +194 -0
  372. package/src/e2ee/deviceStore.ts +80 -0
  373. package/src/e2ee/fileCipher.ts +86 -0
  374. package/src/e2ee/fileEnvelope.ts +115 -0
  375. package/src/e2ee/index.ts +192 -0
  376. package/src/e2ee/limits.ts +2 -0
  377. package/src/e2ee/ns.ts +1 -0
  378. package/src/e2ee/omemo.ts +802 -0
  379. package/src/e2ee/protobuf.ts +188 -0
  380. package/src/e2ee/ratchet.ts +271 -0
  381. package/src/e2ee/sealedBody.ts +17 -0
  382. package/src/e2ee/sealedFiles.ts +181 -0
  383. package/src/e2ee/send.ts +70 -0
  384. package/src/e2ee/stanza.ts +133 -0
  385. package/src/e2ee/store.ts +54 -0
  386. package/src/helpers/accountName.ts +15 -0
  387. package/src/helpers/createMessageFromXml.ts +31 -6
  388. package/src/helpers/createUserNameFromSetUser.ts +2 -1
  389. package/src/helpers/displayName.ts +9 -0
  390. package/src/helpers/persistCrypto.ts +5 -0
  391. package/src/helpers/secureKeyValue.ts +15 -0
  392. package/src/helpers/videoCache.ts +138 -0
  393. package/src/hooks/useLogout.tsx +9 -0
  394. package/src/hooks/useSendMessage.tsx +71 -5
  395. package/src/i18n/strings.ts +180 -0
  396. package/src/main.ts +2 -0
  397. package/src/networking/api-requests/auth.api.ts +9 -1
  398. package/src/networking/api-requests/rooms.api.ts +121 -18
  399. package/src/networking/xmpp/handleStanzas.xmpp.ts +49 -0
  400. package/src/networking/xmpp/sendMediaMessage.xmpp.ts +19 -1
  401. package/src/networking/xmpp/sendTextMessage.xmpp.ts +17 -10
  402. package/src/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.ts +23 -10
  403. package/src/networking/xmppClient.ts +6 -0
  404. package/src/roomStore/persistBackend.ts +8 -11
  405. package/src/roomStore/persistence.ts +15 -0
  406. package/src/types/models/message.model.ts +5 -0
  407. package/src/types/models/room.model.ts +5 -0
  408. package/src/types/types.ts +4 -0
  409. package/src/version.ts +1 -1
@@ -0,0 +1,70 @@
1
+ import type { Client } from '@xmpp/client';
2
+ import type { Element } from 'ltx';
3
+ import {
4
+ accountDomain,
5
+ isE2eeEnabled,
6
+ isE2eeRoom,
7
+ omemoReady,
8
+ resolveRecipients,
9
+ } from './index';
10
+
11
+ export function shouldEncrypt(roomJID: string): boolean {
12
+ return isE2eeEnabled() && isE2eeRoom(roomJID);
13
+ }
14
+
15
+ let queue: Promise<void> = Promise.resolve();
16
+
17
+ export function sendEncrypted(
18
+ client: Client,
19
+ roomJID: string,
20
+ id: string,
21
+ data: Element,
22
+ body: Element,
23
+ plain: Element
24
+ ): void {
25
+ queue = queue.then(async () => {
26
+ let stanza = plain;
27
+ try {
28
+ const crypto = await omemoReady();
29
+ if (!crypto) {throw new Error('omemo_not_ready');}
30
+ const recipients = await resolveRecipients(roomJID, accountDomain(client));
31
+ stanza = await crypto.encryptGroupMessage(roomJID, recipients, [body], id, [data]);
32
+ } catch (error) {
33
+ const reason = String((error as Error)?.message || error);
34
+ if (reason.startsWith('omemo_members_unknown')) {
35
+ console.warn(`OMEMO: not sending to ${roomJID} - ${reason}`);
36
+ return;
37
+ }
38
+ console.warn(`OMEMO: sending to ${roomJID} in clear - ${reason}`);
39
+ }
40
+ try {
41
+ await client.send(stanza);
42
+ } catch (error) {
43
+ console.error('An error occurred while sending message:', error);
44
+ }
45
+ });
46
+ }
47
+
48
+ export function sendSealedMedia(
49
+ client: Client,
50
+ roomJID: string,
51
+ id: string,
52
+ body: Element,
53
+ cleartext: Element[]
54
+ ): void {
55
+ queue = queue.then(async () => {
56
+ try {
57
+ const crypto = await omemoReady();
58
+ if (!crypto) {throw new Error('omemo_not_ready');}
59
+ const recipients = await resolveRecipients(roomJID, accountDomain(client));
60
+ const stanza = await crypto.encryptGroupMessage(roomJID, recipients, [body], id, cleartext);
61
+ await client.send(stanza);
62
+ } catch (error) {
63
+ console.error(
64
+ `OMEMO: refusing to send sealed attachment to ${roomJID} - ${String(
65
+ (error as Error)?.message || error
66
+ )}`
67
+ );
68
+ }
69
+ });
70
+ }
@@ -0,0 +1,133 @@
1
+ import { Element, parse } from 'ltx';
2
+ import { store } from '../roomStore';
3
+ import { translateKey } from '../i18n/strings';
4
+ import { NS_OMEMO } from './ns';
5
+ import { omemoReady, senderJidFromMuc } from './index';
6
+
7
+ export type DecryptOutcome =
8
+ | 'not-encrypted'
9
+ | 'decrypted'
10
+ | 'undecryptable'
11
+ | 'drop';
12
+
13
+ export type E2eeError =
14
+ | 'unsupported'
15
+ | 'other-device'
16
+ | 'failed';
17
+
18
+ const READY_TIMEOUT_MS = 8000;
19
+
20
+ export function encryptedCarrier(
21
+ stanza: Element,
22
+ depth = 0
23
+ ): Element | undefined {
24
+ if (depth > 4 || !stanza?.getChild) {return undefined;}
25
+ if (stanza.name === 'message' && stanza.getChild('encrypted', NS_OMEMO)) {
26
+ return stanza;
27
+ }
28
+ for (const child of stanza.children ?? []) {
29
+ if (typeof child === 'string') {continue;}
30
+ const found = encryptedCarrier(child as Element, depth + 1);
31
+ if (found) {return found;}
32
+ }
33
+ return undefined;
34
+ }
35
+
36
+ const PLACEHOLDER_KEY: Record<E2eeError, string> = {
37
+ unsupported: 'e2ee.unsupportedMessage',
38
+ 'other-device': 'e2ee.otherDevice',
39
+ failed: 'e2ee.undecryptable',
40
+ };
41
+
42
+ export function placeholderKey(error?: string): string {
43
+ return PLACEHOLDER_KEY[error as E2eeError] ?? PLACEHOLDER_KEY.failed;
44
+ }
45
+
46
+ function placeholder(error: E2eeError): Element {
47
+ const settings = store.getState()?.chatSettingStore;
48
+ return new Element('body', {}).t(
49
+ translateKey(
50
+ placeholderKey(error),
51
+ settings?.config?.i18n?.locale || settings?.langSource,
52
+ settings?.config?.i18n?.strings
53
+ )
54
+ );
55
+ }
56
+
57
+ function mark(carrier: Element, error?: E2eeError): void {
58
+ let data = carrier.getChild('data');
59
+ if (!data) {
60
+ data = new Element('data', {});
61
+ carrier.append(data);
62
+ }
63
+ data.attrs.omemoEncrypted = 'true';
64
+ if (error) {
65
+ data.attrs.undecryptable = 'true';
66
+ data.attrs.e2eeError = error;
67
+ }
68
+ }
69
+
70
+ function strip(carrier: Element): void {
71
+ carrier.remove('encrypted', NS_OMEMO);
72
+ carrier.remove('encryption', 'urn:xmpp:eme:0');
73
+ carrier.remove('body');
74
+ }
75
+
76
+ function close(carrier: Element, error: E2eeError): DecryptOutcome {
77
+ strip(carrier);
78
+ carrier.append(placeholder(error));
79
+ mark(carrier, error);
80
+ return 'undecryptable';
81
+ }
82
+
83
+ export function rewriteEncryptedStanza(stanza: Element): DecryptOutcome {
84
+ const carrier = encryptedCarrier(stanza);
85
+ if (!carrier) {return 'not-encrypted';}
86
+ const encrypted = carrier.getChild('encrypted', NS_OMEMO);
87
+ if (!encrypted?.getChild('payload')) {return 'drop';}
88
+ return close(carrier, 'unsupported');
89
+ }
90
+
91
+ const waitForDevice = () => {
92
+ let timer: ReturnType<typeof setTimeout> | undefined;
93
+ const timeout = new Promise<undefined>((resolve) => {
94
+ timer = setTimeout(() => resolve(undefined), READY_TIMEOUT_MS);
95
+ });
96
+ return Promise.race([omemoReady(), timeout]).finally(() => {
97
+ if (timer) {clearTimeout(timer);}
98
+ });
99
+ };
100
+
101
+ export async function decryptStanzaInPlace(
102
+ stanza: Element,
103
+ domain: string
104
+ ): Promise<DecryptOutcome> {
105
+ const carrier = encryptedCarrier(stanza);
106
+ if (!carrier) {return 'not-encrypted';}
107
+
108
+ const encrypted = carrier.getChild('encrypted', NS_OMEMO);
109
+ if (!encrypted?.getChild('payload')) {return 'drop';}
110
+
111
+ const from = String(carrier.attrs?.from || '');
112
+ const roomJid = from.split('/')[0]!;
113
+ const sender = senderJidFromMuc(from, domain);
114
+ const crypto = await waitForDevice();
115
+ const decrypted = sender
116
+ ? await crypto?.decrypt(carrier, sender, roomJid)
117
+ : undefined;
118
+
119
+ if (!decrypted?.content) {
120
+ return close(
121
+ carrier,
122
+ decrypted?.error === 'omemo_not_encrypted_for_this_device'
123
+ ? 'other-device'
124
+ : 'failed'
125
+ );
126
+ }
127
+
128
+ strip(carrier);
129
+ const wrapper = parse(`<omemo-content>${decrypted.content}</omemo-content>`);
130
+ carrier.append(...wrapper.children);
131
+ mark(carrier);
132
+ return 'decrypted';
133
+ }
@@ -0,0 +1,54 @@
1
+ import { fromBase64, toBase64 } from './crypto';
2
+
3
+ export interface OmemoStore {
4
+ get<T>(key: string): Promise<T | undefined>;
5
+ set(key: string, value: unknown): Promise<void>;
6
+ delete(key: string): Promise<void>;
7
+ }
8
+
9
+ const BYTES_TAG = '$bytes';
10
+
11
+ export function encodeValue(value: unknown): string {
12
+ return JSON.stringify(value, (_key, item) =>
13
+ item instanceof Uint8Array ? { [BYTES_TAG]: toBase64(item) } : item
14
+ );
15
+ }
16
+
17
+ export function decodeValue<T>(text: string): T {
18
+ return JSON.parse(text, (_key, item) =>
19
+ item && typeof item === 'object' && typeof item[BYTES_TAG] === 'string'
20
+ ? fromBase64(item[BYTES_TAG])
21
+ : item
22
+ ) as T;
23
+ }
24
+
25
+ export interface StringBackend {
26
+ getItem(key: string): Promise<string | null>;
27
+ setItem(key: string, value: string): Promise<void>;
28
+ removeItem(key: string): Promise<void>;
29
+ }
30
+
31
+ export function createStore(backend: StringBackend, jid: string): OmemoStore {
32
+ const prefix = `${jid.toLowerCase()}|`;
33
+ return {
34
+ get: async <T>(key: string) => {
35
+ const raw = await backend.getItem(prefix + key);
36
+ return raw === null ? undefined : decodeValue<T>(raw);
37
+ },
38
+ set: (key, value) => backend.setItem(prefix + key, encodeValue(value)),
39
+ delete: (key) => backend.removeItem(prefix + key),
40
+ };
41
+ }
42
+
43
+ export function createMemoryBackend(): StringBackend {
44
+ const data = new Map<string, string>();
45
+ return {
46
+ getItem: async (key) => data.get(key) ?? null,
47
+ setItem: async (key, value) => void data.set(key, value),
48
+ removeItem: async (key) => void data.delete(key),
49
+ };
50
+ }
51
+
52
+ export function createMemoryStore(jid = 'memory'): OmemoStore {
53
+ return createStore(createMemoryBackend(), jid);
54
+ }
@@ -0,0 +1,15 @@
1
+ export const accountNameOf = (
2
+ user:
3
+ | { xmppUsername?: string; userJID?: string; id?: string; _id?: string }
4
+ | null
5
+ | undefined,
6
+ appId: string
7
+ ): string => {
8
+ for (const raw of [user?.xmppUsername, user?.userJID, user?.id, user?._id]) {
9
+ const value = String(raw || '').split('@')[0]!.trim();
10
+ if (!value) {continue;}
11
+ if (value.includes('_')) {return value;}
12
+ if (appId) {return `${appId}_${value}`;}
13
+ }
14
+ return '';
15
+ };
@@ -1,4 +1,7 @@
1
1
  import { IMessage } from '../types/types';
2
+ import { isE2eeRoom } from '../e2ee';
3
+ import { composeName } from './displayName';
4
+ import { parseSealedMediaBody } from '../e2ee/sealedBody';
2
5
 
3
6
  interface IMessageWithNewData extends IMessage {
4
7
  [x: string]: any;
@@ -86,13 +89,25 @@ export const createMessageFromXml = async (
86
89
  // Synthesize user.name from sender attrs (web reads usersSet from
87
90
  // redux in <Message>; we fix it once at the source so Avatar /
88
91
  // MessageHeader render the actual sender instead of "??").
89
- const firstName = (merged.senderFirstName || '').toString().trim();
90
- const lastName = (merged.senderLastName || '').toString().trim();
92
+ const firstName = (merged.senderFirstName || merged.firstName || '')
93
+ .toString()
94
+ .trim();
95
+ const lastName = (merged.senderLastName || merged.lastName || '')
96
+ .toString()
97
+ .trim();
91
98
  const fullName = (merged.fullName || '').toString().trim();
92
99
  const senderJID = (merged.senderJID || '').toString();
93
- const senderLocal = senderJID.includes('@')
94
- ? senderJID.split('@')[0]
95
- : senderJID;
100
+ const senderBare = senderJID.split('/')[0] || '';
101
+ const senderResource = senderJID.includes('/') ? senderJID.split('/')[1] || '' : '';
102
+ const senderIsOccupant =
103
+ !!senderResource &&
104
+ (senderBare === String(merged.roomJid || '') ||
105
+ /@conference\./i.test(senderBare));
106
+ const senderLocal = senderIsOccupant
107
+ ? senderResource
108
+ : senderBare.includes('@')
109
+ ? senderBare.split('@')[0]
110
+ : senderBare;
96
111
 
97
112
  // Bug #41 root cause: the resource of the MUC `from` (room@conference/
98
113
  // occupant-id) is the SAME field getDataFromXml uses to derive
@@ -112,7 +127,7 @@ export const createMessageFromXml = async (
112
127
  const xmppFrom = (merged.xmppFrom || '').toString();
113
128
  const fromResource = xmppFrom.includes('/') ? xmppFrom.split('/')[1] : '';
114
129
 
115
- const composed = [firstName, lastName].filter(Boolean).join(' ').trim();
130
+ const composed = composeName(firstName, lastName);
116
131
  const existingName = String(merged?.user?.name || '').trim();
117
132
  const resolvedName =
118
133
  existingName ||
@@ -122,8 +137,18 @@ export const createMessageFromXml = async (
122
137
  merged?.user?.id ||
123
138
  '';
124
139
 
140
+ const unencrypted =
141
+ merged.omemoEncrypted !== 'true' && isE2eeRoom(String(merged.roomJid || ''));
142
+
143
+ const sealedKeys =
144
+ merged.clientEncrypted === 'true'
145
+ ? parseSealedMediaBody(merged.body)
146
+ : undefined;
147
+
125
148
  const message: IMessage = {
126
149
  ...merged,
150
+ ...(unencrypted ? { unencrypted: true } : {}),
151
+ ...(sealedKeys ? { e2eeKeys: sealedKeys, body: 'media' } : {}),
127
152
  user: {
128
153
  ...(merged.user || {}),
129
154
  id: merged?.user?.id || fromResource || senderLocal,
@@ -1,4 +1,5 @@
1
1
  import { IMessage, RoomMember } from '../types/types';
2
+ import { composeName } from './displayName';
2
3
 
3
4
  /**
4
5
  * Cache-only lookup. Returns the literal "Deleted User" as a MISS sentinel
@@ -21,7 +22,7 @@ export const createUserNameFromSetUser = (
21
22
  // fall back to that before giving up and returning the raw id.
22
23
  const bareName = user.name?.trim() || '';
23
24
 
24
- return `${firstName} ${lastName}`.trim() || bareName || userId;
25
+ return composeName(firstName, lastName) || bareName || userId;
25
26
  };
26
27
 
27
28
  /**
@@ -0,0 +1,9 @@
1
+ export const composeName = (
2
+ first?: string | null,
3
+ last?: string | null
4
+ ): string => {
5
+ const f = String(first ?? '').trim();
6
+ const l = String(last ?? '').trim();
7
+ if (f && f.includes('@') && f.toLowerCase() === l.toLowerCase()) {return f;}
8
+ return `${f} ${l}`.trim();
9
+ };
@@ -69,6 +69,11 @@ const loadOrCreateKey = (): Promise<CryptoJS.lib.WordArray> => {
69
69
  return cachedKeyPromise;
70
70
  };
71
71
 
72
+ export async function isPersistKeyKept(): Promise<boolean> {
73
+ const key = await loadOrCreateKey();
74
+ return (await secureGet(CIPHER_KEY_STORE_KEY)) === CryptoJS.enc.Base64.stringify(key);
75
+ }
76
+
72
77
  /** Encrypt a JSON-serialisable payload for storage. Returns the string to persist. */
73
78
  export async function encryptForPersist(plaintext: string): Promise<string> {
74
79
  const key = await loadOrCreateKey();
@@ -74,6 +74,21 @@ export async function secureRemove(key: string): Promise<void> {
74
74
  await AsyncStorage.removeItem(FALLBACK_PREFIX + key).catch(() => undefined);
75
75
  }
76
76
 
77
+ export async function secureGetOrCreate(
78
+ key: string,
79
+ create: () => string
80
+ ): Promise<{ value: string; created: boolean } | null> {
81
+ const existing = await secureGet(key);
82
+ if (existing) {return { value: existing, created: false };}
83
+ const value = create();
84
+ await secureSet(key, value);
85
+ if ((await secureGet(key)) !== value) {
86
+ pushLog('warn', `secureStore: ${key} was not kept`);
87
+ return null;
88
+ }
89
+ return { value, created: true };
90
+ }
91
+
77
92
  /** Test seam only. */
78
93
  export const __resetSecureKeyValueWarningForTests = (): void => {
79
94
  warnedFallback = false;
@@ -0,0 +1,138 @@
1
+ import * as FileSystem from 'expo-file-system/legacy';
2
+ import { filenameFromUrl, getExtensionForMime } from './mimeToExtension';
3
+
4
+ // Containers the native players open from a local file by extension.
5
+ const VIDEO_EXT_RE = /\.(mp4|mov|m4v|webm|3gp|mkv|mpeg)$/i;
6
+
7
+ const hashString = (value: string) => {
8
+ let hash = 5381;
9
+ for (let i = 0; i < value.length; i++) {
10
+ hash = (hash * 33) ^ value.charCodeAt(i);
11
+ }
12
+ return (hash >>> 0).toString(36);
13
+ };
14
+
15
+ export const isRemoteUrl = (url?: string | null): boolean =>
16
+ /^https?:\/\//i.test(url || '');
17
+
18
+ const hostOf = (url: string): string =>
19
+ (/^https?:\/\/(?:[^@/]+@)?([^:/?#]+)/i.exec(url)?.[1] || '').toLowerCase();
20
+
21
+ // The query carries the short-lived file token; the file is the same.
22
+ const withoutQuery = (url: string): string => url.split('#')[0]!.split('?')[0]!;
23
+
24
+ /**
25
+ * Where a remote video is kept once downloaded. The extension matters:
26
+ * AVFoundation picks the demuxer of a local file by it.
27
+ */
28
+ export const getVideoCacheUri = (
29
+ url: string,
30
+ mimetype: string | undefined | null,
31
+ cacheDirectory: string
32
+ ): string => {
33
+ const fromUrl = VIDEO_EXT_RE.exec(filenameFromUrl(url))?.[0];
34
+ const fromMime = getExtensionForMime(mimetype);
35
+ const ext = (
36
+ fromUrl || (VIDEO_EXT_RE.test(fromMime) ? fromMime : '.mp4')
37
+ ).toLowerCase();
38
+ return `${cacheDirectory}ethora-video-${hashString(withoutQuery(url))}${ext}`;
39
+ };
40
+
41
+ /**
42
+ * Hosts that answered a streaming attempt with an error. AVPlayer refuses a
43
+ * server that replies 200 to a byte-range request ("server is not correctly
44
+ * configured"), so for those the next video skips the doomed stream and
45
+ * downloads straight away.
46
+ */
47
+ const hostsWithoutStreaming = new Set<string>();
48
+
49
+ export const markStreamingUnsupported = (url: string): void => {
50
+ const host = hostOf(url);
51
+ if (host) {hostsWithoutStreaming.add(host);}
52
+ };
53
+
54
+ export const isStreamingUnsupported = (url: string): boolean =>
55
+ hostsWithoutStreaming.has(hostOf(url));
56
+
57
+ export const findCachedVideo = async (
58
+ url: string,
59
+ mimetype?: string | null
60
+ ): Promise<string | null> => {
61
+ const cacheDirectory = FileSystem.cacheDirectory;
62
+ if (!cacheDirectory || !isRemoteUrl(url)) {return null;}
63
+ try {
64
+ const uri = getVideoCacheUri(url, mimetype, cacheDirectory);
65
+ const info = await FileSystem.getInfoAsync(uri);
66
+ return info.exists ? uri : null;
67
+ } catch {
68
+ return null;
69
+ }
70
+ };
71
+
72
+ type ProgressListener = (fraction: number) => void;
73
+
74
+ const inFlight = new Map<
75
+ string,
76
+ { promise: Promise<string>; listeners: Set<ProgressListener> }
77
+ >();
78
+
79
+ /**
80
+ * Downloads the video next to the app's other caches and resolves with the
81
+ * local uri. One download per file however many callers ask; `onProgress`
82
+ * gets 0..1 when the server reports a length.
83
+ */
84
+ export const downloadVideoToCache = (
85
+ url: string,
86
+ mimetype?: string | null,
87
+ onProgress?: ProgressListener
88
+ ): Promise<string> => {
89
+ const cacheDirectory = FileSystem.cacheDirectory;
90
+ if (!cacheDirectory) {
91
+ return Promise.reject(new Error('video_cache_unavailable'));
92
+ }
93
+ const finalUri = getVideoCacheUri(url, mimetype, cacheDirectory);
94
+
95
+ const running = inFlight.get(finalUri);
96
+ if (running) {
97
+ if (onProgress) {running.listeners.add(onProgress);}
98
+ return running.promise;
99
+ }
100
+
101
+ const listeners = new Set<ProgressListener>();
102
+ if (onProgress) {listeners.add(onProgress);}
103
+
104
+ const run = async (): Promise<string> => {
105
+ // Download under another name: an interrupted download must not be
106
+ // mistaken for the cached file next time.
107
+ const part = `${finalUri}.part`;
108
+ await FileSystem.deleteAsync(part, { idempotent: true }).catch(() => {});
109
+ const task = FileSystem.createDownloadResumable(url, part, {}, (p) => {
110
+ if (p.totalBytesExpectedToWrite > 0) {
111
+ const fraction = Math.min(
112
+ 1,
113
+ p.totalBytesWritten / p.totalBytesExpectedToWrite
114
+ );
115
+ listeners.forEach((listener) => listener(fraction));
116
+ }
117
+ });
118
+ const result = await task.downloadAsync();
119
+ if (!result || result.status < 200 || result.status >= 300) {
120
+ await FileSystem.deleteAsync(part, { idempotent: true }).catch(() => {});
121
+ throw new Error(`video_download_status_${result?.status ?? 'cancelled'}`);
122
+ }
123
+ await FileSystem.deleteAsync(finalUri, { idempotent: true }).catch(() => {});
124
+ await FileSystem.moveAsync({ from: part, to: finalUri });
125
+ return finalUri;
126
+ };
127
+
128
+ const promise = run().finally(() => {
129
+ inFlight.delete(finalUri);
130
+ });
131
+ inFlight.set(finalUri, { promise, listeners });
132
+ return promise;
133
+ };
134
+
135
+ export const __resetVideoCacheForTests = (): void => {
136
+ hostsWithoutStreaming.clear();
137
+ inFlight.clear();
138
+ };
@@ -12,6 +12,7 @@ import { clearRoomsRestCache } from '../networking/api-requests/rooms.api';
12
12
  import { clearPersistedState } from '../roomStore/persistence';
13
13
  import { secureUserStorage } from '../helpers/secureUserStorage';
14
14
  import { getFlushBoundaryTs } from '../helpers/getServerReadTimestamp';
15
+ import { stopOmemo } from '../e2ee';
15
16
 
16
17
  // AsyncStorage keys the library writes but that aren't cleared by any
17
18
  // slice reducer. Listed here so a single logout call wipes the full
@@ -109,6 +110,14 @@ const logoutService = {
109
110
  console.warn('logoutService: push reset failed', e);
110
111
  }
111
112
 
113
+ stopOmemo();
114
+ try {
115
+ const { clearOpenedFiles } =
116
+ require('../e2ee/sealedFiles') as typeof import('../e2ee/sealedFiles');
117
+ clearOpenedFiles();
118
+ } catch {
119
+ }
120
+
112
121
  // 3. REST: nuke the in-memory `/chats/my` cache so the next login
113
122
  // doesn't read user A's rooms while user B is bootstrapping.
114
123
  try {