@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
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useRef, useState } from 'react';
1
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
2
2
  import { withFileToken } from '../../../helpers/secureFileUrl';
3
3
  import styled from 'styled-components/native';
4
4
  import {
@@ -16,6 +16,7 @@ import { RootState } from '../../../roomStore';
16
16
  import { FullScreenImage } from '../../styled/StyledInputComponents/MediaComponents';
17
17
  import { setActiveFile } from '../../../roomStore/chatSettingsSlice';
18
18
  import {
19
+ ActivityIndicator,
19
20
  Alert,
20
21
  Text,
21
22
  View,
@@ -33,6 +34,13 @@ import PdfViewer from './PdfView';
33
34
  import DocumentViewer from './DocumentViewer';
34
35
  import AudioMessage from '../../styled/AudioMessage';
35
36
  import { isLikelyAudio } from '../../../helpers/mimeToExtension';
37
+ import {
38
+ downloadVideoToCache,
39
+ findCachedVideo,
40
+ isRemoteUrl,
41
+ isStreamingUnsupported,
42
+ markStreamingUnsupported,
43
+ } from '../../../helpers/videoCache';
36
44
  import {
37
45
  getDisplayFileName,
38
46
  getUniqueFileName,
@@ -52,6 +60,15 @@ const GVIEW_PREVIEWABLE = new Set<string>([
52
60
  'text/csv',
53
61
  'application/rtf',
54
62
  ]);
63
+
64
+ const isLocalFile = (url: string | undefined | null) =>
65
+ /^file:\/\//i.test(url || '');
66
+
67
+ const fetchToCache = async (url: string, cachePath: string) =>
68
+ isLocalFile(url)
69
+ ? { status: 200, uri: url }
70
+ : FileSystem.downloadAsync(withFileToken(url), cachePath);
71
+
55
72
  const isGviewPreviewable = (mime: string | undefined | null) => {
56
73
  if (!mime) {return false;}
57
74
  return GVIEW_PREVIEWABLE.has(mime.toLowerCase().split(';')[0]!.trim());
@@ -62,52 +79,163 @@ const isGviewPreviewable = (mime: string | undefined | null) => {
62
79
  // the area with the surrounding letterbox painted as the view's own
63
80
  // (transparent → theme) background, NOT black. `textureView` so the play
64
81
  // overlay composites on top and nothing is clipped on Android.
65
- const ModalVideo: React.FC<{ uri: string }> = ({ uri }) => {
82
+ //
83
+ // The clip is streamed when the server allows it. AVPlayer does not play
84
+ // from a server that ignores byte-range requests (it answers 200 instead of
85
+ // 206 and the player stops with "server is not correctly configured"), so
86
+ // a failed stream falls back to a downloaded copy in the cache — which is
87
+ // also what a reopened video plays from.
88
+ const ModalVideo: React.FC<{ uri: string; mimetype?: string }> = ({
89
+ uri,
90
+ mimetype,
91
+ }) => {
92
+ const theme = useTheme();
93
+ const remote = isRemoteUrl(uri);
94
+ // What the player is given; null until we know where to play from.
95
+ const [source, setSource] = useState<string | null>(remote ? null : uri);
66
96
  const [showPlay, setShowPlay] = useState(true);
97
+ // null = not downloading, otherwise 0..1 (0 while the size is unknown).
98
+ const [progress, setProgress] = useState<number | null>(null);
99
+ const [failed, setFailed] = useState(false);
100
+ const wantsPlay = useRef(false);
101
+ const downloadStarted = useRef(false);
102
+ const mounted = useRef(true);
103
+ useEffect(
104
+ () => () => {
105
+ mounted.current = false;
106
+ },
107
+ []
108
+ );
67
109
 
68
- const player = useVideoPlayer(uri, (p) => {
110
+ const download = useCallback(() => {
111
+ if (downloadStarted.current) {return;}
112
+ downloadStarted.current = true;
113
+ setProgress(0);
114
+ downloadVideoToCache(uri, mimetype, (fraction) => {
115
+ if (mounted.current) {setProgress(fraction);}
116
+ })
117
+ .then((localUri) => {
118
+ if (!mounted.current) {return;}
119
+ setProgress(null);
120
+ setSource(localUri);
121
+ })
122
+ .catch((err) => {
123
+ console.warn('video download failed', err);
124
+ if (!mounted.current) {return;}
125
+ setProgress(null);
126
+ setFailed(true);
127
+ });
128
+ }, [mimetype, uri]);
129
+
130
+ useEffect(() => {
131
+ if (!remote) {return;}
132
+ let active = true;
133
+ findCachedVideo(uri, mimetype).then((cached) => {
134
+ if (!active) {return;}
135
+ if (cached) {
136
+ setSource(cached);
137
+ } else if (isStreamingUnsupported(uri)) {
138
+ download();
139
+ } else {
140
+ setSource(uri);
141
+ }
142
+ });
143
+ return () => {
144
+ active = false;
145
+ };
146
+ }, [download, mimetype, remote, uri]);
147
+
148
+ const player = useVideoPlayer(source, (p) => {
69
149
  p.muted = false;
150
+ // A player made for the downloaded copy picks up a play the user
151
+ // already asked for.
152
+ if (wantsPlay.current) {p.play();}
70
153
  });
71
154
 
72
155
  useEffect(() => {
73
- const sub = player.addListener('playingChange', ({ isPlaying }) => {
156
+ const playing = player.addListener('playingChange', ({ isPlaying }) => {
74
157
  if (isPlaying) {setShowPlay(false);}
75
158
  });
76
- return () => sub.remove();
77
- }, [player]);
159
+ const status = player.addListener('statusChange', (event) => {
160
+ if (event.status !== 'error') {return;}
161
+ if (source === uri && remote) {
162
+ markStreamingUnsupported(uri);
163
+ download();
164
+ } else {
165
+ setFailed(true);
166
+ }
167
+ });
168
+ return () => {
169
+ playing.remove();
170
+ status.remove();
171
+ };
172
+ }, [download, player, remote, source, uri]);
78
173
 
79
174
  const handlePlay = () => {
175
+ wantsPlay.current = true;
80
176
  setShowPlay(false);
81
177
  player.play();
82
178
  };
83
179
 
180
+ if (failed) {
181
+ return (
182
+ <View style={styles.videoMessage}>
183
+ <Text style={{ fontSize: 16, fontWeight: '600', color: theme.text }}>
184
+ This video can't be played
185
+ </Text>
186
+ <Text style={{ color: theme.textSecondary, textAlign: 'center' }}>
187
+ Tap the save icon above to download it.
188
+ </Text>
189
+ </View>
190
+ );
191
+ }
192
+
193
+ const loading = progress !== null || source === null;
194
+
84
195
  return (
85
196
  <View style={{ flex: 1, width: '100%' }}>
86
- <VideoView
87
- player={player}
88
- style={{ flex: 1, backgroundColor: 'transparent' }}
89
- contentFit="contain"
90
- nativeControls
91
- surfaceType="textureView"
92
- // Fullscreen stays available by default on both expo-video lines:
93
- // `allowsFullscreen` (SDK 54, default true) was replaced by
94
- // `fullscreenOptions.enable` (SDK 57, default true). Passing neither
95
- // keeps this file compiling against both.
96
- />
97
- {/* Play affordance shown immediately on open; tapping starts
98
- playback and the native controls take over. */}
99
- {showPlay && (
100
- <TouchableOpacity
101
- activeOpacity={0.8}
102
- onPress={handlePlay}
103
- style={StyleSheet.absoluteFill}
104
- >
105
- <View style={styles.playOverlay}>
106
- <View style={styles.playButton}>
107
- <PlayIcon width={28} height={28} />
108
- </View>
197
+ {/* No surface while the copy downloads: the failed stream would
198
+ show the system's "can't play" glyph under the spinner. */}
199
+ {!loading && (
200
+ <VideoView
201
+ player={player}
202
+ style={{ flex: 1, backgroundColor: 'transparent' }}
203
+ contentFit="contain"
204
+ nativeControls
205
+ surfaceType="textureView"
206
+ // Fullscreen stays available by default on both expo-video lines:
207
+ // `allowsFullscreen` (SDK 54, default true) was replaced by
208
+ // `fullscreenOptions.enable` (SDK 57, default true). Passing
209
+ // neither keeps this file compiling against both.
210
+ />
211
+ )}
212
+ {loading ? (
213
+ <View style={styles.playOverlay} pointerEvents="none">
214
+ <View style={styles.playButton}>
215
+ <ActivityIndicator color="#FFFFFF" />
109
216
  </View>
110
- </TouchableOpacity>
217
+ {!!progress && (
218
+ <Text style={[styles.progress, { color: theme.textSecondary }]}>
219
+ {Math.round(progress * 100)}%
220
+ </Text>
221
+ )}
222
+ </View>
223
+ ) : (
224
+ showPlay && (
225
+ // Play affordance shown immediately on open; tapping starts
226
+ // playback and the native controls take over.
227
+ <TouchableOpacity
228
+ activeOpacity={0.8}
229
+ onPress={handlePlay}
230
+ style={StyleSheet.absoluteFill}
231
+ >
232
+ <View style={styles.playOverlay}>
233
+ <View style={styles.playButton}>
234
+ <PlayIcon width={28} height={28} />
235
+ </View>
236
+ </View>
237
+ </TouchableOpacity>
238
+ )
111
239
  )}
112
240
  </View>
113
241
  );
@@ -175,7 +303,7 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
175
303
 
176
304
  try {
177
305
  const filePath = FileSystem.cacheDirectory + displayFileName;
178
- const download = await FileSystem.downloadAsync(withFileToken(activeFile.fileURL), filePath);
306
+ const download = await fetchToCache(activeFile.fileURL, filePath);
179
307
 
180
308
  if (download.status === 200) {
181
309
  await getMediaLibrary()?.saveToLibraryAsync(download.uri);
@@ -222,7 +350,7 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
222
350
  const saveFileToDownloads = async () => {
223
351
  try {
224
352
  const filePath = FileSystem.cacheDirectory + displayFileName;
225
- const download = await FileSystem.downloadAsync(withFileToken(activeFile.fileURL), filePath);
353
+ const download = await fetchToCache(activeFile.fileURL, filePath);
226
354
 
227
355
  if (download.status !== 200) {
228
356
  Alert.alert('Error', 'Failed to save the file.');
@@ -332,7 +460,14 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
332
460
  />
333
461
  );
334
462
  case activeFile.mimetype.startsWith('video/'):
335
- return <ModalVideo uri={withFileToken(activeFile.fileURL)} />;
463
+ return (
464
+ <ModalVideo
465
+ // Remount per file: the player state belongs to one clip.
466
+ key={activeFile.fileURL}
467
+ uri={withFileToken(activeFile.fileURL)}
468
+ mimetype={activeFile.mimetype}
469
+ />
470
+ );
336
471
  case activeFile.mimetype.includes('application/octet-stream'):
337
472
  return (
338
473
  <View
@@ -394,13 +529,14 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
394
529
  />
395
530
  </View>
396
531
  );
397
- case activeFile.mimetype === 'application/pdf':
532
+ case activeFile.mimetype === 'application/pdf' &&
533
+ !(Platform.OS === 'android' && isLocalFile(activeFile.fileURL)):
398
534
  return <PdfViewer pdfUrl={withFileToken(activeFile.fileURL)} />;
399
535
  default: {
400
536
  // Office docs (.docx / .xlsx / .pptx / .doc / .xls / .ppt /
401
537
  // .txt / .csv / .rtf) → render inline via Google's gview embed
402
538
  // — fixes the "blank preview" complaint for docs (bug #9).
403
- if (isGviewPreviewable(activeFile.mimetype)) {
539
+ if (isGviewPreviewable(activeFile.mimetype) && !isLocalFile(activeFile.fileURL)) {
404
540
  return (
405
541
  <DocumentViewer
406
542
  url={activeFile.fileURL}
@@ -486,6 +622,16 @@ const styles = StyleSheet.create({
486
622
  justifyContent: 'center',
487
623
  alignItems: 'center',
488
624
  },
625
+ progress: {
626
+ marginTop: 10,
627
+ fontSize: 13,
628
+ fontWeight: '600',
629
+ },
630
+ videoMessage: {
631
+ alignItems: 'center',
632
+ gap: 8,
633
+ padding: 20,
634
+ },
489
635
  });
490
636
 
491
637
  export default FilePreviewModal;
@@ -25,6 +25,10 @@ const PdfViewer: React.FC<PdfViewerProps> = ({ pdfUrl }) => {
25
25
  let cancelled = false;
26
26
  const run = async () => {
27
27
  try {
28
+ if (/^file:\/\//i.test(pdfUrl)) {
29
+ setLocalUri(pdfUrl);
30
+ return;
31
+ }
28
32
  const target = FileSystem.cacheDirectory + `pdf-${Date.now()}.pdf`;
29
33
  const result = await FileSystem.downloadAsync(withFileToken(pdfUrl), target);
30
34
  if (cancelled) {return;}
@@ -17,6 +17,7 @@ import {
17
17
  EditIcon,
18
18
  IconDoc,
19
19
  LeaveIcon,
20
+ LockIcon,
20
21
  LogoutIcon,
21
22
  ProfileIcon,
22
23
  ShareIcon,
@@ -43,7 +44,21 @@ import {
43
44
  import { addRoomViaApi, setCurrentRoom } from '../../../roomStore/roomsSlice';
44
45
  import { runLogoutFlow } from '../../Menu/HeaderRoomListMenu';
45
46
  import { useLogout } from '../../../hooks/useLogout';
46
- import { ApiRoom, postPrivateRoom } from '../../../networking/api-requests/rooms.api';
47
+ import {
48
+ ApiRoom,
49
+ ownAppId,
50
+ postPrivateRoom,
51
+ } from '../../../networking/api-requests/rooms.api';
52
+ import EncryptionCard from '../../MainComponents/EncryptionCard';
53
+ import { accountNameOf } from '../../../helpers/accountName';
54
+ import { composeName } from '../../../helpers/displayName';
55
+ import { isUnresolvedSenderId } from '../../../helpers/isUnresolvedSenderId';
56
+
57
+ /** A name, unless it is only the account's id standing in for one. */
58
+ const nameOrNothing = (name?: string | null): string => {
59
+ const trimmed = String(name ?? '').trim();
60
+ return isUnresolvedSenderId(trimmed) ? '' : trimmed;
61
+ };
47
62
  import {
48
63
  getUserFiles,
49
64
  isMediaFile,
@@ -196,8 +211,8 @@ const UserProfileModal: React.FC<UserProfileModalProps> = ({
196
211
 
197
212
  const handleShare = useCallback(async () => {
198
213
  const name =
199
- profileUser?.name ||
200
- `${profileUser?.firstName ?? ''} ${profileUser?.lastName ?? ''}`.trim();
214
+ nameOrNothing(profileUser?.name) ||
215
+ composeName(profileUser?.firstName, profileUser?.lastName);
201
216
  const id =
202
217
  profileUser?.userJID ||
203
218
  profileUser?.defaultWallet?.walletAddress ||
@@ -238,7 +253,7 @@ const UserProfileModal: React.FC<UserProfileModalProps> = ({
238
253
  }
239
254
  };
240
255
 
241
- const handlePrivateMessage = useCallback(async () => {
256
+ const openPrivateRoom = useCallback(async (e2ee: boolean) => {
242
257
  showToast({
243
258
  id: Date.now().toString(),
244
259
  title: 'Room creation',
@@ -248,7 +263,8 @@ const UserProfileModal: React.FC<UserProfileModalProps> = ({
248
263
  });
249
264
  if (config?.newArch) {
250
265
  const newRoom = await postPrivateRoom(
251
- selectedUser?.userJID ?? (selectedUser?.id || '')
266
+ selectedUser?.userJID ?? (selectedUser?.id || ''),
267
+ e2ee
252
268
  );
253
269
  handleRoomCreation(newRoom, 2);
254
270
  } else {
@@ -283,6 +299,17 @@ const UserProfileModal: React.FC<UserProfileModalProps> = ({
283
299
  dispatch(setActiveModal(undefined));
284
300
  }, [selectedUser]); // eslint-disable-line react-hooks/exhaustive-deps
285
301
 
302
+ const handlePrivateMessage = useCallback(
303
+ () => openPrivateRoom(false),
304
+ [openPrivateRoom]
305
+ );
306
+ const handleEncryptedPrivateMessage = useCallback(
307
+ () => openPrivateRoom(true),
308
+ [openPrivateRoom]
309
+ );
310
+ const canStartEncrypted =
311
+ config?.e2ee?.enabled === true && !!config?.newArch;
312
+
286
313
  const openFile = (file: UserFile) => {
287
314
  dispatch(
288
315
  setActiveFile({
@@ -333,6 +360,16 @@ const UserProfileModal: React.FC<UserProfileModalProps> = ({
333
360
  icon: (color: string) => <ChatIcon color={color} />,
334
361
  onPress: handlePrivateMessage,
335
362
  },
363
+ ...(canStartEncrypted
364
+ ? [
365
+ {
366
+ key: 'encrypted-message',
367
+ label: t('action.encryptedMessage'),
368
+ icon: (color: string) => <LockIcon color={color} />,
369
+ onPress: handleEncryptedPrivateMessage,
370
+ },
371
+ ]
372
+ : []),
336
373
  ]),
337
374
  {
338
375
  key: 'share',
@@ -341,7 +378,7 @@ const UserProfileModal: React.FC<UserProfileModalProps> = ({
341
378
  onPress: handleShare,
342
379
  },
343
380
  ],
344
- [isOwnProfile, t, handleShare, handlePrivateMessage, handleLogout] // eslint-disable-line react-hooks/exhaustive-deps
381
+ [isOwnProfile, t, handleShare, handlePrivateMessage, handleEncryptedPrivateMessage, canStartEncrypted, handleLogout] // eslint-disable-line react-hooks/exhaustive-deps
345
382
  );
346
383
 
347
384
  // Collapsed, the hero's round buttons are gone — the "…" menu carries
@@ -373,8 +410,8 @@ const UserProfileModal: React.FC<UserProfileModalProps> = ({
373
410
  }
374
411
 
375
412
  const displayName =
376
- profileUser?.name ||
377
- `${profileUser?.firstName ?? ''} ${profileUser?.lastName ?? ''}`.trim() ||
413
+ nameOrNothing(profileUser?.name) ||
414
+ composeName(profileUser?.firstName, profileUser?.lastName) ||
378
415
  t('modal.profile.title');
379
416
  const heroImage =
380
417
  appendFileToken(profileUser?.profileImage, fileToken) || null;
@@ -529,6 +566,14 @@ const UserProfileModal: React.FC<UserProfileModalProps> = ({
529
566
  </Text>
530
567
  </View>
531
568
 
569
+ {!isOwnProfile && config?.e2ee?.enabled === true && (
570
+ <EncryptionCard
571
+ account={accountNameOf(profileUser, ownAppId())}
572
+ style={styles.card}
573
+ testID="user-profile-e2ee"
574
+ />
575
+ )}
576
+
532
577
  {tabs.length > 0 && (
533
578
  <View style={styles.card}>
534
579
  <View style={styles.tabsRow}>
@@ -1,5 +1,6 @@
1
1
  /** @format */
2
2
 
3
+ import EncryptionCard from '../../MainComponents/EncryptionCard';
3
4
  import React, { useCallback, useMemo } from 'react';
4
5
  import {
5
6
  StyleSheet,
@@ -159,6 +160,10 @@ const UserSettingsModal: React.FC<UserSettingsModalProps> = ({
159
160
  </View>
160
161
  ) : null}
161
162
 
163
+ {config?.e2ee?.enabled === true ? (
164
+ <EncryptionCard style={styles.card} testID="settings-e2ee" />
165
+ ) : null}
166
+
162
167
  {options.map((option) => (
163
168
  <TouchableOpacity
164
169
  key={option.key}
@@ -20,6 +20,10 @@ import LastMessageItem from './LastMessageItem';
20
20
  import { LastRoomMessageText } from './styled/StyledRoomComponents';
21
21
  import { useTheme } from '../../hooks/useTheme';
22
22
  import { resolveTheme } from '../../theme/theme';
23
+ import { LockIcon } from '../../assets/icons';
24
+ import { useT } from '../../i18n/useT';
25
+ import { composeName } from '../../helpers/displayName';
26
+ import { isUnresolvedSenderId } from '../../helpers/isUnresolvedSenderId';
23
27
 
24
28
  interface ChatRoomItemProps {
25
29
  chat: IRoom;
@@ -37,6 +41,7 @@ const ChatRoomItem: React.FC<ChatRoomItemProps> = ({
37
41
  // Rows can be rendered with an explicit `config` prop (tests, hosts
38
42
  // composing their own list); honour its colours over the store's.
39
43
  const storeTheme = useTheme();
44
+ const t = useT();
40
45
  const theme = useMemo(
41
46
  () => (config ? resolveTheme(config) : storeTheme),
42
47
  [config, storeTheme]
@@ -61,19 +66,12 @@ const ChatRoomItem: React.FC<ChatRoomItemProps> = ({
61
66
  const localId = rawUserId.split('@')[0];
62
67
  const entry = usersSet?.[localId] ?? usersSet?.[rawUserId];
63
68
  const fromUsersSet = entry
64
- ? `${entry.firstName ?? ''} ${entry.lastName ?? ''}`.trim()
69
+ ? composeName(entry.firstName, entry.lastName)
65
70
  : '';
66
71
 
67
- // usersSet first, exactly like Message.tsx: it's the live store, so
68
- // a renamed user updates here too. The message's own `name` is the
69
- // fallback that carries broadcast/system senders ("Ethora"), which
70
- // never appear in usersSet.
72
+ const ownName = String(message?.user?.name || '').trim();
71
73
  const safeName =
72
- fromUsersSet ||
73
- String(message?.user?.name || '').trim() ||
74
- localId ||
75
- rawUserId ||
76
- 'Unknown';
74
+ fromUsersSet || (isUnresolvedSenderId(ownName) ? '' : ownName);
77
75
 
78
76
  return {
79
77
  ...message,
@@ -176,7 +174,21 @@ const ChatRoomItem: React.FC<ChatRoomItemProps> = ({
176
174
  }}
177
175
  >
178
176
  <ChatInfo>
179
- <ChatName text={displayName} />
177
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 5 }}>
178
+ <View style={{ flexShrink: 1 }}>
179
+ <ChatName text={displayName} />
180
+ </View>
181
+ {chat?.e2ee && (
182
+ <View
183
+ accessible
184
+ accessibilityRole="image"
185
+ accessibilityLabel={t('e2ee.roomEncrypted')}
186
+ testID="room-e2ee-lock"
187
+ >
188
+ <LockIcon width={13} height={13} color={theme.textSecondary} />
189
+ </View>
190
+ )}
191
+ </View>
180
192
  </ChatInfo>
181
193
  {stamp ? (
182
194
  <UserCount
@@ -6,6 +6,7 @@ import LastMessagePhoto from './styled/LastMessagePhoto';
6
6
  import LastMessageEmoji from './styled/LastMessageEmoji';
7
7
  import LastMessageFile from './styled/LastMessageFile';
8
8
  import LastAudioMessage from './styled/LastAudioMessage';
9
+ import LastMessageSealed from './styled/LastMessageSealed';
9
10
 
10
11
  interface LastMessageItemProps {
11
12
  lastMessage: LastMessage;
@@ -14,6 +15,10 @@ interface LastMessageItemProps {
14
15
  const LastMessageItem: FC<LastMessageItemProps> = ({ lastMessage }) => {
15
16
  const { body, emoji, mimetype } = lastMessage;
16
17
 
18
+ if (lastMessage?.clientEncrypted === 'true') {
19
+ return <LastMessageSealed {...lastMessage} />;
20
+ }
21
+
17
22
  if (mimetype) {
18
23
  if (mimetype.startsWith('image/')) {
19
24
  return <LastMessagePhoto {...lastMessage} />;
@@ -33,7 +33,9 @@ const LastAudioMessage: FC<LastMessageEmojiProps> = ({ user, body }) => {
33
33
  const theme = useTheme();
34
34
  return (
35
35
  <LastRoomMessageContainer>
36
- <LastRoomMessageName numberOfLines={1}>{user?.name ?? ''}</LastRoomMessageName>
36
+ {!!user?.name && (
37
+ <LastRoomMessageName numberOfLines={1}>{user.name}</LastRoomMessageName>
38
+ )}
37
39
  <AudioRow>
38
40
  <PlayButton>
39
41
  <PlayIcon width={10} height={10} color={theme.dark ? theme.text : '#1F2937'} />
@@ -43,7 +43,7 @@ const LastMessageFile: FC<LastMessageFileProps> = ({
43
43
 
44
44
  return (
45
45
  <LastRoomMessageContainer>
46
- <LastRoomMessageName>{user?.name || ''}:</LastRoomMessageName>
46
+ {!!user?.name && <LastRoomMessageName>{user.name}:</LastRoomMessageName>}
47
47
  <View
48
48
  style={{
49
49
  display: 'flex',
@@ -67,7 +67,7 @@ const LastMessageFile: FC<LastMessageFileProps> = ({
67
67
  }}
68
68
  />
69
69
  ) : (
70
- <FileIcon style={{ width: '20px', height: '20px' }} />
70
+ <FileIcon width={20} height={20} />
71
71
  )}
72
72
  <LastRoomMessageText>{displayName}</LastRoomMessageText>
73
73
  </View>
@@ -47,7 +47,7 @@ const LastMessagePhoto: FC<LastMessagePhotoProps> = ({
47
47
  : 'file';
48
48
  return (
49
49
  <LastRoomMessageContainer>
50
- <LastRoomMessageName>{user?.name || ''}:</LastRoomMessageName>
50
+ {!!user?.name && <LastRoomMessageName>{user.name}:</LastRoomMessageName>}
51
51
  <View
52
52
  style={{
53
53
  display: 'flex',
@@ -0,0 +1,31 @@
1
+ import React, { FC } from 'react';
2
+ import { View } from 'react-native';
3
+ import { LastMessage } from '../../../types/types';
4
+ import {
5
+ LastRoomMessageContainer,
6
+ LastRoomMessageName,
7
+ LastRoomMessageText,
8
+ } from './StyledRoomComponents';
9
+ import { LockIcon } from '../../../assets/icons';
10
+ import { useTheme } from '../../../hooks/useTheme';
11
+ import { useT } from '../../../i18n/useT';
12
+
13
+ interface LastMessageSealedProps extends Pick<LastMessage, 'user'> {}
14
+
15
+ const LastMessageSealed: FC<LastMessageSealedProps> = ({ user }) => {
16
+ const t = useT();
17
+ const theme = useTheme();
18
+ const name = user?.name;
19
+
20
+ return (
21
+ <LastRoomMessageContainer>
22
+ {!!name && <LastRoomMessageName>{name}:</LastRoomMessageName>}
23
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
24
+ <LockIcon width={14} height={14} color={theme.textSecondary} />
25
+ <LastRoomMessageText>{t('media.sealedFile')}</LastRoomMessageText>
26
+ </View>
27
+ </LastRoomMessageContainer>
28
+ );
29
+ };
30
+
31
+ export default LastMessageSealed;
@@ -62,7 +62,7 @@ const LastMessageVideo: FC<LastMessageVideoProps> = ({
62
62
  : 'file';
63
63
  return (
64
64
  <LastRoomMessageContainer>
65
- <LastRoomMessageName>{user?.name || ''}:</LastRoomMessageName>
65
+ {!!user?.name && <LastRoomMessageName>{user.name}:</LastRoomMessageName>}
66
66
  <View
67
67
  style={{
68
68
  display: 'flex',
@@ -23,6 +23,8 @@ import MessageList from '../MainComponents/MessageList';
23
23
  import ModalHeaderComponent from '../Modals/ModalHeaderComponent';
24
24
  import { setCloseActiveMessage, setEditAction } from '../../roomStore/roomsSlice';
25
25
  import { EditWrapper } from '../MainComponents/EditWrapper';
26
+ import EncryptedSendNotice from '../MainComponents/EncryptedSendNotice';
27
+ import { MAX_SEAL_BYTES } from '../../e2ee/limits';
26
28
  import { useSendMessage } from '../../hooks/useSendMessage';
27
29
  import { createMainMessageForThread } from '../../helpers/createMainMessageForThread';
28
30
  import { useChatSettingState } from '../../hooks/useChatSettingState';
@@ -85,6 +87,10 @@ const ThreadWrapper: FC<ThreadWrapperProps> = ({
85
87
  // Same field the room header and the list show (`name` can be the id).
86
88
  const roomDisplayName = String(room?.title || room?.name || 'chat').trim();
87
89
 
90
+ const e2eeRoom = !!room?.e2ee;
91
+ const e2eeOn = config?.e2ee?.enabled === true;
92
+ const maxMediaBytes = e2eeRoom && e2eeOn ? MAX_SEAL_BYTES : undefined;
93
+
88
94
  const [isLoadingMore, setIsLoadingMore] = useState(false);
89
95
  const [isChecked, setIsChecked] = useState(false);
90
96
 
@@ -233,6 +239,10 @@ const ThreadWrapper: FC<ThreadWrapperProps> = ({
233
239
  DockTag === View ? { paddingBottom: dockPadding } : null,
234
240
  ]}
235
241
  >
242
+ {e2eeRoom && !e2eeOn && !editAction?.isEdit ? (
243
+ <EncryptedSendNotice />
244
+ ) : (
245
+ <>
236
246
  <Pressable
237
247
  testID="thread-also-send"
238
248
  accessibilityRole="checkbox"
@@ -274,11 +284,14 @@ const ThreadWrapper: FC<ThreadWrapperProps> = ({
274
284
  sendMedia={sendMedia}
275
285
  sendMessage={editAction?.isEdit ? sendEditMessage : sendMessage}
276
286
  config={config}
287
+ maxMediaBytes={maxMediaBytes}
277
288
  onFocus={sendStartComposing}
278
289
  onBlur={sendEndComposing}
279
290
  isLoading={!!room?.isLoading}
280
291
  isMessageProcessing={isLastMessageFromUserAndProcessing(roomJid)}
281
292
  />
293
+ </>
294
+ )}
282
295
  </DockTag>
283
296
  </KeyboardAvoidingView>
284
297