@ethora/chat-component-rn 26.10.5 → 26.10.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 (426) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +18 -0
  3. package/app.plugin.js +2 -0
  4. package/lib/commonjs/assets/icons.js +2 -2
  5. package/lib/commonjs/assets/icons.js.map +1 -1
  6. package/lib/commonjs/components/MainComponents/AlbumGrid.js +152 -0
  7. package/lib/commonjs/components/MainComponents/AlbumGrid.js.map +1 -0
  8. package/lib/commonjs/components/MainComponents/ChatRoom.js +2 -2
  9. package/lib/commonjs/components/MainComponents/ChatRoom.js.map +1 -1
  10. package/lib/commonjs/components/MainComponents/MediaMessage.js +199 -123
  11. package/lib/commonjs/components/MainComponents/MediaMessage.js.map +1 -1
  12. package/lib/commonjs/components/MainComponents/MediaUploadOverlay.js +109 -0
  13. package/lib/commonjs/components/MainComponents/MediaUploadOverlay.js.map +1 -0
  14. package/lib/commonjs/components/MessageBubble/MediaCaption.js +51 -0
  15. package/lib/commonjs/components/MessageBubble/MediaCaption.js.map +1 -0
  16. package/lib/commonjs/components/MessageBubble/Message.js +40 -1
  17. package/lib/commonjs/components/MessageBubble/Message.js.map +1 -1
  18. package/lib/commonjs/components/MessageBubble/MessageInteractions.js +2 -1
  19. package/lib/commonjs/components/MessageBubble/MessageInteractions.js.map +1 -1
  20. package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js +75 -0
  21. package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
  22. package/lib/commonjs/components/RoomComponents/LastMessageItem.js +20 -4
  23. package/lib/commonjs/components/RoomComponents/LastMessageItem.js.map +1 -1
  24. package/lib/commonjs/components/RoomComponents/styled/LastAudioMessage.js +3 -2
  25. package/lib/commonjs/components/RoomComponents/styled/LastAudioMessage.js.map +1 -1
  26. package/lib/commonjs/components/RoomComponents/styled/LastMessageFile.js +3 -2
  27. package/lib/commonjs/components/RoomComponents/styled/LastMessageFile.js.map +1 -1
  28. package/lib/commonjs/components/RoomComponents/styled/LastMessagePhoto.js +3 -2
  29. package/lib/commonjs/components/RoomComponents/styled/LastMessagePhoto.js.map +1 -1
  30. package/lib/commonjs/components/RoomComponents/styled/LastMessageVideo.js +3 -2
  31. package/lib/commonjs/components/RoomComponents/styled/LastMessageVideo.js.map +1 -1
  32. package/lib/commonjs/components/Thread/ThreadWrapper.js +3 -2
  33. package/lib/commonjs/components/Thread/ThreadWrapper.js.map +1 -1
  34. package/lib/commonjs/components/styled/MediaFilePreview.js +60 -47
  35. package/lib/commonjs/components/styled/MediaFilePreview.js.map +1 -1
  36. package/lib/commonjs/components/styled/MessageImage.js +16 -6
  37. package/lib/commonjs/components/styled/MessageImage.js.map +1 -1
  38. package/lib/commonjs/components/styled/SendInput.js +99 -68
  39. package/lib/commonjs/components/styled/SendInput.js.map +1 -1
  40. package/lib/commonjs/components/styled/VideoMessage.js +24 -10
  41. package/lib/commonjs/components/styled/VideoMessage.js.map +1 -1
  42. package/lib/commonjs/context/xmppProvider.js +46 -0
  43. package/lib/commonjs/context/xmppProvider.js.map +1 -1
  44. package/lib/commonjs/e2ee/fileEnvelope.js +6 -0
  45. package/lib/commonjs/e2ee/fileEnvelope.js.map +1 -1
  46. package/lib/commonjs/e2ee/sealedBody.js +32 -2
  47. package/lib/commonjs/e2ee/sealedBody.js.map +1 -1
  48. package/lib/commonjs/helpers/albumLayout.js +150 -0
  49. package/lib/commonjs/helpers/albumLayout.js.map +1 -0
  50. package/lib/commonjs/helpers/attachments.js +255 -0
  51. package/lib/commonjs/helpers/attachments.js.map +1 -0
  52. package/lib/commonjs/helpers/caption.js +214 -0
  53. package/lib/commonjs/helpers/caption.js.map +1 -0
  54. package/lib/commonjs/helpers/createMainMessageForThread.js +5 -1
  55. package/lib/commonjs/helpers/createMainMessageForThread.js.map +1 -1
  56. package/lib/commonjs/helpers/createMessageFromXml.js +19 -5
  57. package/lib/commonjs/helpers/createMessageFromXml.js.map +1 -1
  58. package/lib/commonjs/helpers/fileKind.js +40 -0
  59. package/lib/commonjs/helpers/fileKind.js.map +1 -0
  60. package/lib/commonjs/helpers/getDataFromXml.js +6 -1
  61. package/lib/commonjs/helpers/getDataFromXml.js.map +1 -1
  62. package/lib/commonjs/helpers/mapWithConcurrency.js +41 -0
  63. package/lib/commonjs/helpers/mapWithConcurrency.js.map +1 -0
  64. package/lib/commonjs/helpers/mediaLimits.js +33 -0
  65. package/lib/commonjs/helpers/mediaLimits.js.map +1 -0
  66. package/lib/commonjs/helpers/mediaSendRetry.js +64 -0
  67. package/lib/commonjs/helpers/mediaSendRetry.js.map +1 -0
  68. package/lib/commonjs/helpers/mediaWireData.js +62 -0
  69. package/lib/commonjs/helpers/mediaWireData.js.map +1 -0
  70. package/lib/commonjs/helpers/mentionSpans.js +19 -0
  71. package/lib/commonjs/helpers/mentionSpans.js.map +1 -0
  72. package/lib/commonjs/helpers/mimeToExtension.js +45 -0
  73. package/lib/commonjs/helpers/mimeToExtension.js.map +1 -1
  74. package/lib/commonjs/helpers/readReceipts.js +173 -0
  75. package/lib/commonjs/helpers/readReceipts.js.map +1 -0
  76. package/lib/commonjs/helpers/repairFilenameEncoding.js +79 -0
  77. package/lib/commonjs/helpers/repairFilenameEncoding.js.map +1 -0
  78. package/lib/commonjs/helpers/safeJson.js +18 -0
  79. package/lib/commonjs/helpers/safeJson.js.map +1 -0
  80. package/lib/commonjs/helpers/secureStoreRuntime.js +10 -1
  81. package/lib/commonjs/helpers/secureStoreRuntime.js.map +1 -1
  82. package/lib/commonjs/hooks/useSendMessage.js +384 -193
  83. package/lib/commonjs/hooks/useSendMessage.js.map +1 -1
  84. package/lib/commonjs/i18n/strings.js +96 -0
  85. package/lib/commonjs/i18n/strings.js.map +1 -1
  86. package/lib/commonjs/main.js +81 -0
  87. package/lib/commonjs/main.js.map +1 -1
  88. package/lib/commonjs/networking/api-requests/auth.api.js +62 -3
  89. package/lib/commonjs/networking/api-requests/auth.api.js.map +1 -1
  90. package/lib/commonjs/networking/api-requests/push.api.js +22 -7
  91. package/lib/commonjs/networking/api-requests/push.api.js.map +1 -1
  92. package/lib/commonjs/networking/stanzaHandlers.js +100 -3
  93. package/lib/commonjs/networking/stanzaHandlers.js.map +1 -1
  94. package/lib/commonjs/networking/xmpp/handleStanzas.xmpp.js +4 -2
  95. package/lib/commonjs/networking/xmpp/handleStanzas.xmpp.js.map +1 -1
  96. package/lib/commonjs/networking/xmpp/sendDisplayedMarker.xmpp.js +28 -0
  97. package/lib/commonjs/networking/xmpp/sendDisplayedMarker.xmpp.js.map +1 -0
  98. package/lib/commonjs/networking/xmpp/sendMediaMessage.xmpp.js +53 -2
  99. package/lib/commonjs/networking/xmpp/sendMediaMessage.xmpp.js.map +1 -1
  100. package/lib/commonjs/networking/xmpp/sendReceivedReceipt.xmpp.js +28 -0
  101. package/lib/commonjs/networking/xmpp/sendReceivedReceipt.xmpp.js.map +1 -0
  102. package/lib/commonjs/networking/xmppClient.js +64 -0
  103. package/lib/commonjs/networking/xmppClient.js.map +1 -1
  104. package/lib/commonjs/push/pushKeyStore.js +169 -0
  105. package/lib/commonjs/push/pushKeyStore.js.map +1 -0
  106. package/lib/commonjs/push/sealedBox.js +106 -0
  107. package/lib/commonjs/push/sealedBox.js.map +1 -0
  108. package/lib/commonjs/push/sealedPush.js +124 -0
  109. package/lib/commonjs/push/sealedPush.js.map +1 -0
  110. package/lib/commonjs/push/sealedPushTask.js +195 -0
  111. package/lib/commonjs/push/sealedPushTask.js.map +1 -0
  112. package/lib/commonjs/roomStore/persistence.js +78 -5
  113. package/lib/commonjs/roomStore/persistence.js.map +1 -1
  114. package/lib/commonjs/roomStore/roomHeapSlice.js +4 -0
  115. package/lib/commonjs/roomStore/roomHeapSlice.js.map +1 -1
  116. package/lib/commonjs/roomStore/roomsSlice.js +114 -1
  117. package/lib/commonjs/roomStore/roomsSlice.js.map +1 -1
  118. package/lib/commonjs/services/pushRegistration.js +12 -7
  119. package/lib/commonjs/services/pushRegistration.js.map +1 -1
  120. package/lib/commonjs/types/models/attachment.model.js +2 -0
  121. package/lib/commonjs/types/models/attachment.model.js.map +1 -0
  122. package/lib/commonjs/version.js +1 -1
  123. package/lib/module/assets/icons.js +1 -1
  124. package/lib/module/assets/icons.js.map +1 -1
  125. package/lib/module/components/MainComponents/AlbumGrid.js +146 -0
  126. package/lib/module/components/MainComponents/AlbumGrid.js.map +1 -0
  127. package/lib/module/components/MainComponents/ChatRoom.js +2 -2
  128. package/lib/module/components/MainComponents/ChatRoom.js.map +1 -1
  129. package/lib/module/components/MainComponents/MediaMessage.js +200 -124
  130. package/lib/module/components/MainComponents/MediaMessage.js.map +1 -1
  131. package/lib/module/components/MainComponents/MediaUploadOverlay.js +104 -0
  132. package/lib/module/components/MainComponents/MediaUploadOverlay.js.map +1 -0
  133. package/lib/module/components/MessageBubble/MediaCaption.js +46 -0
  134. package/lib/module/components/MessageBubble/MediaCaption.js.map +1 -0
  135. package/lib/module/components/MessageBubble/Message.js +44 -5
  136. package/lib/module/components/MessageBubble/Message.js.map +1 -1
  137. package/lib/module/components/MessageBubble/MessageInteractions.js +2 -1
  138. package/lib/module/components/MessageBubble/MessageInteractions.js.map +1 -1
  139. package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js +75 -0
  140. package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
  141. package/lib/module/components/RoomComponents/LastMessageItem.js +20 -4
  142. package/lib/module/components/RoomComponents/LastMessageItem.js.map +1 -1
  143. package/lib/module/components/RoomComponents/styled/LastAudioMessage.js +3 -2
  144. package/lib/module/components/RoomComponents/styled/LastAudioMessage.js.map +1 -1
  145. package/lib/module/components/RoomComponents/styled/LastMessageFile.js +3 -2
  146. package/lib/module/components/RoomComponents/styled/LastMessageFile.js.map +1 -1
  147. package/lib/module/components/RoomComponents/styled/LastMessagePhoto.js +3 -2
  148. package/lib/module/components/RoomComponents/styled/LastMessagePhoto.js.map +1 -1
  149. package/lib/module/components/RoomComponents/styled/LastMessageVideo.js +3 -2
  150. package/lib/module/components/RoomComponents/styled/LastMessageVideo.js.map +1 -1
  151. package/lib/module/components/Thread/ThreadWrapper.js +3 -2
  152. package/lib/module/components/Thread/ThreadWrapper.js.map +1 -1
  153. package/lib/module/components/styled/MediaFilePreview.js +61 -48
  154. package/lib/module/components/styled/MediaFilePreview.js.map +1 -1
  155. package/lib/module/components/styled/MessageImage.js +16 -6
  156. package/lib/module/components/styled/MessageImage.js.map +1 -1
  157. package/lib/module/components/styled/SendInput.js +99 -68
  158. package/lib/module/components/styled/SendInput.js.map +1 -1
  159. package/lib/module/components/styled/VideoMessage.js +24 -10
  160. package/lib/module/components/styled/VideoMessage.js.map +1 -1
  161. package/lib/module/context/xmppProvider.js +46 -0
  162. package/lib/module/context/xmppProvider.js.map +1 -1
  163. package/lib/module/e2ee/fileEnvelope.js +1 -1
  164. package/lib/module/e2ee/fileEnvelope.js.map +1 -1
  165. package/lib/module/e2ee/sealedBody.js +32 -2
  166. package/lib/module/e2ee/sealedBody.js.map +1 -1
  167. package/lib/module/helpers/albumLayout.js +143 -0
  168. package/lib/module/helpers/albumLayout.js.map +1 -0
  169. package/lib/module/helpers/attachments.js +245 -0
  170. package/lib/module/helpers/attachments.js.map +1 -0
  171. package/lib/module/helpers/caption.js +207 -0
  172. package/lib/module/helpers/caption.js.map +1 -0
  173. package/lib/module/helpers/createMainMessageForThread.js +5 -1
  174. package/lib/module/helpers/createMainMessageForThread.js.map +1 -1
  175. package/lib/module/helpers/createMessageFromXml.js +20 -6
  176. package/lib/module/helpers/createMessageFromXml.js.map +1 -1
  177. package/lib/module/helpers/fileKind.js +34 -0
  178. package/lib/module/helpers/fileKind.js.map +1 -0
  179. package/lib/module/helpers/getDataFromXml.js +6 -1
  180. package/lib/module/helpers/getDataFromXml.js.map +1 -1
  181. package/lib/module/helpers/mapWithConcurrency.js +37 -0
  182. package/lib/module/helpers/mapWithConcurrency.js.map +1 -0
  183. package/lib/module/helpers/mediaLimits.js +27 -0
  184. package/lib/module/helpers/mediaLimits.js.map +1 -0
  185. package/lib/module/helpers/mediaSendRetry.js +55 -0
  186. package/lib/module/helpers/mediaSendRetry.js.map +1 -0
  187. package/lib/module/helpers/mediaWireData.js +58 -0
  188. package/lib/module/helpers/mediaWireData.js.map +1 -0
  189. package/lib/module/helpers/mentionSpans.js +14 -0
  190. package/lib/module/helpers/mentionSpans.js.map +1 -0
  191. package/lib/module/helpers/mimeToExtension.js +43 -0
  192. package/lib/module/helpers/mimeToExtension.js.map +1 -1
  193. package/lib/module/helpers/readReceipts.js +160 -0
  194. package/lib/module/helpers/readReceipts.js.map +1 -0
  195. package/lib/module/helpers/repairFilenameEncoding.js +74 -0
  196. package/lib/module/helpers/repairFilenameEncoding.js.map +1 -0
  197. package/lib/module/helpers/safeJson.js +13 -0
  198. package/lib/module/helpers/safeJson.js.map +1 -0
  199. package/lib/module/helpers/secureStoreRuntime.js +10 -1
  200. package/lib/module/helpers/secureStoreRuntime.js.map +1 -1
  201. package/lib/module/hooks/useSendMessage.js +387 -195
  202. package/lib/module/hooks/useSendMessage.js.map +1 -1
  203. package/lib/module/i18n/strings.js +96 -0
  204. package/lib/module/i18n/strings.js.map +1 -1
  205. package/lib/module/main.js +6 -0
  206. package/lib/module/main.js.map +1 -1
  207. package/lib/module/networking/api-requests/auth.api.js +62 -3
  208. package/lib/module/networking/api-requests/auth.api.js.map +1 -1
  209. package/lib/module/networking/api-requests/push.api.js +21 -6
  210. package/lib/module/networking/api-requests/push.api.js.map +1 -1
  211. package/lib/module/networking/stanzaHandlers.js +97 -4
  212. package/lib/module/networking/stanzaHandlers.js.map +1 -1
  213. package/lib/module/networking/xmpp/handleStanzas.xmpp.js +5 -3
  214. package/lib/module/networking/xmpp/handleStanzas.xmpp.js.map +1 -1
  215. package/lib/module/networking/xmpp/sendDisplayedMarker.xmpp.js +25 -0
  216. package/lib/module/networking/xmpp/sendDisplayedMarker.xmpp.js.map +1 -0
  217. package/lib/module/networking/xmpp/sendMediaMessage.xmpp.js +53 -2
  218. package/lib/module/networking/xmpp/sendMediaMessage.xmpp.js.map +1 -1
  219. package/lib/module/networking/xmpp/sendReceivedReceipt.xmpp.js +25 -0
  220. package/lib/module/networking/xmpp/sendReceivedReceipt.xmpp.js.map +1 -0
  221. package/lib/module/networking/xmppClient.js +64 -0
  222. package/lib/module/networking/xmppClient.js.map +1 -1
  223. package/lib/module/push/pushKeyStore.js +159 -0
  224. package/lib/module/push/pushKeyStore.js.map +1 -0
  225. package/lib/module/push/sealedBox.js +97 -0
  226. package/lib/module/push/sealedBox.js.map +1 -0
  227. package/lib/module/push/sealedPush.js +117 -0
  228. package/lib/module/push/sealedPush.js.map +1 -0
  229. package/lib/module/push/sealedPushTask.js +187 -0
  230. package/lib/module/push/sealedPushTask.js.map +1 -0
  231. package/lib/module/roomStore/persistence.js +78 -5
  232. package/lib/module/roomStore/persistence.js.map +1 -1
  233. package/lib/module/roomStore/roomHeapSlice.js +4 -0
  234. package/lib/module/roomStore/roomHeapSlice.js.map +1 -1
  235. package/lib/module/roomStore/roomsSlice.js +109 -0
  236. package/lib/module/roomStore/roomsSlice.js.map +1 -1
  237. package/lib/module/services/pushRegistration.js +12 -7
  238. package/lib/module/services/pushRegistration.js.map +1 -1
  239. package/lib/module/types/models/attachment.model.js +2 -0
  240. package/lib/module/types/models/attachment.model.js.map +1 -0
  241. package/lib/module/version.js +1 -1
  242. package/lib/typescript/assets/icons.d.ts +6 -1
  243. package/lib/typescript/assets/icons.d.ts.map +1 -1
  244. package/lib/typescript/components/MainComponents/AlbumGrid.d.ts +21 -0
  245. package/lib/typescript/components/MainComponents/AlbumGrid.d.ts.map +1 -0
  246. package/lib/typescript/components/MainComponents/ChatRoom.d.ts.map +1 -1
  247. package/lib/typescript/components/MainComponents/MediaMessage.d.ts.map +1 -1
  248. package/lib/typescript/components/MainComponents/MediaUploadOverlay.d.ts +17 -0
  249. package/lib/typescript/components/MainComponents/MediaUploadOverlay.d.ts.map +1 -0
  250. package/lib/typescript/components/MessageBubble/MediaCaption.d.ts +12 -0
  251. package/lib/typescript/components/MessageBubble/MediaCaption.d.ts.map +1 -0
  252. package/lib/typescript/components/MessageBubble/Message.d.ts.map +1 -1
  253. package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts.map +1 -1
  254. package/lib/typescript/components/Modals/FilePreviewModal/FilePreviewModal.d.ts.map +1 -1
  255. package/lib/typescript/components/RoomComponents/LastMessageItem.d.ts.map +1 -1
  256. package/lib/typescript/components/RoomComponents/styled/LastAudioMessage.d.ts +2 -0
  257. package/lib/typescript/components/RoomComponents/styled/LastAudioMessage.d.ts.map +1 -1
  258. package/lib/typescript/components/RoomComponents/styled/LastMessageFile.d.ts +2 -0
  259. package/lib/typescript/components/RoomComponents/styled/LastMessageFile.d.ts.map +1 -1
  260. package/lib/typescript/components/RoomComponents/styled/LastMessagePhoto.d.ts +2 -0
  261. package/lib/typescript/components/RoomComponents/styled/LastMessagePhoto.d.ts.map +1 -1
  262. package/lib/typescript/components/RoomComponents/styled/LastMessageVideo.d.ts +2 -0
  263. package/lib/typescript/components/RoomComponents/styled/LastMessageVideo.d.ts.map +1 -1
  264. package/lib/typescript/components/Thread/ThreadWrapper.d.ts.map +1 -1
  265. package/lib/typescript/components/styled/MediaFilePreview.d.ts.map +1 -1
  266. package/lib/typescript/components/styled/MessageImage.d.ts +10 -0
  267. package/lib/typescript/components/styled/MessageImage.d.ts.map +1 -1
  268. package/lib/typescript/components/styled/SendInput.d.ts +7 -1
  269. package/lib/typescript/components/styled/SendInput.d.ts.map +1 -1
  270. package/lib/typescript/components/styled/VideoMessage.d.ts +7 -0
  271. package/lib/typescript/components/styled/VideoMessage.d.ts.map +1 -1
  272. package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
  273. package/lib/typescript/e2ee/fileEnvelope.d.ts +1 -1
  274. package/lib/typescript/e2ee/fileEnvelope.d.ts.map +1 -1
  275. package/lib/typescript/e2ee/sealedBody.d.ts +16 -0
  276. package/lib/typescript/e2ee/sealedBody.d.ts.map +1 -1
  277. package/lib/typescript/helpers/albumLayout.d.ts +66 -0
  278. package/lib/typescript/helpers/albumLayout.d.ts.map +1 -0
  279. package/lib/typescript/helpers/attachments.d.ts +55 -0
  280. package/lib/typescript/helpers/attachments.d.ts.map +1 -0
  281. package/lib/typescript/helpers/caption.d.ts +64 -0
  282. package/lib/typescript/helpers/caption.d.ts.map +1 -0
  283. package/lib/typescript/helpers/createMainMessageForThread.d.ts.map +1 -1
  284. package/lib/typescript/helpers/createMessageFromXml.d.ts.map +1 -1
  285. package/lib/typescript/helpers/fileKind.d.ts +20 -0
  286. package/lib/typescript/helpers/fileKind.d.ts.map +1 -0
  287. package/lib/typescript/helpers/getDataFromXml.d.ts.map +1 -1
  288. package/lib/typescript/helpers/mapWithConcurrency.d.ts +9 -0
  289. package/lib/typescript/helpers/mapWithConcurrency.d.ts.map +1 -0
  290. package/lib/typescript/helpers/mediaLimits.d.ts +13 -0
  291. package/lib/typescript/helpers/mediaLimits.d.ts.map +1 -0
  292. package/lib/typescript/helpers/mediaSendRetry.d.ts +26 -0
  293. package/lib/typescript/helpers/mediaSendRetry.d.ts.map +1 -0
  294. package/lib/typescript/helpers/mediaWireData.d.ts +16 -0
  295. package/lib/typescript/helpers/mediaWireData.d.ts.map +1 -0
  296. package/lib/typescript/helpers/mentionSpans.d.ts +8 -0
  297. package/lib/typescript/helpers/mentionSpans.d.ts.map +1 -0
  298. package/lib/typescript/helpers/mimeToExtension.d.ts +7 -0
  299. package/lib/typescript/helpers/mimeToExtension.d.ts.map +1 -1
  300. package/lib/typescript/helpers/readReceipts.d.ts +82 -0
  301. package/lib/typescript/helpers/readReceipts.d.ts.map +1 -0
  302. package/lib/typescript/helpers/repairFilenameEncoding.d.ts +12 -0
  303. package/lib/typescript/helpers/repairFilenameEncoding.d.ts.map +1 -0
  304. package/lib/typescript/helpers/safeJson.d.ts +2 -0
  305. package/lib/typescript/helpers/safeJson.d.ts.map +1 -0
  306. package/lib/typescript/helpers/secureStoreRuntime.d.ts +17 -3
  307. package/lib/typescript/helpers/secureStoreRuntime.d.ts.map +1 -1
  308. package/lib/typescript/hooks/useSendMessage.d.ts +2 -1
  309. package/lib/typescript/hooks/useSendMessage.d.ts.map +1 -1
  310. package/lib/typescript/i18n/strings.d.ts.map +1 -1
  311. package/lib/typescript/main.d.ts +6 -0
  312. package/lib/typescript/main.d.ts.map +1 -1
  313. package/lib/typescript/networking/api-requests/auth.api.d.ts +2 -0
  314. package/lib/typescript/networking/api-requests/auth.api.d.ts.map +1 -1
  315. package/lib/typescript/networking/api-requests/push.api.d.ts +14 -2
  316. package/lib/typescript/networking/api-requests/push.api.d.ts.map +1 -1
  317. package/lib/typescript/networking/stanzaHandlers.d.ts +7 -3
  318. package/lib/typescript/networking/stanzaHandlers.d.ts.map +1 -1
  319. package/lib/typescript/networking/xmpp/handleStanzas.xmpp.d.ts.map +1 -1
  320. package/lib/typescript/networking/xmpp/sendDisplayedMarker.xmpp.d.ts +3 -0
  321. package/lib/typescript/networking/xmpp/sendDisplayedMarker.xmpp.d.ts.map +1 -0
  322. package/lib/typescript/networking/xmpp/sendMediaMessage.xmpp.d.ts +12 -0
  323. package/lib/typescript/networking/xmpp/sendMediaMessage.xmpp.d.ts.map +1 -1
  324. package/lib/typescript/networking/xmpp/sendReceivedReceipt.xmpp.d.ts +3 -0
  325. package/lib/typescript/networking/xmpp/sendReceivedReceipt.xmpp.d.ts.map +1 -0
  326. package/lib/typescript/networking/xmppClient.d.ts +15 -0
  327. package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
  328. package/lib/typescript/push/pushKeyStore.d.ts +35 -0
  329. package/lib/typescript/push/pushKeyStore.d.ts.map +1 -0
  330. package/lib/typescript/push/sealedBox.d.ts +24 -0
  331. package/lib/typescript/push/sealedBox.d.ts.map +1 -0
  332. package/lib/typescript/push/sealedPush.d.ts +61 -0
  333. package/lib/typescript/push/sealedPush.d.ts.map +1 -0
  334. package/lib/typescript/push/sealedPushTask.d.ts +33 -0
  335. package/lib/typescript/push/sealedPushTask.d.ts.map +1 -0
  336. package/lib/typescript/roomStore/persistence.d.ts.map +1 -1
  337. package/lib/typescript/roomStore/roomHeapSlice.d.ts +22 -1
  338. package/lib/typescript/roomStore/roomHeapSlice.d.ts.map +1 -1
  339. package/lib/typescript/roomStore/roomsSlice.d.ts +67 -3
  340. package/lib/typescript/roomStore/roomsSlice.d.ts.map +1 -1
  341. package/lib/typescript/services/pushRegistration.d.ts +8 -0
  342. package/lib/typescript/services/pushRegistration.d.ts.map +1 -1
  343. package/lib/typescript/types/models/attachment.model.d.ts +52 -0
  344. package/lib/typescript/types/models/attachment.model.d.ts.map +1 -0
  345. package/lib/typescript/types/models/message.model.d.ts +26 -0
  346. package/lib/typescript/types/models/message.model.d.ts.map +1 -1
  347. package/lib/typescript/types/models/modal.model.d.ts +7 -0
  348. package/lib/typescript/types/models/modal.model.d.ts.map +1 -1
  349. package/lib/typescript/types/models/room.model.d.ts +2 -0
  350. package/lib/typescript/types/models/room.model.d.ts.map +1 -1
  351. package/lib/typescript/types/types.d.ts +35 -1
  352. package/lib/typescript/types/types.d.ts.map +1 -1
  353. package/lib/typescript/version.d.ts +1 -1
  354. package/package.json +14 -1
  355. package/plugins/notification-service/Info.plist +31 -0
  356. package/plugins/notification-service/NotificationService.swift +195 -0
  357. package/plugins/notification-service/SealedBox.swift +355 -0
  358. package/plugins/notification-service/check/gen-vectors.mjs +24 -0
  359. package/plugins/notification-service/check/main.swift +37 -0
  360. package/plugins/notification-service/check/run.sh +12 -0
  361. package/plugins/notification-service/check/vectors.json +1 -0
  362. package/plugins/withNotificationServiceExtension.js +189 -0
  363. package/src/assets/icons.tsx +20 -2
  364. package/src/components/MainComponents/AlbumGrid.tsx +183 -0
  365. package/src/components/MainComponents/ChatRoom.tsx +4 -2
  366. package/src/components/MainComponents/MediaMessage.tsx +246 -130
  367. package/src/components/MainComponents/MediaUploadOverlay.tsx +102 -0
  368. package/src/components/MessageBubble/MediaCaption.tsx +45 -0
  369. package/src/components/MessageBubble/Message.tsx +71 -3
  370. package/src/components/MessageBubble/MessageInteractions.tsx +2 -1
  371. package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +64 -0
  372. package/src/components/RoomComponents/LastMessageItem.tsx +15 -4
  373. package/src/components/RoomComponents/styled/LastAudioMessage.tsx +6 -3
  374. package/src/components/RoomComponents/styled/LastMessageFile.tsx +6 -2
  375. package/src/components/RoomComponents/styled/LastMessagePhoto.tsx +6 -2
  376. package/src/components/RoomComponents/styled/LastMessageVideo.tsx +6 -2
  377. package/src/components/Thread/ThreadWrapper.tsx +5 -9
  378. package/src/components/styled/MediaFilePreview.tsx +49 -34
  379. package/src/components/styled/MessageImage.tsx +21 -5
  380. package/src/components/styled/SendInput.tsx +101 -87
  381. package/src/components/styled/VideoMessage.tsx +34 -9
  382. package/src/context/xmppProvider.tsx +32 -0
  383. package/src/e2ee/fileEnvelope.ts +1 -1
  384. package/src/e2ee/sealedBody.ts +44 -5
  385. package/src/helpers/albumLayout.ts +207 -0
  386. package/src/helpers/attachments.ts +316 -0
  387. package/src/helpers/caption.ts +283 -0
  388. package/src/helpers/createMainMessageForThread.ts +6 -1
  389. package/src/helpers/createMessageFromXml.ts +21 -5
  390. package/src/helpers/fileKind.ts +38 -0
  391. package/src/helpers/getDataFromXml.ts +5 -1
  392. package/src/helpers/mapWithConcurrency.ts +32 -0
  393. package/src/helpers/mediaLimits.ts +27 -0
  394. package/src/helpers/mediaSendRetry.ts +59 -0
  395. package/src/helpers/mediaWireData.ts +57 -0
  396. package/src/helpers/mentionSpans.ts +17 -0
  397. package/src/helpers/mimeToExtension.ts +51 -0
  398. package/src/helpers/readReceipts.ts +219 -0
  399. package/src/helpers/repairFilenameEncoding.ts +82 -0
  400. package/src/helpers/safeJson.ts +11 -0
  401. package/src/helpers/secureStoreRuntime.ts +21 -3
  402. package/src/hooks/useSendMessage.tsx +448 -249
  403. package/src/i18n/strings.ts +96 -0
  404. package/src/main.ts +30 -0
  405. package/src/networking/api-requests/auth.api.ts +60 -3
  406. package/src/networking/api-requests/push.api.ts +29 -4
  407. package/src/networking/stanzaHandlers.ts +91 -2
  408. package/src/networking/xmpp/handleStanzas.xmpp.ts +6 -2
  409. package/src/networking/xmpp/sendDisplayedMarker.xmpp.ts +31 -0
  410. package/src/networking/xmpp/sendMediaMessage.xmpp.ts +60 -3
  411. package/src/networking/xmpp/sendReceivedReceipt.xmpp.ts +31 -0
  412. package/src/networking/xmppClient.ts +48 -0
  413. package/src/push/pushKeyStore.ts +163 -0
  414. package/src/push/sealedBox.ts +104 -0
  415. package/src/push/sealedPush.ts +127 -0
  416. package/src/push/sealedPushTask.ts +175 -0
  417. package/src/roomStore/persistence.ts +94 -3
  418. package/src/roomStore/roomHeapSlice.ts +23 -1
  419. package/src/roomStore/roomsSlice.ts +112 -1
  420. package/src/services/pushRegistration.ts +25 -5
  421. package/src/types/models/attachment.model.ts +51 -0
  422. package/src/types/models/message.model.ts +29 -0
  423. package/src/types/models/modal.model.ts +7 -0
  424. package/src/types/models/room.model.ts +2 -0
  425. package/src/types/types.ts +41 -1
  426. package/src/version.ts +1 -1
@@ -0,0 +1,355 @@
1
+ import CryptoKit
2
+ import Foundation
3
+
4
+ /// libsodium's sealed box (`crypto_box_seal_open`) without libsodium: the
5
+ /// notification service extension cannot load the app's JavaScript, and a
6
+ /// pod for the extension is one more thing every host app would have to
7
+ /// carry (the Sodium pod also excludes arm64 simulators). X25519 comes from
8
+ /// CryptoKit; HSalsa20, XSalsa20, Poly1305 and BLAKE2b are ported from
9
+ /// tweetnacl and poly1305-donna / the BLAKE2 reference, public domain.
10
+ ///
11
+ /// Layout: ephemeral public key (32) || crypto_box(message, nonce,
12
+ /// recipient pk, ephemeral sk), with crypto_box = XSalsa20-Poly1305 under
13
+ /// HSalsa20(X25519 shared secret) and nonce = BLAKE2b-192(epk || rpk).
14
+ /// The SDK's `src/push/sealedBox.ts` is the same construction in JS; both
15
+ /// are pinned against libsodium (JS: `__tests__/sealedBox.test.ts`, Swift:
16
+ /// `plugins/notification-service/check/`).
17
+ enum SealedBox {
18
+ static let publicKeyBytes = 32
19
+ static let secretKeyBytes = 32
20
+ static let macBytes = 16
21
+ static let nonceBytes = 24
22
+ /// Ephemeral public key + Poly1305 tag.
23
+ static let overheadBytes = 48
24
+
25
+ /// The X25519 public key of a secret key (`crypto_scalarmult_base`).
26
+ static func publicKey(of secretKey: [UInt8]) -> [UInt8]? {
27
+ guard let key = try? Curve25519.KeyAgreement.PrivateKey(rawRepresentation: secretKey) else { return nil }
28
+ return [UInt8](key.publicKey.rawRepresentation)
29
+ }
30
+
31
+ /// `crypto_box_seal_open`. Nil for anything that does not open.
32
+ static func open(_ sealed: [UInt8], secretKey: [UInt8]) -> [UInt8]? {
33
+ guard sealed.count >= overheadBytes, secretKey.count == secretKeyBytes,
34
+ let recipientPublicKey = publicKey(of: secretKey)
35
+ else { return nil }
36
+ let ephemeralPublicKey = Array(sealed[0..<publicKeyBytes])
37
+ let box = Array(sealed[publicKeyBytes...])
38
+ guard let shared = x25519(secretKey: secretKey, publicKey: ephemeralPublicKey) else { return nil }
39
+ let key = Salsa.hsalsa20(key: shared, input: [UInt8](repeating: 0, count: 16))
40
+ let nonce = Blake2b.hash(ephemeralPublicKey + recipientPublicKey, outputLength: nonceBytes)
41
+ return secretboxOpen(box, nonce: nonce, key: key)
42
+ }
43
+
44
+ private static func x25519(secretKey: [UInt8], publicKey: [UInt8]) -> [UInt8]? {
45
+ guard let priv = try? Curve25519.KeyAgreement.PrivateKey(rawRepresentation: secretKey),
46
+ let pub = try? Curve25519.KeyAgreement.PublicKey(rawRepresentation: publicKey),
47
+ let shared = try? priv.sharedSecretFromKeyAgreement(with: pub)
48
+ else { return nil }
49
+ return shared.withUnsafeBytes { [UInt8]($0) }
50
+ }
51
+
52
+ /// `crypto_secretbox_open_easy`: tag (16) || ciphertext under XSalsa20-Poly1305.
53
+ static func secretboxOpen(_ box: [UInt8], nonce: [UInt8], key: [UInt8]) -> [UInt8]? {
54
+ guard box.count >= macBytes, nonce.count == nonceBytes, key.count == 32 else { return nil }
55
+ let tag = Array(box[0..<macBytes])
56
+ let ciphertext = Array(box[macBytes...])
57
+ // Keystream block 0: the first 32 bytes key Poly1305, the message starts at byte 32.
58
+ let stream = Salsa.xsalsa20(key: key, nonce: nonce, length: 32 + ciphertext.count)
59
+ let expected = Poly1305.mac(ciphertext, key: Array(stream[0..<32]))
60
+ guard constantTimeEqual(expected, tag) else { return nil }
61
+ var message = [UInt8](repeating: 0, count: ciphertext.count)
62
+ for i in 0..<ciphertext.count { message[i] = ciphertext[i] ^ stream[32 + i] }
63
+ return message
64
+ }
65
+
66
+ private static func constantTimeEqual(_ a: [UInt8], _ b: [UInt8]) -> Bool {
67
+ guard a.count == b.count else { return false }
68
+ var diff: UInt8 = 0
69
+ for i in 0..<a.count { diff |= a[i] ^ b[i] }
70
+ return diff == 0
71
+ }
72
+ }
73
+
74
+ // MARK: - Salsa20 / HSalsa20 / XSalsa20 (tweetnacl)
75
+
76
+ enum Salsa {
77
+ private static let sigma: [UInt32] = load32([UInt8]("expand 32-byte k".utf8))
78
+
79
+ private static func load32(_ bytes: [UInt8]) -> [UInt32] {
80
+ var words = [UInt32](repeating: 0, count: bytes.count / 4)
81
+ for i in 0..<words.count {
82
+ var w: UInt32 = 0
83
+ for j in 0..<4 { w |= UInt32(bytes[i * 4 + j]) << (8 * UInt32(j)) }
84
+ words[i] = w
85
+ }
86
+ return words
87
+ }
88
+
89
+ private static func store32(_ words: [UInt32]) -> [UInt8] {
90
+ var out = [UInt8](repeating: 0, count: words.count * 4)
91
+ for (i, w) in words.enumerated() {
92
+ out[i * 4] = UInt8(truncatingIfNeeded: w)
93
+ out[i * 4 + 1] = UInt8(truncatingIfNeeded: w >> 8)
94
+ out[i * 4 + 2] = UInt8(truncatingIfNeeded: w >> 16)
95
+ out[i * 4 + 3] = UInt8(truncatingIfNeeded: w >> 24)
96
+ }
97
+ return out
98
+ }
99
+
100
+ @inline(__always) private static func rotl(_ v: UInt32, _ n: UInt32) -> UInt32 {
101
+ (v << n) | (v >> (32 - n))
102
+ }
103
+
104
+ /// The Salsa20 state: sigma on the diagonal, key in two halves, 16-byte input in the middle row.
105
+ private static func state(key: [UInt8], input: [UInt8]) -> [UInt32] {
106
+ let k = load32(key)
107
+ let n = load32(input)
108
+ return [
109
+ sigma[0], k[0], k[1], k[2],
110
+ k[3], sigma[1], n[0], n[1],
111
+ n[2], n[3], sigma[2], k[4],
112
+ k[5], k[6], k[7], sigma[3],
113
+ ]
114
+ }
115
+
116
+ /// 20 rounds (10 double rounds) in place.
117
+ private static func rounds(_ x: inout [UInt32]) {
118
+ @inline(__always) func quarter(_ a: Int, _ b: Int, _ c: Int, _ d: Int) {
119
+ x[b] ^= rotl(x[a] &+ x[d], 7)
120
+ x[c] ^= rotl(x[b] &+ x[a], 9)
121
+ x[d] ^= rotl(x[c] &+ x[b], 13)
122
+ x[a] ^= rotl(x[d] &+ x[c], 18)
123
+ }
124
+ for _ in 0..<10 {
125
+ // column round
126
+ quarter(0, 4, 8, 12)
127
+ quarter(5, 9, 13, 1)
128
+ quarter(10, 14, 2, 6)
129
+ quarter(15, 3, 7, 11)
130
+ // row round
131
+ quarter(0, 1, 2, 3)
132
+ quarter(5, 6, 7, 4)
133
+ quarter(10, 11, 8, 9)
134
+ quarter(15, 12, 13, 14)
135
+ }
136
+ }
137
+
138
+ /// HSalsa20: 32-byte key, 16-byte input → 32-byte subkey (no feed-forward).
139
+ static func hsalsa20(key: [UInt8], input: [UInt8]) -> [UInt8] {
140
+ var x = state(key: key, input: input)
141
+ rounds(&x)
142
+ return store32([x[0], x[5], x[10], x[15], x[6], x[7], x[8], x[9]])
143
+ }
144
+
145
+ /// One 64-byte Salsa20 block for the 8-byte nonce and the block counter.
146
+ private static func block(key: [UInt8], nonce: [UInt8], counter: UInt64) -> [UInt8] {
147
+ var input = nonce
148
+ for i in 0..<8 { input.append(UInt8(truncatingIfNeeded: counter >> (8 * UInt64(i)))) }
149
+ let initial = state(key: key, input: input)
150
+ var x = initial
151
+ rounds(&x)
152
+ for i in 0..<16 { x[i] = x[i] &+ initial[i] }
153
+ return store32(x)
154
+ }
155
+
156
+ /// XSalsa20 keystream: HSalsa20 over the first 16 nonce bytes, Salsa20 over the last 8.
157
+ static func xsalsa20(key: [UInt8], nonce: [UInt8], length: Int) -> [UInt8] {
158
+ let subkey = hsalsa20(key: key, input: Array(nonce[0..<16]))
159
+ let nonce8 = Array(nonce[16..<24])
160
+ var out = [UInt8]()
161
+ out.reserveCapacity(length + 64)
162
+ var counter: UInt64 = 0
163
+ while out.count < length {
164
+ out += block(key: subkey, nonce: nonce8, counter: counter)
165
+ counter += 1
166
+ }
167
+ return Array(out[0..<length])
168
+ }
169
+ }
170
+
171
+ // MARK: - Poly1305 (poly1305-donna, 26-bit limbs)
172
+
173
+ enum Poly1305 {
174
+ private static func load32(_ m: [UInt8], _ i: Int) -> UInt32 {
175
+ var w: UInt32 = 0
176
+ for j in 0..<4 { w |= UInt32(m[i + j]) << (8 * UInt32(j)) }
177
+ return w
178
+ }
179
+
180
+ static func mac(_ message: [UInt8], key: [UInt8]) -> [UInt8] {
181
+ precondition(key.count == 32)
182
+ // r, clamped
183
+ let r0 = load32(key, 0) & 0x3ffffff
184
+ let r1 = (load32(key, 3) >> 2) & 0x3ffff03
185
+ let r2 = (load32(key, 6) >> 4) & 0x3ffc0ff
186
+ let r3 = (load32(key, 9) >> 6) & 0x3f03fff
187
+ let r4 = (load32(key, 12) >> 8) & 0x00fffff
188
+ let s1 = r1 &* 5, s2 = r2 &* 5, s3 = r3 &* 5, s4 = r4 &* 5
189
+ var h0: UInt32 = 0, h1: UInt32 = 0, h2: UInt32 = 0, h3: UInt32 = 0, h4: UInt32 = 0
190
+
191
+ func absorb(_ m: [UInt8], _ offset: Int, hibit: UInt32) {
192
+ h0 &+= load32(m, offset) & 0x3ffffff
193
+ h1 &+= (load32(m, offset + 3) >> 2) & 0x3ffffff
194
+ h2 &+= (load32(m, offset + 6) >> 4) & 0x3ffffff
195
+ h3 &+= (load32(m, offset + 9) >> 6) & 0x3ffffff
196
+ h4 &+= (load32(m, offset + 12) >> 8) | hibit
197
+
198
+ let d0 = UInt64(h0) * UInt64(r0) + UInt64(h1) * UInt64(s4) + UInt64(h2) * UInt64(s3) + UInt64(h3) * UInt64(s2) + UInt64(h4) * UInt64(s1)
199
+ var d1 = UInt64(h0) * UInt64(r1) + UInt64(h1) * UInt64(r0) + UInt64(h2) * UInt64(s4) + UInt64(h3) * UInt64(s3) + UInt64(h4) * UInt64(s2)
200
+ var d2 = UInt64(h0) * UInt64(r2) + UInt64(h1) * UInt64(r1) + UInt64(h2) * UInt64(r0) + UInt64(h3) * UInt64(s4) + UInt64(h4) * UInt64(s3)
201
+ var d3 = UInt64(h0) * UInt64(r3) + UInt64(h1) * UInt64(r2) + UInt64(h2) * UInt64(r1) + UInt64(h3) * UInt64(r0) + UInt64(h4) * UInt64(s4)
202
+ var d4 = UInt64(h0) * UInt64(r4) + UInt64(h1) * UInt64(r3) + UInt64(h2) * UInt64(r2) + UInt64(h3) * UInt64(r1) + UInt64(h4) * UInt64(r0)
203
+
204
+ var c = UInt32(truncatingIfNeeded: d0 >> 26); h0 = UInt32(truncatingIfNeeded: d0) & 0x3ffffff
205
+ d1 += UInt64(c); c = UInt32(truncatingIfNeeded: d1 >> 26); h1 = UInt32(truncatingIfNeeded: d1) & 0x3ffffff
206
+ d2 += UInt64(c); c = UInt32(truncatingIfNeeded: d2 >> 26); h2 = UInt32(truncatingIfNeeded: d2) & 0x3ffffff
207
+ d3 += UInt64(c); c = UInt32(truncatingIfNeeded: d3 >> 26); h3 = UInt32(truncatingIfNeeded: d3) & 0x3ffffff
208
+ d4 += UInt64(c); c = UInt32(truncatingIfNeeded: d4 >> 26); h4 = UInt32(truncatingIfNeeded: d4) & 0x3ffffff
209
+ h0 &+= c &* 5; c = h0 >> 26; h0 &= 0x3ffffff
210
+ h1 &+= c
211
+ }
212
+
213
+ var offset = 0
214
+ while message.count - offset >= 16 {
215
+ absorb(message, offset, hibit: 1 << 24)
216
+ offset += 16
217
+ }
218
+ if offset < message.count {
219
+ var last = Array(message[offset...])
220
+ last.append(1)
221
+ while last.count < 16 { last.append(0) }
222
+ absorb(last, 0, hibit: 0)
223
+ }
224
+
225
+ // full carry
226
+ var c = h1 >> 26; h1 &= 0x3ffffff
227
+ h2 &+= c; c = h2 >> 26; h2 &= 0x3ffffff
228
+ h3 &+= c; c = h3 >> 26; h3 &= 0x3ffffff
229
+ h4 &+= c; c = h4 >> 26; h4 &= 0x3ffffff
230
+ h0 &+= c &* 5; c = h0 >> 26; h0 &= 0x3ffffff
231
+ h1 &+= c
232
+
233
+ // h + -p
234
+ var g0 = h0 &+ 5; c = g0 >> 26; g0 &= 0x3ffffff
235
+ var g1 = h1 &+ c; c = g1 >> 26; g1 &= 0x3ffffff
236
+ var g2 = h2 &+ c; c = g2 >> 26; g2 &= 0x3ffffff
237
+ var g3 = h3 &+ c; c = g3 >> 26; g3 &= 0x3ffffff
238
+ var g4 = h4 &+ c &- (1 << 26)
239
+
240
+ // select h if h < p, else h - p
241
+ var mask = (g4 >> 31) &- 1
242
+ g0 &= mask; g1 &= mask; g2 &= mask; g3 &= mask; g4 &= mask
243
+ mask = ~mask
244
+ h0 = (h0 & mask) | g0
245
+ h1 = (h1 & mask) | g1
246
+ h2 = (h2 & mask) | g2
247
+ h3 = (h3 & mask) | g3
248
+ h4 = (h4 & mask) | g4
249
+
250
+ // h = h % 2^128
251
+ h0 = (h0 | (h1 << 26))
252
+ h1 = ((h1 >> 6) | (h2 << 20))
253
+ h2 = ((h2 >> 12) | (h3 << 14))
254
+ h3 = ((h3 >> 18) | (h4 << 8))
255
+
256
+ // mac = (h + pad) % 2^128
257
+ var f = UInt64(h0) + UInt64(load32(key, 16)); h0 = UInt32(truncatingIfNeeded: f)
258
+ f = UInt64(h1) + UInt64(load32(key, 20)) + (f >> 32); h1 = UInt32(truncatingIfNeeded: f)
259
+ f = UInt64(h2) + UInt64(load32(key, 24)) + (f >> 32); h2 = UInt32(truncatingIfNeeded: f)
260
+ f = UInt64(h3) + UInt64(load32(key, 28)) + (f >> 32); h3 = UInt32(truncatingIfNeeded: f)
261
+
262
+ var out = [UInt8]()
263
+ for w in [h0, h1, h2, h3] {
264
+ for i in 0..<4 { out.append(UInt8(truncatingIfNeeded: w >> (8 * UInt32(i)))) }
265
+ }
266
+ return out
267
+ }
268
+ }
269
+
270
+ // MARK: - BLAKE2b (RFC 7693), unkeyed
271
+
272
+ enum Blake2b {
273
+ private static let iv: [UInt64] = [
274
+ 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
275
+ 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
276
+ ]
277
+
278
+ private static let sigma: [[Int]] = [
279
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
280
+ [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
281
+ [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
282
+ [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
283
+ [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
284
+ [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
285
+ [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
286
+ [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
287
+ [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
288
+ [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
289
+ ]
290
+
291
+ @inline(__always) private static func rotr(_ v: UInt64, _ n: UInt64) -> UInt64 {
292
+ (v >> n) | (v << (64 - n))
293
+ }
294
+
295
+ private static func compress(_ h: inout [UInt64], block: [UInt8], counter: UInt64, last: Bool) {
296
+ var m = [UInt64](repeating: 0, count: 16)
297
+ for i in 0..<16 {
298
+ var w: UInt64 = 0
299
+ for j in 0..<8 { w |= UInt64(block[i * 8 + j]) << (8 * UInt64(j)) }
300
+ m[i] = w
301
+ }
302
+ var v = h + iv
303
+ v[12] ^= counter
304
+ // counter high word stays 0: inputs here are far below 2^64 bytes
305
+ if last { v[14] = ~v[14] }
306
+
307
+ @inline(__always) func g(_ a: Int, _ b: Int, _ c: Int, _ d: Int, _ x: UInt64, _ y: UInt64) {
308
+ v[a] = v[a] &+ v[b] &+ x
309
+ v[d] = rotr(v[d] ^ v[a], 32)
310
+ v[c] = v[c] &+ v[d]
311
+ v[b] = rotr(v[b] ^ v[c], 24)
312
+ v[a] = v[a] &+ v[b] &+ y
313
+ v[d] = rotr(v[d] ^ v[a], 16)
314
+ v[c] = v[c] &+ v[d]
315
+ v[b] = rotr(v[b] ^ v[c], 63)
316
+ }
317
+
318
+ for round in 0..<12 {
319
+ let s = sigma[round % 10]
320
+ g(0, 4, 8, 12, m[s[0]], m[s[1]])
321
+ g(1, 5, 9, 13, m[s[2]], m[s[3]])
322
+ g(2, 6, 10, 14, m[s[4]], m[s[5]])
323
+ g(3, 7, 11, 15, m[s[6]], m[s[7]])
324
+ g(0, 5, 10, 15, m[s[8]], m[s[9]])
325
+ g(1, 6, 11, 12, m[s[10]], m[s[11]])
326
+ g(2, 7, 8, 13, m[s[12]], m[s[13]])
327
+ g(3, 4, 9, 14, m[s[14]], m[s[15]])
328
+ }
329
+ for i in 0..<8 { h[i] ^= v[i] ^ v[i + 8] }
330
+ }
331
+
332
+ /// BLAKE2b with no key, `outputLength` 1...64 bytes (`crypto_generichash`).
333
+ static func hash(_ message: [UInt8], outputLength: Int) -> [UInt8] {
334
+ precondition((1...64).contains(outputLength))
335
+ var h = iv
336
+ h[0] ^= 0x01010000 ^ UInt64(outputLength)
337
+
338
+ var offset = 0
339
+ // Every full block except the last one; the last block (full or not) is final.
340
+ while message.count - offset > 128 {
341
+ compress(&h, block: Array(message[offset..<offset + 128]), counter: UInt64(offset + 128), last: false)
342
+ offset += 128
343
+ }
344
+ var last = Array(message[offset...])
345
+ let consumed = UInt64(message.count)
346
+ while last.count < 128 { last.append(0) }
347
+ compress(&h, block: last, counter: consumed, last: true)
348
+
349
+ var out = [UInt8]()
350
+ for w in h {
351
+ for i in 0..<8 { out.append(UInt8(truncatingIfNeeded: w >> (8 * UInt64(i)))) }
352
+ }
353
+ return Array(out[0..<outputLength])
354
+ }
355
+ }
@@ -0,0 +1,24 @@
1
+ // Sealed boxes made by libsodium for known keys, for the Swift check.
2
+ import sodium from 'libsodium-wrappers-sumo';
3
+ import { createHash } from 'node:crypto';
4
+ await sodium.ready;
5
+ const b64 = (u) => Buffer.from(u).toString('base64');
6
+ const vectors = [];
7
+ const messages = ['', 'a', 'hello', 'x'.repeat(15), 'y'.repeat(16), 'z'.repeat(17), 'q'.repeat(1000),
8
+ JSON.stringify({ title: 'Alice', body: 'héllo 👋 — ok', sender: 'Alice', senderJid: 'a@x', chat: 'Team', chatJid: 't@c.x', msgID: '1', image: '' })];
9
+ for (const message of messages) {
10
+ const pair = sodium.crypto_box_keypair();
11
+ const sealed = sodium.crypto_box_seal(sodium.from_string(message), pair.publicKey);
12
+ vectors.push({ sk: b64(pair.privateKey), pk: b64(pair.publicKey), sealed: b64(sealed), plaintext: b64(sodium.from_string(message)),
13
+ kid: createHash('sha256').update(Buffer.from(pair.publicKey)).digest('hex').slice(0, 16) });
14
+ }
15
+ // primitives
16
+ const key = sodium.randombytes_buf(32), nonce = sodium.randombytes_buf(24), msg = sodium.randombytes_buf(100);
17
+ const box = sodium.crypto_secretbox_easy(msg, nonce, key);
18
+ const hs = null;
19
+ const bl = sodium.crypto_generichash(24, msg);
20
+ const pkey = sodium.randombytes_buf(32);
21
+ const auth = sodium.crypto_onetimeauth(msg, pkey);
22
+ const out = { vectors, secretbox: { key: b64(key), nonce: b64(nonce), msg: b64(msg), box: b64(box) },
23
+ blake2b24: { msg: b64(msg), out: b64(bl) }, poly1305: { key: b64(pkey), msg: b64(msg), out: b64(auth) } };
24
+ process.stdout.write(JSON.stringify(out));
@@ -0,0 +1,37 @@
1
+ import Foundation
2
+
3
+ struct Vector: Decodable { let sk: String; let pk: String; let sealed: String; let plaintext: String; let kid: String }
4
+ struct Secretbox: Decodable { let key: String; let nonce: String; let msg: String; let box: String }
5
+ struct Blake: Decodable { let msg: String; let out: String }
6
+ struct Poly: Decodable { let key: String; let msg: String; let out: String }
7
+ struct File: Decodable { let vectors: [Vector]; let secretbox: Secretbox; let blake2b24: Blake; let poly1305: Poly }
8
+
9
+ func b(_ s: String) -> [UInt8] { [UInt8](Data(base64Encoded: s)!) }
10
+
11
+ let path = CommandLine.arguments[1]
12
+ let file = try! JSONDecoder().decode(File.self, from: Data(contentsOf: URL(fileURLWithPath: path)))
13
+ var failures = 0
14
+ func check(_ name: String, _ ok: Bool) {
15
+ print((ok ? "ok " : "FAIL ") + name)
16
+ if !ok { failures += 1 }
17
+ }
18
+
19
+ check("blake2b-24", Blake2b.hash(b(file.blake2b24.msg), outputLength: 24) == b(file.blake2b24.out))
20
+ check("poly1305", Poly1305.mac(b(file.poly1305.msg), key: b(file.poly1305.key)) == b(file.poly1305.out))
21
+ check("secretbox open", SealedBox.secretboxOpen(b(file.secretbox.box), nonce: b(file.secretbox.nonce), key: b(file.secretbox.key)) == b(file.secretbox.msg))
22
+ var damaged = b(file.secretbox.box); damaged[damaged.count - 1] ^= 1
23
+ check("secretbox rejects a damaged box", SealedBox.secretboxOpen(damaged, nonce: b(file.secretbox.nonce), key: b(file.secretbox.key)) == nil)
24
+
25
+ for (i, v) in file.vectors.enumerated() {
26
+ let sk = b(v.sk)
27
+ check("vector \(i) public key", SealedBox.publicKey(of: sk) == b(v.pk))
28
+ let opened = SealedBox.open(b(v.sealed), secretKey: sk)
29
+ check("vector \(i) opens (\(b(v.plaintext).count) bytes)", opened == b(v.plaintext))
30
+ var bad = b(v.sealed); bad[40] ^= 0x80
31
+ check("vector \(i) rejects a damaged box", SealedBox.open(bad, secretKey: sk) == nil)
32
+ let other = b(file.vectors[(i + 1) % file.vectors.count].sk)
33
+ check("vector \(i) rejects another key", SealedBox.open(b(v.sealed), secretKey: other) == nil)
34
+ }
35
+ check("short box", SealedBox.open([UInt8](repeating: 1, count: 47), secretKey: b(file.vectors[0].sk)) == nil)
36
+ print(failures == 0 ? "ALL OK" : "\(failures) FAILED")
37
+ exit(failures == 0 ? 0 : 1)
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env bash
2
+ # Pins SealedBox.swift against libsodium: vectors.json holds sealed boxes,
3
+ # a secretbox, a BLAKE2b-192 and a Poly1305 tag made by libsodium
4
+ # (gen-vectors.mjs, needs `npm i libsodium-wrappers-sumo` next to it), and
5
+ # main.swift opens them with the extension's code. Needs Xcode's swiftc.
6
+ #
7
+ # plugins/notification-service/check/run.sh
8
+ set -euo pipefail
9
+ cd "$(dirname "$0")"
10
+ out="$(mktemp -d)"
11
+ swiftc -O ../SealedBox.swift main.swift -o "$out/check"
12
+ "$out/check" vectors.json
@@ -0,0 +1 @@
1
+ {"vectors":[{"sk":"psM/YEAHlVzqtN7KovphUjE5m7l17ff2MtiE3XU3Rwc=","pk":"P0AoVn+A+5HOI2AWQR5LXYfM9PHq3kJp1FCs2HRCkXY=","sealed":"G3wRmRzod33/f03nW+VhrVO2l4nnAvjGQdTvmzclOxE4cjpoZLBRb+HnFofI4ypH","plaintext":"","kid":"fdc829f52a022956"},{"sk":"Ogi04QlXCW1IiS2xC3KnnFkpbcfABAGUkGb4GxcLEO8=","pk":"FQQKe0KEz2TtlctxVMQcpuu+XNTygtNsiu+D6PtUAFE=","sealed":"bKRCdjHWR0LV9UWcpjKTYXdYGApsnfDm2q5MCWQCFzPQ4CEOt0+cnBZbWjVBoP5VVw==","plaintext":"YQ==","kid":"cb31ce9c7a59fb37"},{"sk":"4Eyj9vf7de+t6iaYSg84h1ewjMdotI4ModztdG3ATg8=","pk":"Otr0gAyXgdMbayn6xJPrblxVl69qQ59XdWBjRpX7JQc=","sealed":"9ouwN9/psosK75W1BruvhtjoJwp6JtazX1uhIrq+5QZuRFKFrW5LIkFpY5DN4irDdsugdZ0=","plaintext":"aGVsbG8=","kid":"730fca4a57de1bc7"},{"sk":"7ny883FomgIgDoED095pPBX0wwDH4bakBIBeMwkylrY=","pk":"mlNQn4wPpUoHdW4sn/OzwTuHtloRvWrbHXDuuR1qYhw=","sealed":"46McjbyP1UQ0ZlHmeQJatsWhiKxdiREiDZ8VEIRVcH/GSjfUNIEXiu0sBMTJDzCS2DsN/IsltMFuGq0/gpNi","plaintext":"eHh4eHh4eHh4eHh4eHh4","kid":"9306f2956c55f34a"},{"sk":"SiT54TCZ8JikZYE/8d5HO9Py4h2frgC4UeyFOt1nWGc=","pk":"hlinYRVWthcOWNcpj1+8GLiPglaMduupEJZbPH59wkg=","sealed":"7PFOl2gyBwvfoxfiqOJujHB5tPTTXFlwrH4UoLnGYEw/+Em1x466vmTFEMqLqiFLF+/5vYJAfprmr0VxmnGJEg==","plaintext":"eXl5eXl5eXl5eXl5eXl5eQ==","kid":"e2a6c8eb7bf954f2"},{"sk":"YK2tqAoGCoeOR04drKeXQ0p154uTagHKz1fFFh5QWvI=","pk":"jWEcVvha+Q+pYCae2Q078liIUW8nxqdVU8lFMWu3lBY=","sealed":"My+/ng0WLFg0rdK2ndFbojUDKBWzetQlfSEju7a5Fy5Ea+gAwGuwHNem4bhOc7JYPtWPDwSHhjVVvB6iNzP2yew=","plaintext":"enp6enp6enp6enp6enp6eno=","kid":"7d297889ca460ceb"},{"sk":"4lpxBLHJVWN1LILEZ9ewSH0FYO4yF8IPQGs41y5E7cI=","pk":"tXrQ+qOYKuTvEU66adVcFPI2aBV4mH+zan5F0iExmwE=","sealed":"/SM2pXuwyPneAQhJutEN00rUTK4Ngnfgto3jX+3hGFoRWy/JJAXEp+i7huR8bbhcs2DUH/V4soWiE1/8mq4qrIbJJUlZWdePskVnU88de5vN/mfPFo+TzaHQVaAudAM+Qf3SkT5t+qxECXdY/9rpEUkI44FQkWz5zxP67RwtYe5nJQxTyM25mr31pKqr4j7Od3vQXLOxhROPnn4ftJC7UAnJuAmhV81TXnWUKakUJFD87/Ge2EVt/MJfplndu7tC0KDTRRpwd+KLsAE0FaSRqvOD9eg+muND7GhJEhMLiTB+ZjHB8dbXw/JQPvTl7Bdynh86UGDjG2C/QchiK26OkYHRLNFgHjeMm8Vs0yDM+FIsiNYtG5qKym2qlLP+E9+tgJW/9Mo3CWA+0B3iOoRebmfab9maJBHf1CoZv8oFeXliPGBdDaj+cIxAfY+W28cQAHuwrZ/tmfdIoW+qxkFN2fAquYc+NY31s0RVuPZje3XnNAgou8bI8dokiXx+eELAXFfPWo7fdI6ZUFTQA69Y+2ZOtqPJ+Am63rZg40TLsJJhvwjJkhfRaNbvNxlh3YQ2LZ3OkN+SI4yCG0LcDYxWwD2D18Npjajnn+X40RfAVKU77MFl4sFUkK7VhCZrxfQ2LxRNT9OQM4dOp/uf74258c7bV9//qckfO7PsZZCu35aGXF6JlhfDLwU8V36/3TE0eKgJIhV/PsbsHk9Wq6r4Io2r3sGpk9xfqjrg/n1IMFrl3C6L8szSJWjd/P/mnh+L95DnJfX6hMh13Z5R19e//2pTQCjUk9o4fJJef4SUgc15BjejcPPyPsyK/eaSpi/pbkVrprvCkUFmZmXHOtua/YAhtJ3sKlIb6w5jDg0URXc0XeSBJvbCY/V7EuLLBtPnpld6emExNYXTI//Qi7E76zOYQ5PZLU8rJgKEI9DZ1FqGKMTHvgydKscMJSAPzZ2fDbEMjKBvWN+CUAiLpEYma+ciF8mLMB0VZeeJyAV+oEZSMG4pDyOOh1smR5tJ1/McPkXht5gTuHfMRfaVQZa0cr/hzPbWhFMBvgJOWdUbYKExhrXjnrHC28xPsGfqT0c/atP3ySyuuDiGLcIpzLo4TAM2QJVE2f/2ia++ptoKhFp/Q3kQ4bqPenHxf3AbWCveHzmfQ9BRlwZ8aLPPYWtzG4s0bz48RpLRO2Cnxa9/MbNaG8WumuG6sK9wU5SK4diau82G1GVxKSE/32lJKBx4OnI2nH6SkB9h7H1UBwaYZPgkR75rocaQeAn/ley/4IWb/5NKbTx3AIYUIpPSNvfzFbzunb+hDLOkJiKDodQE6E+JYbOctbogmWQULCyBW1x1du38VeMUJjlSfse9nRN3vYkvf9DzkZIVc3Fd8WcIzO8MDEjy/VBedg==","plaintext":"cXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcQ==","kid":"3f3c6f78c9cc4b77"},{"sk":"85ijOhZXrKrVfynAK+oLZ9y8Gl4fxXmkbvy/seh9/Qc=","pk":"ExZYsb44gdGGq48vel9El5jzMnaD60ND81UCJx8RKy0=","sealed":"0qM9JBNfalw7KVnCCfumgNBwsIt3qSn0Gnuo2N9fQybkAvSAV0PBug210jJPb4nAKE32EhNq7977njuOMvrAZzMA2AuI9GKNxlTYpdeV6+0G4ZPcZpH0eR5Joblk7myRF622E+23XmuBI4q4dpYNJaWr/SGzUQ951UFBiqMwoDvbrNPfNCpQ8+0q3Pg4flEHEjzF7hVuSJX+elQ2E4gMTJRJmm7Fx6c9dJbPYpO3P7P8EN9R0oxv","plaintext":"eyJ0aXRsZSI6IkFsaWNlIiwiYm9keSI6ImjDqWxsbyDwn5GLIOKAlCBvayIsInNlbmRlciI6IkFsaWNlIiwic2VuZGVySmlkIjoiYUB4IiwiY2hhdCI6IlRlYW0iLCJjaGF0SmlkIjoidEBjLngiLCJtc2dJRCI6IjEiLCJpbWFnZSI6IiJ9","kid":"c396a01c6df912f0"}],"secretbox":{"key":"PNi8//d+s1h1376uDwtQb02HwDJ867wS69nn2Nh+YGk=","nonce":"MJALUxy6rktv0IeXa5Eel2TPg3PRx3Ra","msg":"7gJAxFbQvjY9WUcggw+Eip8eld00817fLWH3YPBiQlFWAF20JCKXf/KfxxpQ4yk6nkJjUJdowDYhoHPKiPKcPrH6nUWJ+0WHHD7BLziabdCNxWr0WLcHqYlfevynoDBOZINpNA==","box":"ABn+r2ayhlV/tBVzYZUS1PoxVKdLrv+IZMTgOk3Czqlz3bVm+VHOolF1XW4XCodO7sL7ikl5dzbGuUye/XRuIgQxGiWXIyewmK9NKv/7CJG4pIujo0Jqydp0LIvWL4fvcZhDCzmR+dIaUfHIQizDs2ofGFc="},"blake2b24":{"msg":"7gJAxFbQvjY9WUcggw+Eip8eld00817fLWH3YPBiQlFWAF20JCKXf/KfxxpQ4yk6nkJjUJdowDYhoHPKiPKcPrH6nUWJ+0WHHD7BLziabdCNxWr0WLcHqYlfevynoDBOZINpNA==","out":"aOiFzIaoPYSHzp1/PrvwUfTbLOdT/mwY"},"poly1305":{"key":"kRbuceM6lJgm3xkFq9APi50xfeG1DQc2HaG7bunl3rA=","msg":"7gJAxFbQvjY9WUcggw+Eip8eld00817fLWH3YPBiQlFWAF20JCKXf/KfxxpQ4yk6nkJjUJdowDYhoHPKiPKcPrH6nUWJ+0WHHD7BLziabdCNxWr0WLcHqYlfevynoDBOZINpNA==","out":"6iotU8uBgFrEu9FpQuxrkQ=="}}
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Expo config plugin — adds an iOS Notification Service Extension for Ethora
3
+ * chat pushes. The extension (plugins/notification-service/NotificationService.swift)
4
+ * - decrypts encrypted (sealed) pushes with the device key the app keeps in
5
+ * the keychain and shows the real title and text in place of the
6
+ * "New message" placeholder;
7
+ * - shows the attachment thumbnail (photo / video preview) the push service
8
+ * sends as `previewUrl`.
9
+ *
10
+ * An extension is a separate target signed with the app, so it cannot ship
11
+ * inside the npm package as code: this plugin writes it into ios/ on every
12
+ * `expo prebuild` (the generated ios/ folder is gitignored in Expo apps).
13
+ * For the key to be readable from the extension, the app and the extension
14
+ * share a keychain access group (`$(AppIdentifierPrefix)<bundle id>`, the
15
+ * app's own default group, listed first so items the app writes without a
16
+ * group — expo-secure-store's — land in it). The sealed box is opened in
17
+ * plain Swift (SealedBox.swift: CryptoKit for X25519, the rest ported from
18
+ * tweetnacl), so the extension needs no pod.
19
+ *
20
+ * Host app.json:
21
+ * "plugins": ["@ethora/chat-component-rn"]
22
+ * or with options:
23
+ * "plugins": [["@ethora/chat-component-rn", { "appleTeamId": "ABCDE12345" }]]
24
+ *
25
+ * Options:
26
+ * appleTeamId signing team of the extension; default: ios.appleTeamId,
27
+ * else the team already set on the app target in Xcode.
28
+ */
29
+
30
+ const fs = require('fs');
31
+ const path = require('path');
32
+ const { withDangerousMod, withEntitlementsPlist, withXcodeProject } = require('expo/config-plugins');
33
+
34
+ const TARGET_NAME = 'EthoraNotificationService';
35
+ const SOURCE_DIR = path.join(__dirname, 'notification-service');
36
+ const SOURCES = ['NotificationService.swift', 'SealedBox.swift'];
37
+ const FILES = [...SOURCES, 'Info.plist'];
38
+ const ENTITLEMENTS_FILE = `${TARGET_NAME}.entitlements`;
39
+ const KEYCHAIN_GROUPS_KEY = 'keychain-access-groups';
40
+
41
+ const appBundleId = (config) => {
42
+ const bundleId = config.ios && config.ios.bundleIdentifier;
43
+ if (!bundleId) {
44
+ throw new Error(
45
+ '[@ethora/chat-component-rn] ios.bundleIdentifier is required for the notification service extension'
46
+ );
47
+ }
48
+ return bundleId;
49
+ };
50
+
51
+ const extensionBundleId = (config) => `${appBundleId(config)}.${TARGET_NAME}`;
52
+
53
+ /** The keychain access group shared by the app and the extension. */
54
+ const keychainGroup = (config) => `$(AppIdentifierPrefix)${appBundleId(config)}`;
55
+
56
+ const withSharedGroupFirst = (groups, group) => {
57
+ const list = Array.isArray(groups) ? groups.filter((g) => g !== group) : [];
58
+ return [group, ...list];
59
+ };
60
+
61
+ // EAS Build creates signing credentials only for targets it knows about.
62
+ const withEasAppExtension = (config) => {
63
+ const bundleIdentifier = extensionBundleId(config);
64
+ const group = keychainGroup(config);
65
+ config.extra = config.extra || {};
66
+ config.extra.eas = config.extra.eas || {};
67
+ config.extra.eas.build = config.extra.eas.build || {};
68
+ config.extra.eas.build.experimental = config.extra.eas.build.experimental || {};
69
+ const ios = (config.extra.eas.build.experimental.ios =
70
+ config.extra.eas.build.experimental.ios || {});
71
+ const extensions = (ios.appExtensions = ios.appExtensions || []);
72
+ let entry = extensions.find((ext) => ext.targetName === TARGET_NAME);
73
+ if (!entry) {
74
+ entry = { targetName: TARGET_NAME, bundleIdentifier, entitlements: {} };
75
+ extensions.push(entry);
76
+ }
77
+ entry.entitlements = entry.entitlements || {};
78
+ entry.entitlements[KEYCHAIN_GROUPS_KEY] = withSharedGroupFirst(
79
+ entry.entitlements[KEYCHAIN_GROUPS_KEY],
80
+ group
81
+ );
82
+ return config;
83
+ };
84
+
85
+ // The app's side of the shared keychain group (Keychain Sharing capability).
86
+ const withAppKeychainGroup = (config) =>
87
+ withEntitlementsPlist(config, (cfg) => {
88
+ const group = keychainGroup(cfg);
89
+ cfg.modResults[KEYCHAIN_GROUPS_KEY] = withSharedGroupFirst(
90
+ cfg.modResults[KEYCHAIN_GROUPS_KEY],
91
+ group
92
+ );
93
+ return cfg;
94
+ });
95
+
96
+ const entitlementsPlist = (group) => `<?xml version="1.0" encoding="UTF-8"?>
97
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
98
+ <plist version="1.0">
99
+ <dict>
100
+ <key>${KEYCHAIN_GROUPS_KEY}</key>
101
+ <array>
102
+ <string>${group}</string>
103
+ </array>
104
+ </dict>
105
+ </plist>
106
+ `;
107
+
108
+ // Copied on every prebuild so an SDK update reaches the app.
109
+ const withExtensionFiles = (config) =>
110
+ withDangerousMod(config, [
111
+ 'ios',
112
+ (cfg) => {
113
+ const dest = path.join(cfg.modRequest.platformProjectRoot, TARGET_NAME);
114
+ fs.mkdirSync(dest, { recursive: true });
115
+ for (const file of FILES) {
116
+ fs.copyFileSync(path.join(SOURCE_DIR, file), path.join(dest, file));
117
+ }
118
+ fs.writeFileSync(path.join(dest, ENTITLEMENTS_FILE), entitlementsPlist(keychainGroup(cfg)));
119
+ return cfg;
120
+ },
121
+ ]);
122
+
123
+ const unquote = (value) => (typeof value === 'string' ? value.replace(/^"(.*)"$/, '$1') : value);
124
+
125
+ const targetBuildSettings = (project, targetUuid) => {
126
+ const target = project.pbxNativeTargetSection()[targetUuid];
127
+ const list = target && project.pbxXCConfigurationList()[target.buildConfigurationList];
128
+ const configs = project.pbxXCBuildConfigurationSection();
129
+ return ((list && list.buildConfigurations) || [])
130
+ .map((ref) => configs[ref.value])
131
+ .filter((entry) => entry && entry.buildSettings)
132
+ .map((entry) => entry.buildSettings);
133
+ };
134
+
135
+ const withExtensionTarget = (config, { appleTeamId } = {}) =>
136
+ withXcodeProject(config, (cfg) => {
137
+ const project = cfg.modResults;
138
+ const bundleIdentifier = extensionBundleId(cfg);
139
+
140
+ const appTarget = project.getFirstTarget();
141
+ const appSettings = targetBuildSettings(project, appTarget.uuid)[0] || {};
142
+ const team = appleTeamId || (cfg.ios && cfg.ios.appleTeamId) || unquote(appSettings.DEVELOPMENT_TEAM);
143
+
144
+ let targetUuid = project.findTargetKey(`"${TARGET_NAME}"`) || project.findTargetKey(TARGET_NAME);
145
+ if (!targetUuid) {
146
+ // xcode's addTarget expects these sections to exist.
147
+ const objects = project.hash.project.objects;
148
+ objects.PBXTargetDependency = objects.PBXTargetDependency || {};
149
+ objects.PBXContainerItemProxy = objects.PBXContainerItemProxy || {};
150
+
151
+ const group = project.addPbxGroup([...FILES, ENTITLEMENTS_FILE], TARGET_NAME, TARGET_NAME);
152
+ const mainGroup = project.getFirstProject().firstProject.mainGroup;
153
+ project.addToPbxGroup(group.uuid, mainGroup);
154
+
155
+ // 'app_extension' also embeds the .appex into the app (Copy Files to
156
+ // PlugIns) and makes the app depend on it.
157
+ const target = project.addTarget(TARGET_NAME, 'app_extension', TARGET_NAME, bundleIdentifier);
158
+ project.addBuildPhase(SOURCES, 'PBXSourcesBuildPhase', 'Sources', target.uuid);
159
+ project.addBuildPhase([], 'PBXResourcesBuildPhase', 'Resources', target.uuid);
160
+ project.addBuildPhase([], 'PBXFrameworksBuildPhase', 'Frameworks', target.uuid);
161
+ targetUuid = target.uuid;
162
+ }
163
+
164
+ for (const settings of targetBuildSettings(project, targetUuid)) {
165
+ Object.assign(settings, {
166
+ PRODUCT_NAME: `"${TARGET_NAME}"`,
167
+ PRODUCT_BUNDLE_IDENTIFIER: `"${bundleIdentifier}"`,
168
+ INFOPLIST_FILE: `"${TARGET_NAME}/Info.plist"`,
169
+ GENERATE_INFOPLIST_FILE: 'NO',
170
+ CODE_SIGN_ENTITLEMENTS: `"${TARGET_NAME}/${ENTITLEMENTS_FILE}"`,
171
+ SWIFT_VERSION: appSettings.SWIFT_VERSION || '5.0',
172
+ IPHONEOS_DEPLOYMENT_TARGET: appSettings.IPHONEOS_DEPLOYMENT_TARGET || '15.1',
173
+ TARGETED_DEVICE_FAMILY: appSettings.TARGETED_DEVICE_FAMILY || '"1,2"',
174
+ // Must match the app, App Store Connect rejects a mismatch.
175
+ MARKETING_VERSION: cfg.version || appSettings.MARKETING_VERSION || '1.0.0',
176
+ CURRENT_PROJECT_VERSION: (cfg.ios && cfg.ios.buildNumber) || appSettings.CURRENT_PROJECT_VERSION || '1',
177
+ CODE_SIGN_STYLE: 'Automatic',
178
+ CLANG_ENABLE_MODULES: 'YES',
179
+ SKIP_INSTALL: 'YES',
180
+ });
181
+ if (team) settings.DEVELOPMENT_TEAM = team;
182
+ }
183
+ return cfg;
184
+ });
185
+
186
+ const withNotificationServiceExtension = (config, props = {}) =>
187
+ withExtensionTarget(withExtensionFiles(withAppKeychainGroup(withEasAppExtension(config))), props);
188
+
189
+ module.exports = withNotificationServiceExtension;
@@ -720,7 +720,25 @@ export const ArowDownIcon = ({
720
720
  </Svg>
721
721
  );
722
722
 
723
- export const DoubleTick = ({ fill = '#fff', color = '#0052CD', ...props }) => (
723
+ export const SingleTick = ({ stroke = '#16A34A', ...props }) => (
724
+ <Svg
725
+ width="16"
726
+ height="16"
727
+ viewBox="0 0 16 16"
728
+ fill="none"
729
+ {...props}
730
+ >
731
+ <Path
732
+ d="M4 8.5L7 11.5L12 5.5"
733
+ stroke={stroke}
734
+ strokeWidth="1.5"
735
+ strokeLinecap="round"
736
+ strokeLinejoin="round"
737
+ />
738
+ </Svg>
739
+ );
740
+
741
+ export const DoubleTick = ({ fill = '#fff', color = '#0052CD', stroke = '#16A34A', ...props }) => (
724
742
  <Svg
725
743
  width="16"
726
744
  height="16"
@@ -730,7 +748,7 @@ export const DoubleTick = ({ fill = '#fff', color = '#0052CD', ...props }) => (
730
748
  >
731
749
  <Path
732
750
  d="M1.75 9.75L4.25 12.25M7.75 8.25L10.25 5.75M5.75 9.75L8.25 12.25L14.25 5.75"
733
- stroke="#16A34A"
751
+ stroke={stroke}
734
752
  strokeWidth="1.5"
735
753
  strokeLinecap="round"
736
754
  strokeLinejoin="round"