@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.
- package/CHANGELOG.md +10 -0
- package/README.md +18 -0
- package/app.plugin.js +2 -0
- package/lib/commonjs/assets/icons.js +2 -2
- package/lib/commonjs/assets/icons.js.map +1 -1
- package/lib/commonjs/components/MainComponents/AlbumGrid.js +152 -0
- package/lib/commonjs/components/MainComponents/AlbumGrid.js.map +1 -0
- package/lib/commonjs/components/MainComponents/ChatRoom.js +2 -2
- package/lib/commonjs/components/MainComponents/ChatRoom.js.map +1 -1
- package/lib/commonjs/components/MainComponents/MediaMessage.js +199 -123
- package/lib/commonjs/components/MainComponents/MediaMessage.js.map +1 -1
- package/lib/commonjs/components/MainComponents/MediaUploadOverlay.js +109 -0
- package/lib/commonjs/components/MainComponents/MediaUploadOverlay.js.map +1 -0
- package/lib/commonjs/components/MessageBubble/MediaCaption.js +51 -0
- package/lib/commonjs/components/MessageBubble/MediaCaption.js.map +1 -0
- package/lib/commonjs/components/MessageBubble/Message.js +40 -1
- package/lib/commonjs/components/MessageBubble/Message.js.map +1 -1
- package/lib/commonjs/components/MessageBubble/MessageInteractions.js +2 -1
- package/lib/commonjs/components/MessageBubble/MessageInteractions.js.map +1 -1
- package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js +75 -0
- package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/LastMessageItem.js +20 -4
- package/lib/commonjs/components/RoomComponents/LastMessageItem.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/styled/LastAudioMessage.js +3 -2
- package/lib/commonjs/components/RoomComponents/styled/LastAudioMessage.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/styled/LastMessageFile.js +3 -2
- package/lib/commonjs/components/RoomComponents/styled/LastMessageFile.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/styled/LastMessagePhoto.js +3 -2
- package/lib/commonjs/components/RoomComponents/styled/LastMessagePhoto.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/styled/LastMessageVideo.js +3 -2
- package/lib/commonjs/components/RoomComponents/styled/LastMessageVideo.js.map +1 -1
- package/lib/commonjs/components/Thread/ThreadWrapper.js +3 -2
- package/lib/commonjs/components/Thread/ThreadWrapper.js.map +1 -1
- package/lib/commonjs/components/styled/MediaFilePreview.js +60 -47
- package/lib/commonjs/components/styled/MediaFilePreview.js.map +1 -1
- package/lib/commonjs/components/styled/MessageImage.js +16 -6
- package/lib/commonjs/components/styled/MessageImage.js.map +1 -1
- package/lib/commonjs/components/styled/SendInput.js +99 -68
- package/lib/commonjs/components/styled/SendInput.js.map +1 -1
- package/lib/commonjs/components/styled/VideoMessage.js +24 -10
- package/lib/commonjs/components/styled/VideoMessage.js.map +1 -1
- package/lib/commonjs/context/xmppProvider.js +46 -0
- package/lib/commonjs/context/xmppProvider.js.map +1 -1
- package/lib/commonjs/e2ee/fileEnvelope.js +6 -0
- package/lib/commonjs/e2ee/fileEnvelope.js.map +1 -1
- package/lib/commonjs/e2ee/sealedBody.js +32 -2
- package/lib/commonjs/e2ee/sealedBody.js.map +1 -1
- package/lib/commonjs/helpers/albumLayout.js +150 -0
- package/lib/commonjs/helpers/albumLayout.js.map +1 -0
- package/lib/commonjs/helpers/attachments.js +255 -0
- package/lib/commonjs/helpers/attachments.js.map +1 -0
- package/lib/commonjs/helpers/caption.js +214 -0
- package/lib/commonjs/helpers/caption.js.map +1 -0
- package/lib/commonjs/helpers/createMainMessageForThread.js +5 -1
- package/lib/commonjs/helpers/createMainMessageForThread.js.map +1 -1
- package/lib/commonjs/helpers/createMessageFromXml.js +19 -5
- package/lib/commonjs/helpers/createMessageFromXml.js.map +1 -1
- package/lib/commonjs/helpers/fileKind.js +40 -0
- package/lib/commonjs/helpers/fileKind.js.map +1 -0
- package/lib/commonjs/helpers/getDataFromXml.js +6 -1
- package/lib/commonjs/helpers/getDataFromXml.js.map +1 -1
- package/lib/commonjs/helpers/mapWithConcurrency.js +41 -0
- package/lib/commonjs/helpers/mapWithConcurrency.js.map +1 -0
- package/lib/commonjs/helpers/mediaLimits.js +33 -0
- package/lib/commonjs/helpers/mediaLimits.js.map +1 -0
- package/lib/commonjs/helpers/mediaSendRetry.js +64 -0
- package/lib/commonjs/helpers/mediaSendRetry.js.map +1 -0
- package/lib/commonjs/helpers/mediaWireData.js +62 -0
- package/lib/commonjs/helpers/mediaWireData.js.map +1 -0
- package/lib/commonjs/helpers/mentionSpans.js +19 -0
- package/lib/commonjs/helpers/mentionSpans.js.map +1 -0
- package/lib/commonjs/helpers/mimeToExtension.js +45 -0
- package/lib/commonjs/helpers/mimeToExtension.js.map +1 -1
- package/lib/commonjs/helpers/readReceipts.js +173 -0
- package/lib/commonjs/helpers/readReceipts.js.map +1 -0
- package/lib/commonjs/helpers/repairFilenameEncoding.js +79 -0
- package/lib/commonjs/helpers/repairFilenameEncoding.js.map +1 -0
- package/lib/commonjs/helpers/safeJson.js +18 -0
- package/lib/commonjs/helpers/safeJson.js.map +1 -0
- package/lib/commonjs/helpers/secureStoreRuntime.js +10 -1
- package/lib/commonjs/helpers/secureStoreRuntime.js.map +1 -1
- package/lib/commonjs/hooks/useSendMessage.js +384 -193
- package/lib/commonjs/hooks/useSendMessage.js.map +1 -1
- package/lib/commonjs/i18n/strings.js +96 -0
- package/lib/commonjs/i18n/strings.js.map +1 -1
- package/lib/commonjs/main.js +81 -0
- package/lib/commonjs/main.js.map +1 -1
- package/lib/commonjs/networking/api-requests/auth.api.js +62 -3
- package/lib/commonjs/networking/api-requests/auth.api.js.map +1 -1
- package/lib/commonjs/networking/api-requests/push.api.js +22 -7
- package/lib/commonjs/networking/api-requests/push.api.js.map +1 -1
- package/lib/commonjs/networking/stanzaHandlers.js +100 -3
- package/lib/commonjs/networking/stanzaHandlers.js.map +1 -1
- package/lib/commonjs/networking/xmpp/handleStanzas.xmpp.js +4 -2
- package/lib/commonjs/networking/xmpp/handleStanzas.xmpp.js.map +1 -1
- package/lib/commonjs/networking/xmpp/sendDisplayedMarker.xmpp.js +28 -0
- package/lib/commonjs/networking/xmpp/sendDisplayedMarker.xmpp.js.map +1 -0
- package/lib/commonjs/networking/xmpp/sendMediaMessage.xmpp.js +53 -2
- package/lib/commonjs/networking/xmpp/sendMediaMessage.xmpp.js.map +1 -1
- package/lib/commonjs/networking/xmpp/sendReceivedReceipt.xmpp.js +28 -0
- package/lib/commonjs/networking/xmpp/sendReceivedReceipt.xmpp.js.map +1 -0
- package/lib/commonjs/networking/xmppClient.js +64 -0
- package/lib/commonjs/networking/xmppClient.js.map +1 -1
- package/lib/commonjs/push/pushKeyStore.js +169 -0
- package/lib/commonjs/push/pushKeyStore.js.map +1 -0
- package/lib/commonjs/push/sealedBox.js +106 -0
- package/lib/commonjs/push/sealedBox.js.map +1 -0
- package/lib/commonjs/push/sealedPush.js +124 -0
- package/lib/commonjs/push/sealedPush.js.map +1 -0
- package/lib/commonjs/push/sealedPushTask.js +195 -0
- package/lib/commonjs/push/sealedPushTask.js.map +1 -0
- package/lib/commonjs/roomStore/persistence.js +78 -5
- package/lib/commonjs/roomStore/persistence.js.map +1 -1
- package/lib/commonjs/roomStore/roomHeapSlice.js +4 -0
- package/lib/commonjs/roomStore/roomHeapSlice.js.map +1 -1
- package/lib/commonjs/roomStore/roomsSlice.js +114 -1
- package/lib/commonjs/roomStore/roomsSlice.js.map +1 -1
- package/lib/commonjs/services/pushRegistration.js +12 -7
- package/lib/commonjs/services/pushRegistration.js.map +1 -1
- package/lib/commonjs/types/models/attachment.model.js +2 -0
- package/lib/commonjs/types/models/attachment.model.js.map +1 -0
- package/lib/commonjs/version.js +1 -1
- package/lib/module/assets/icons.js +1 -1
- package/lib/module/assets/icons.js.map +1 -1
- package/lib/module/components/MainComponents/AlbumGrid.js +146 -0
- package/lib/module/components/MainComponents/AlbumGrid.js.map +1 -0
- package/lib/module/components/MainComponents/ChatRoom.js +2 -2
- package/lib/module/components/MainComponents/ChatRoom.js.map +1 -1
- package/lib/module/components/MainComponents/MediaMessage.js +200 -124
- package/lib/module/components/MainComponents/MediaMessage.js.map +1 -1
- package/lib/module/components/MainComponents/MediaUploadOverlay.js +104 -0
- package/lib/module/components/MainComponents/MediaUploadOverlay.js.map +1 -0
- package/lib/module/components/MessageBubble/MediaCaption.js +46 -0
- package/lib/module/components/MessageBubble/MediaCaption.js.map +1 -0
- package/lib/module/components/MessageBubble/Message.js +44 -5
- package/lib/module/components/MessageBubble/Message.js.map +1 -1
- package/lib/module/components/MessageBubble/MessageInteractions.js +2 -1
- package/lib/module/components/MessageBubble/MessageInteractions.js.map +1 -1
- package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js +75 -0
- package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
- package/lib/module/components/RoomComponents/LastMessageItem.js +20 -4
- package/lib/module/components/RoomComponents/LastMessageItem.js.map +1 -1
- package/lib/module/components/RoomComponents/styled/LastAudioMessage.js +3 -2
- package/lib/module/components/RoomComponents/styled/LastAudioMessage.js.map +1 -1
- package/lib/module/components/RoomComponents/styled/LastMessageFile.js +3 -2
- package/lib/module/components/RoomComponents/styled/LastMessageFile.js.map +1 -1
- package/lib/module/components/RoomComponents/styled/LastMessagePhoto.js +3 -2
- package/lib/module/components/RoomComponents/styled/LastMessagePhoto.js.map +1 -1
- package/lib/module/components/RoomComponents/styled/LastMessageVideo.js +3 -2
- package/lib/module/components/RoomComponents/styled/LastMessageVideo.js.map +1 -1
- package/lib/module/components/Thread/ThreadWrapper.js +3 -2
- package/lib/module/components/Thread/ThreadWrapper.js.map +1 -1
- package/lib/module/components/styled/MediaFilePreview.js +61 -48
- package/lib/module/components/styled/MediaFilePreview.js.map +1 -1
- package/lib/module/components/styled/MessageImage.js +16 -6
- package/lib/module/components/styled/MessageImage.js.map +1 -1
- package/lib/module/components/styled/SendInput.js +99 -68
- package/lib/module/components/styled/SendInput.js.map +1 -1
- package/lib/module/components/styled/VideoMessage.js +24 -10
- package/lib/module/components/styled/VideoMessage.js.map +1 -1
- package/lib/module/context/xmppProvider.js +46 -0
- package/lib/module/context/xmppProvider.js.map +1 -1
- package/lib/module/e2ee/fileEnvelope.js +1 -1
- package/lib/module/e2ee/fileEnvelope.js.map +1 -1
- package/lib/module/e2ee/sealedBody.js +32 -2
- package/lib/module/e2ee/sealedBody.js.map +1 -1
- package/lib/module/helpers/albumLayout.js +143 -0
- package/lib/module/helpers/albumLayout.js.map +1 -0
- package/lib/module/helpers/attachments.js +245 -0
- package/lib/module/helpers/attachments.js.map +1 -0
- package/lib/module/helpers/caption.js +207 -0
- package/lib/module/helpers/caption.js.map +1 -0
- package/lib/module/helpers/createMainMessageForThread.js +5 -1
- package/lib/module/helpers/createMainMessageForThread.js.map +1 -1
- package/lib/module/helpers/createMessageFromXml.js +20 -6
- package/lib/module/helpers/createMessageFromXml.js.map +1 -1
- package/lib/module/helpers/fileKind.js +34 -0
- package/lib/module/helpers/fileKind.js.map +1 -0
- package/lib/module/helpers/getDataFromXml.js +6 -1
- package/lib/module/helpers/getDataFromXml.js.map +1 -1
- package/lib/module/helpers/mapWithConcurrency.js +37 -0
- package/lib/module/helpers/mapWithConcurrency.js.map +1 -0
- package/lib/module/helpers/mediaLimits.js +27 -0
- package/lib/module/helpers/mediaLimits.js.map +1 -0
- package/lib/module/helpers/mediaSendRetry.js +55 -0
- package/lib/module/helpers/mediaSendRetry.js.map +1 -0
- package/lib/module/helpers/mediaWireData.js +58 -0
- package/lib/module/helpers/mediaWireData.js.map +1 -0
- package/lib/module/helpers/mentionSpans.js +14 -0
- package/lib/module/helpers/mentionSpans.js.map +1 -0
- package/lib/module/helpers/mimeToExtension.js +43 -0
- package/lib/module/helpers/mimeToExtension.js.map +1 -1
- package/lib/module/helpers/readReceipts.js +160 -0
- package/lib/module/helpers/readReceipts.js.map +1 -0
- package/lib/module/helpers/repairFilenameEncoding.js +74 -0
- package/lib/module/helpers/repairFilenameEncoding.js.map +1 -0
- package/lib/module/helpers/safeJson.js +13 -0
- package/lib/module/helpers/safeJson.js.map +1 -0
- package/lib/module/helpers/secureStoreRuntime.js +10 -1
- package/lib/module/helpers/secureStoreRuntime.js.map +1 -1
- package/lib/module/hooks/useSendMessage.js +387 -195
- package/lib/module/hooks/useSendMessage.js.map +1 -1
- package/lib/module/i18n/strings.js +96 -0
- package/lib/module/i18n/strings.js.map +1 -1
- package/lib/module/main.js +6 -0
- package/lib/module/main.js.map +1 -1
- package/lib/module/networking/api-requests/auth.api.js +62 -3
- package/lib/module/networking/api-requests/auth.api.js.map +1 -1
- package/lib/module/networking/api-requests/push.api.js +21 -6
- package/lib/module/networking/api-requests/push.api.js.map +1 -1
- package/lib/module/networking/stanzaHandlers.js +97 -4
- package/lib/module/networking/stanzaHandlers.js.map +1 -1
- package/lib/module/networking/xmpp/handleStanzas.xmpp.js +5 -3
- package/lib/module/networking/xmpp/handleStanzas.xmpp.js.map +1 -1
- package/lib/module/networking/xmpp/sendDisplayedMarker.xmpp.js +25 -0
- package/lib/module/networking/xmpp/sendDisplayedMarker.xmpp.js.map +1 -0
- package/lib/module/networking/xmpp/sendMediaMessage.xmpp.js +53 -2
- package/lib/module/networking/xmpp/sendMediaMessage.xmpp.js.map +1 -1
- package/lib/module/networking/xmpp/sendReceivedReceipt.xmpp.js +25 -0
- package/lib/module/networking/xmpp/sendReceivedReceipt.xmpp.js.map +1 -0
- package/lib/module/networking/xmppClient.js +64 -0
- package/lib/module/networking/xmppClient.js.map +1 -1
- package/lib/module/push/pushKeyStore.js +159 -0
- package/lib/module/push/pushKeyStore.js.map +1 -0
- package/lib/module/push/sealedBox.js +97 -0
- package/lib/module/push/sealedBox.js.map +1 -0
- package/lib/module/push/sealedPush.js +117 -0
- package/lib/module/push/sealedPush.js.map +1 -0
- package/lib/module/push/sealedPushTask.js +187 -0
- package/lib/module/push/sealedPushTask.js.map +1 -0
- package/lib/module/roomStore/persistence.js +78 -5
- package/lib/module/roomStore/persistence.js.map +1 -1
- package/lib/module/roomStore/roomHeapSlice.js +4 -0
- package/lib/module/roomStore/roomHeapSlice.js.map +1 -1
- package/lib/module/roomStore/roomsSlice.js +109 -0
- package/lib/module/roomStore/roomsSlice.js.map +1 -1
- package/lib/module/services/pushRegistration.js +12 -7
- package/lib/module/services/pushRegistration.js.map +1 -1
- package/lib/module/types/models/attachment.model.js +2 -0
- package/lib/module/types/models/attachment.model.js.map +1 -0
- package/lib/module/version.js +1 -1
- package/lib/typescript/assets/icons.d.ts +6 -1
- package/lib/typescript/assets/icons.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/AlbumGrid.d.ts +21 -0
- package/lib/typescript/components/MainComponents/AlbumGrid.d.ts.map +1 -0
- package/lib/typescript/components/MainComponents/ChatRoom.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/MediaMessage.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/MediaUploadOverlay.d.ts +17 -0
- package/lib/typescript/components/MainComponents/MediaUploadOverlay.d.ts.map +1 -0
- package/lib/typescript/components/MessageBubble/MediaCaption.d.ts +12 -0
- package/lib/typescript/components/MessageBubble/MediaCaption.d.ts.map +1 -0
- package/lib/typescript/components/MessageBubble/Message.d.ts.map +1 -1
- package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts.map +1 -1
- package/lib/typescript/components/Modals/FilePreviewModal/FilePreviewModal.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/LastMessageItem.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/styled/LastAudioMessage.d.ts +2 -0
- package/lib/typescript/components/RoomComponents/styled/LastAudioMessage.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/styled/LastMessageFile.d.ts +2 -0
- package/lib/typescript/components/RoomComponents/styled/LastMessageFile.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/styled/LastMessagePhoto.d.ts +2 -0
- package/lib/typescript/components/RoomComponents/styled/LastMessagePhoto.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/styled/LastMessageVideo.d.ts +2 -0
- package/lib/typescript/components/RoomComponents/styled/LastMessageVideo.d.ts.map +1 -1
- package/lib/typescript/components/Thread/ThreadWrapper.d.ts.map +1 -1
- package/lib/typescript/components/styled/MediaFilePreview.d.ts.map +1 -1
- package/lib/typescript/components/styled/MessageImage.d.ts +10 -0
- package/lib/typescript/components/styled/MessageImage.d.ts.map +1 -1
- package/lib/typescript/components/styled/SendInput.d.ts +7 -1
- package/lib/typescript/components/styled/SendInput.d.ts.map +1 -1
- package/lib/typescript/components/styled/VideoMessage.d.ts +7 -0
- package/lib/typescript/components/styled/VideoMessage.d.ts.map +1 -1
- package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
- package/lib/typescript/e2ee/fileEnvelope.d.ts +1 -1
- package/lib/typescript/e2ee/fileEnvelope.d.ts.map +1 -1
- package/lib/typescript/e2ee/sealedBody.d.ts +16 -0
- package/lib/typescript/e2ee/sealedBody.d.ts.map +1 -1
- package/lib/typescript/helpers/albumLayout.d.ts +66 -0
- package/lib/typescript/helpers/albumLayout.d.ts.map +1 -0
- package/lib/typescript/helpers/attachments.d.ts +55 -0
- package/lib/typescript/helpers/attachments.d.ts.map +1 -0
- package/lib/typescript/helpers/caption.d.ts +64 -0
- package/lib/typescript/helpers/caption.d.ts.map +1 -0
- package/lib/typescript/helpers/createMainMessageForThread.d.ts.map +1 -1
- package/lib/typescript/helpers/createMessageFromXml.d.ts.map +1 -1
- package/lib/typescript/helpers/fileKind.d.ts +20 -0
- package/lib/typescript/helpers/fileKind.d.ts.map +1 -0
- package/lib/typescript/helpers/getDataFromXml.d.ts.map +1 -1
- package/lib/typescript/helpers/mapWithConcurrency.d.ts +9 -0
- package/lib/typescript/helpers/mapWithConcurrency.d.ts.map +1 -0
- package/lib/typescript/helpers/mediaLimits.d.ts +13 -0
- package/lib/typescript/helpers/mediaLimits.d.ts.map +1 -0
- package/lib/typescript/helpers/mediaSendRetry.d.ts +26 -0
- package/lib/typescript/helpers/mediaSendRetry.d.ts.map +1 -0
- package/lib/typescript/helpers/mediaWireData.d.ts +16 -0
- package/lib/typescript/helpers/mediaWireData.d.ts.map +1 -0
- package/lib/typescript/helpers/mentionSpans.d.ts +8 -0
- package/lib/typescript/helpers/mentionSpans.d.ts.map +1 -0
- package/lib/typescript/helpers/mimeToExtension.d.ts +7 -0
- package/lib/typescript/helpers/mimeToExtension.d.ts.map +1 -1
- package/lib/typescript/helpers/readReceipts.d.ts +82 -0
- package/lib/typescript/helpers/readReceipts.d.ts.map +1 -0
- package/lib/typescript/helpers/repairFilenameEncoding.d.ts +12 -0
- package/lib/typescript/helpers/repairFilenameEncoding.d.ts.map +1 -0
- package/lib/typescript/helpers/safeJson.d.ts +2 -0
- package/lib/typescript/helpers/safeJson.d.ts.map +1 -0
- package/lib/typescript/helpers/secureStoreRuntime.d.ts +17 -3
- package/lib/typescript/helpers/secureStoreRuntime.d.ts.map +1 -1
- package/lib/typescript/hooks/useSendMessage.d.ts +2 -1
- package/lib/typescript/hooks/useSendMessage.d.ts.map +1 -1
- package/lib/typescript/i18n/strings.d.ts.map +1 -1
- package/lib/typescript/main.d.ts +6 -0
- package/lib/typescript/main.d.ts.map +1 -1
- package/lib/typescript/networking/api-requests/auth.api.d.ts +2 -0
- package/lib/typescript/networking/api-requests/auth.api.d.ts.map +1 -1
- package/lib/typescript/networking/api-requests/push.api.d.ts +14 -2
- package/lib/typescript/networking/api-requests/push.api.d.ts.map +1 -1
- package/lib/typescript/networking/stanzaHandlers.d.ts +7 -3
- package/lib/typescript/networking/stanzaHandlers.d.ts.map +1 -1
- package/lib/typescript/networking/xmpp/handleStanzas.xmpp.d.ts.map +1 -1
- package/lib/typescript/networking/xmpp/sendDisplayedMarker.xmpp.d.ts +3 -0
- package/lib/typescript/networking/xmpp/sendDisplayedMarker.xmpp.d.ts.map +1 -0
- package/lib/typescript/networking/xmpp/sendMediaMessage.xmpp.d.ts +12 -0
- package/lib/typescript/networking/xmpp/sendMediaMessage.xmpp.d.ts.map +1 -1
- package/lib/typescript/networking/xmpp/sendReceivedReceipt.xmpp.d.ts +3 -0
- package/lib/typescript/networking/xmpp/sendReceivedReceipt.xmpp.d.ts.map +1 -0
- package/lib/typescript/networking/xmppClient.d.ts +15 -0
- package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
- package/lib/typescript/push/pushKeyStore.d.ts +35 -0
- package/lib/typescript/push/pushKeyStore.d.ts.map +1 -0
- package/lib/typescript/push/sealedBox.d.ts +24 -0
- package/lib/typescript/push/sealedBox.d.ts.map +1 -0
- package/lib/typescript/push/sealedPush.d.ts +61 -0
- package/lib/typescript/push/sealedPush.d.ts.map +1 -0
- package/lib/typescript/push/sealedPushTask.d.ts +33 -0
- package/lib/typescript/push/sealedPushTask.d.ts.map +1 -0
- package/lib/typescript/roomStore/persistence.d.ts.map +1 -1
- package/lib/typescript/roomStore/roomHeapSlice.d.ts +22 -1
- package/lib/typescript/roomStore/roomHeapSlice.d.ts.map +1 -1
- package/lib/typescript/roomStore/roomsSlice.d.ts +67 -3
- package/lib/typescript/roomStore/roomsSlice.d.ts.map +1 -1
- package/lib/typescript/services/pushRegistration.d.ts +8 -0
- package/lib/typescript/services/pushRegistration.d.ts.map +1 -1
- package/lib/typescript/types/models/attachment.model.d.ts +52 -0
- package/lib/typescript/types/models/attachment.model.d.ts.map +1 -0
- package/lib/typescript/types/models/message.model.d.ts +26 -0
- package/lib/typescript/types/models/message.model.d.ts.map +1 -1
- package/lib/typescript/types/models/modal.model.d.ts +7 -0
- package/lib/typescript/types/models/modal.model.d.ts.map +1 -1
- package/lib/typescript/types/models/room.model.d.ts +2 -0
- package/lib/typescript/types/models/room.model.d.ts.map +1 -1
- package/lib/typescript/types/types.d.ts +35 -1
- package/lib/typescript/types/types.d.ts.map +1 -1
- package/lib/typescript/version.d.ts +1 -1
- package/package.json +14 -1
- package/plugins/notification-service/Info.plist +31 -0
- package/plugins/notification-service/NotificationService.swift +195 -0
- package/plugins/notification-service/SealedBox.swift +355 -0
- package/plugins/notification-service/check/gen-vectors.mjs +24 -0
- package/plugins/notification-service/check/main.swift +37 -0
- package/plugins/notification-service/check/run.sh +12 -0
- package/plugins/notification-service/check/vectors.json +1 -0
- package/plugins/withNotificationServiceExtension.js +189 -0
- package/src/assets/icons.tsx +20 -2
- package/src/components/MainComponents/AlbumGrid.tsx +183 -0
- package/src/components/MainComponents/ChatRoom.tsx +4 -2
- package/src/components/MainComponents/MediaMessage.tsx +246 -130
- package/src/components/MainComponents/MediaUploadOverlay.tsx +102 -0
- package/src/components/MessageBubble/MediaCaption.tsx +45 -0
- package/src/components/MessageBubble/Message.tsx +71 -3
- package/src/components/MessageBubble/MessageInteractions.tsx +2 -1
- package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +64 -0
- package/src/components/RoomComponents/LastMessageItem.tsx +15 -4
- package/src/components/RoomComponents/styled/LastAudioMessage.tsx +6 -3
- package/src/components/RoomComponents/styled/LastMessageFile.tsx +6 -2
- package/src/components/RoomComponents/styled/LastMessagePhoto.tsx +6 -2
- package/src/components/RoomComponents/styled/LastMessageVideo.tsx +6 -2
- package/src/components/Thread/ThreadWrapper.tsx +5 -9
- package/src/components/styled/MediaFilePreview.tsx +49 -34
- package/src/components/styled/MessageImage.tsx +21 -5
- package/src/components/styled/SendInput.tsx +101 -87
- package/src/components/styled/VideoMessage.tsx +34 -9
- package/src/context/xmppProvider.tsx +32 -0
- package/src/e2ee/fileEnvelope.ts +1 -1
- package/src/e2ee/sealedBody.ts +44 -5
- package/src/helpers/albumLayout.ts +207 -0
- package/src/helpers/attachments.ts +316 -0
- package/src/helpers/caption.ts +283 -0
- package/src/helpers/createMainMessageForThread.ts +6 -1
- package/src/helpers/createMessageFromXml.ts +21 -5
- package/src/helpers/fileKind.ts +38 -0
- package/src/helpers/getDataFromXml.ts +5 -1
- package/src/helpers/mapWithConcurrency.ts +32 -0
- package/src/helpers/mediaLimits.ts +27 -0
- package/src/helpers/mediaSendRetry.ts +59 -0
- package/src/helpers/mediaWireData.ts +57 -0
- package/src/helpers/mentionSpans.ts +17 -0
- package/src/helpers/mimeToExtension.ts +51 -0
- package/src/helpers/readReceipts.ts +219 -0
- package/src/helpers/repairFilenameEncoding.ts +82 -0
- package/src/helpers/safeJson.ts +11 -0
- package/src/helpers/secureStoreRuntime.ts +21 -3
- package/src/hooks/useSendMessage.tsx +448 -249
- package/src/i18n/strings.ts +96 -0
- package/src/main.ts +30 -0
- package/src/networking/api-requests/auth.api.ts +60 -3
- package/src/networking/api-requests/push.api.ts +29 -4
- package/src/networking/stanzaHandlers.ts +91 -2
- package/src/networking/xmpp/handleStanzas.xmpp.ts +6 -2
- package/src/networking/xmpp/sendDisplayedMarker.xmpp.ts +31 -0
- package/src/networking/xmpp/sendMediaMessage.xmpp.ts +60 -3
- package/src/networking/xmpp/sendReceivedReceipt.xmpp.ts +31 -0
- package/src/networking/xmppClient.ts +48 -0
- package/src/push/pushKeyStore.ts +163 -0
- package/src/push/sealedBox.ts +104 -0
- package/src/push/sealedPush.ts +127 -0
- package/src/push/sealedPushTask.ts +175 -0
- package/src/roomStore/persistence.ts +94 -3
- package/src/roomStore/roomHeapSlice.ts +23 -1
- package/src/roomStore/roomsSlice.ts +112 -1
- package/src/services/pushRegistration.ts +25 -5
- package/src/types/models/attachment.model.ts +51 -0
- package/src/types/models/message.model.ts +29 -0
- package/src/types/models/modal.model.ts +7 -0
- package/src/types/models/room.model.ts +2 -0
- package/src/types/types.ts +41 -1
- package/src/version.ts +1 -1
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { hsalsa, xsalsa20poly1305 } from '@noble/ciphers/salsa.js';
|
|
4
|
+
import { clean, concatBytes, u32, u8, utf8ToBytes } from '@noble/ciphers/utils.js';
|
|
5
|
+
import { x25519 } from '@noble/curves/ed25519.js';
|
|
6
|
+
import { blake2b } from '@noble/hashes/blake2.js';
|
|
7
|
+
import { sha256 } from '@noble/hashes/sha2.js';
|
|
8
|
+
import { bytesToHex } from '@noble/hashes/utils.js';
|
|
9
|
+
|
|
10
|
+
// libsodium's sealed box (`crypto_box_seal` / `crypto_box_seal_open`) on
|
|
11
|
+
// the noble primitives the e2ee code already ships, so the JS side needs
|
|
12
|
+
// no native libsodium. The push service seals every chat push for a
|
|
13
|
+
// device that registered an X25519 public key (ethora-backend,
|
|
14
|
+
// services/push/README.md, "Encrypted pushes"); this is the matching
|
|
15
|
+
// receiver. The iOS notification service extension decrypts the same
|
|
16
|
+
// boxes in Swift with libsodium (plugins/notification-service).
|
|
17
|
+
//
|
|
18
|
+
// Layout: ephemeral public key (32) || crypto_box(message, nonce,
|
|
19
|
+
// recipient pk, ephemeral sk), with crypto_box = XSalsa20-Poly1305 under
|
|
20
|
+
// HSalsa20(X25519 shared secret) and nonce = BLAKE2b-192(epk || rpk).
|
|
21
|
+
// `__tests__/sealedBox.test.ts` pins this against libsodium itself.
|
|
22
|
+
|
|
23
|
+
export const SEALED_BOX_PUBLIC_KEY_BYTES = 32;
|
|
24
|
+
export const SEALED_BOX_SECRET_KEY_BYTES = 32;
|
|
25
|
+
/** Ephemeral public key (32) + Poly1305 tag (16). */
|
|
26
|
+
export const SEALED_BOX_OVERHEAD_BYTES = 48;
|
|
27
|
+
const NONCE_BYTES = 24;
|
|
28
|
+
const SIGMA = u32(utf8ToBytes('expand 32-byte k'));
|
|
29
|
+
const ZERO_NONCE_PREFIX = new Uint32Array(4);
|
|
30
|
+
export const generateSealedBoxKeyPair = () => {
|
|
31
|
+
const {
|
|
32
|
+
secretKey,
|
|
33
|
+
publicKey
|
|
34
|
+
} = x25519.keygen();
|
|
35
|
+
return {
|
|
36
|
+
publicKey,
|
|
37
|
+
secretKey
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
export const sealedBoxPublicKeyOf = secretKey => x25519.getPublicKey(secretKey);
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The key id the push service derives when the registration carries none:
|
|
44
|
+
* the first 16 hex characters of SHA-256 over the raw public key. Sent back
|
|
45
|
+
* as `kid` in every sealed push.
|
|
46
|
+
*/
|
|
47
|
+
export const sealedBoxKeyId = publicKey => bytesToHex(sha256(publicKey)).slice(0, 16);
|
|
48
|
+
|
|
49
|
+
// crypto_box_beforenm: HSalsa20 over the X25519 shared secret.
|
|
50
|
+
const boxSharedKey = (theirPublicKey, mySecretKey) => {
|
|
51
|
+
const shared = x25519.getSharedSecret(mySecretKey, theirPublicKey);
|
|
52
|
+
const words = new Uint32Array(8);
|
|
53
|
+
hsalsa(SIGMA, u32(shared), ZERO_NONCE_PREFIX, words);
|
|
54
|
+
clean(shared);
|
|
55
|
+
return u8(words);
|
|
56
|
+
};
|
|
57
|
+
const sealNonce = (ephemeralPublicKey, recipientPublicKey) => blake2b(concatBytes(ephemeralPublicKey, recipientPublicKey), {
|
|
58
|
+
dkLen: NONCE_BYTES
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
/** `crypto_box_seal`: used by the tests and the key tool, not by the app. */
|
|
62
|
+
export function sealedBoxSeal(message, recipientPublicKey) {
|
|
63
|
+
if (recipientPublicKey.length !== SEALED_BOX_PUBLIC_KEY_BYTES) {
|
|
64
|
+
throw new Error('sealedBoxSeal: the public key must be 32 bytes');
|
|
65
|
+
}
|
|
66
|
+
const ephemeral = generateSealedBoxKeyPair();
|
|
67
|
+
const key = boxSharedKey(recipientPublicKey, ephemeral.secretKey);
|
|
68
|
+
const nonce = sealNonce(ephemeral.publicKey, recipientPublicKey);
|
|
69
|
+
const box = xsalsa20poly1305(key, nonce).encrypt(message);
|
|
70
|
+
clean(key, ephemeral.secretKey);
|
|
71
|
+
return concatBytes(ephemeral.publicKey, box);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* `crypto_box_seal_open`. Returns null for anything that does not open:
|
|
76
|
+
* a wrong key, a box for another key, a damaged or truncated box.
|
|
77
|
+
*/
|
|
78
|
+
export function sealedBoxOpen(sealed, publicKey, secretKey) {
|
|
79
|
+
if (sealed.length < SEALED_BOX_OVERHEAD_BYTES || publicKey.length !== SEALED_BOX_PUBLIC_KEY_BYTES || secretKey.length !== SEALED_BOX_SECRET_KEY_BYTES) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
const ephemeralPublicKey = sealed.subarray(0, SEALED_BOX_PUBLIC_KEY_BYTES);
|
|
83
|
+
const box = sealed.subarray(SEALED_BOX_PUBLIC_KEY_BYTES);
|
|
84
|
+
let key = null;
|
|
85
|
+
try {
|
|
86
|
+
key = boxSharedKey(ephemeralPublicKey, secretKey);
|
|
87
|
+
const nonce = sealNonce(ephemeralPublicKey, publicKey);
|
|
88
|
+
return xsalsa20poly1305(key, nonce).decrypt(box);
|
|
89
|
+
} catch {
|
|
90
|
+
return null;
|
|
91
|
+
} finally {
|
|
92
|
+
if (key) {
|
|
93
|
+
clean(key);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=sealedBox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["hsalsa","xsalsa20poly1305","clean","concatBytes","u32","u8","utf8ToBytes","x25519","blake2b","sha256","bytesToHex","SEALED_BOX_PUBLIC_KEY_BYTES","SEALED_BOX_SECRET_KEY_BYTES","SEALED_BOX_OVERHEAD_BYTES","NONCE_BYTES","SIGMA","ZERO_NONCE_PREFIX","Uint32Array","generateSealedBoxKeyPair","secretKey","publicKey","keygen","sealedBoxPublicKeyOf","getPublicKey","sealedBoxKeyId","slice","boxSharedKey","theirPublicKey","mySecretKey","shared","getSharedSecret","words","sealNonce","ephemeralPublicKey","recipientPublicKey","dkLen","sealedBoxSeal","message","length","Error","ephemeral","key","nonce","box","encrypt","sealedBoxOpen","sealed","subarray","decrypt"],"sourceRoot":"../../../src","sources":["push/sealedBox.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { fromBase64 } from '../e2ee/crypto';
|
|
4
|
+
import { sealedBoxKeyId, sealedBoxOpen } from './sealedBox';
|
|
5
|
+
|
|
6
|
+
// The envelope of an encrypted chat push (ethora-backend,
|
|
7
|
+
// services/push/README.md, "Encrypted pushes"): the FCM `data` map on
|
|
8
|
+
// Android, the custom keys next to `aps` on iOS. Only ids travel in the
|
|
9
|
+
// clear; everything shown to the user is inside `enc`, a sealed box for
|
|
10
|
+
// the key the device registered with its token.
|
|
11
|
+
|
|
12
|
+
export const SEALED_PUSH_ALG = 'sealed-box-x25519';
|
|
13
|
+
export const SEALED_PUSH_VERSION = '1';
|
|
14
|
+
|
|
15
|
+
/** What the box holds: the notification the service would have sent in the clear. */
|
|
16
|
+
|
|
17
|
+
const str = value => typeof value === 'string' ? value : '';
|
|
18
|
+
|
|
19
|
+
/** The envelope of `data` when it is a sealed chat push, else null. */
|
|
20
|
+
export const sealedPushEnvelopeOf = data => {
|
|
21
|
+
if (!data || data.alg !== SEALED_PUSH_ALG) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const enc = str(data.enc);
|
|
25
|
+
return {
|
|
26
|
+
v: str(data.v) || SEALED_PUSH_VERSION,
|
|
27
|
+
alg: SEALED_PUSH_ALG,
|
|
28
|
+
kid: str(data.kid),
|
|
29
|
+
jid: str(data.jid),
|
|
30
|
+
msgID: str(data.msgID),
|
|
31
|
+
userJid: str(data.userJid),
|
|
32
|
+
...(enc ? {
|
|
33
|
+
enc
|
|
34
|
+
} : {})
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
export const isSealedPush = data => sealedPushEnvelopeOf(data) !== null;
|
|
38
|
+
const parseContent = plaintext => {
|
|
39
|
+
try {
|
|
40
|
+
const parsed = JSON.parse(new TextDecoder().decode(plaintext));
|
|
41
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
title: str(parsed.title),
|
|
46
|
+
body: str(parsed.body),
|
|
47
|
+
sender: str(parsed.sender),
|
|
48
|
+
senderJid: str(parsed.senderJid),
|
|
49
|
+
chat: str(parsed.chat),
|
|
50
|
+
chatJid: str(parsed.chatJid),
|
|
51
|
+
msgID: str(parsed.msgID),
|
|
52
|
+
image: str(parsed.image)
|
|
53
|
+
};
|
|
54
|
+
} catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Open a sealed push with the device key. `key` null means there is no key
|
|
60
|
+
* on this device. Never throws: every failure names why, so the caller can
|
|
61
|
+
* show the placeholder and log the reason.
|
|
62
|
+
*/
|
|
63
|
+
export function openSealedPush(envelope, key) {
|
|
64
|
+
if (!key) {
|
|
65
|
+
return {
|
|
66
|
+
ok: false,
|
|
67
|
+
reason: 'no-key',
|
|
68
|
+
envelope
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (envelope.kid && envelope.kid !== sealedBoxKeyId(key.publicKey)) {
|
|
72
|
+
return {
|
|
73
|
+
ok: false,
|
|
74
|
+
reason: 'key-mismatch',
|
|
75
|
+
envelope
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if (!envelope.enc) {
|
|
79
|
+
return {
|
|
80
|
+
ok: false,
|
|
81
|
+
reason: 'no-ciphertext',
|
|
82
|
+
envelope
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
let sealed;
|
|
86
|
+
try {
|
|
87
|
+
sealed = fromBase64(envelope.enc);
|
|
88
|
+
} catch {
|
|
89
|
+
return {
|
|
90
|
+
ok: false,
|
|
91
|
+
reason: 'bad-ciphertext',
|
|
92
|
+
envelope
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const plaintext = sealedBoxOpen(sealed, key.publicKey, key.secretKey);
|
|
96
|
+
if (!plaintext) {
|
|
97
|
+
return {
|
|
98
|
+
ok: false,
|
|
99
|
+
reason: 'bad-ciphertext',
|
|
100
|
+
envelope
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
const content = parseContent(plaintext);
|
|
104
|
+
if (!content) {
|
|
105
|
+
return {
|
|
106
|
+
ok: false,
|
|
107
|
+
reason: 'bad-content',
|
|
108
|
+
envelope
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
ok: true,
|
|
113
|
+
content,
|
|
114
|
+
envelope
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=sealedPush.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["fromBase64","sealedBoxKeyId","sealedBoxOpen","SEALED_PUSH_ALG","SEALED_PUSH_VERSION","str","value","sealedPushEnvelopeOf","data","alg","enc","v","kid","jid","msgID","userJid","isSealedPush","parseContent","plaintext","parsed","JSON","parse","TextDecoder","decode","title","body","sender","senderJid","chat","chatJid","image","openSealedPush","envelope","key","ok","reason","publicKey","sealed","secretKey","content"],"sourceRoot":"../../../src","sources":["push/sealedPush.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { AppState, Platform } from 'react-native';
|
|
4
|
+
import { pushLog } from '../utils/devLogger';
|
|
5
|
+
import { loadPushSecretKey } from './pushKeyStore';
|
|
6
|
+
import { openSealedPush, sealedPushEnvelopeOf } from './sealedPush';
|
|
7
|
+
|
|
8
|
+
// Android half of encrypted pushes. A sealed push is data-only, so the OS
|
|
9
|
+
// shows nothing by itself: expo-notifications hands it to a background
|
|
10
|
+
// task (expo-task-manager, foreground, background and killed app alike)
|
|
11
|
+
// and this task opens the box and posts the notification. iOS does the
|
|
12
|
+
// same in the notification service extension; here `registerSealedPushTask`
|
|
13
|
+
// is a no-op.
|
|
14
|
+
//
|
|
15
|
+
// Both packages are optional peers, required lazily: a host that does not
|
|
16
|
+
// want encrypted pushes never pays for them.
|
|
17
|
+
|
|
18
|
+
export const SEALED_PUSH_TASK = 'ETHORA_SEALED_PUSH';
|
|
19
|
+
const PLACEHOLDER_TITLE = 'New message';
|
|
20
|
+
let registered = false;
|
|
21
|
+
const loadTaskManager = () => {
|
|
22
|
+
try {
|
|
23
|
+
return require('expo-task-manager');
|
|
24
|
+
} catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const loadNotifications = () => {
|
|
29
|
+
try {
|
|
30
|
+
return require('expo-notifications');
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The FCM data map out of the task payload. expo-notifications passes the
|
|
38
|
+
* message as `{ notification, data }`; the data keys are on `data`
|
|
39
|
+
* itself, `data.dataString` carries the same as a JSON string on some
|
|
40
|
+
* versions, and a Firebase `RemoteMessage` keeps them under `data.data`.
|
|
41
|
+
*/
|
|
42
|
+
export const sealedPushDataOf = payload => {
|
|
43
|
+
const root = payload && typeof payload === 'object' ? payload : null;
|
|
44
|
+
if (!root) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const candidates = [root.data, root];
|
|
48
|
+
const data = root.data && typeof root.data === 'object' ? root.data : null;
|
|
49
|
+
if (data) {
|
|
50
|
+
if (typeof data.dataString === 'string') {
|
|
51
|
+
try {
|
|
52
|
+
candidates.unshift(JSON.parse(data.dataString));
|
|
53
|
+
} catch {
|
|
54
|
+
// not JSON — fall through to the map itself
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
candidates.push(data.data);
|
|
58
|
+
}
|
|
59
|
+
for (const candidate of candidates) {
|
|
60
|
+
if (sealedPushEnvelopeOf(candidate)) {
|
|
61
|
+
return candidate;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
};
|
|
66
|
+
const notificationContent = (result, options) => {
|
|
67
|
+
const {
|
|
68
|
+
envelope
|
|
69
|
+
} = result;
|
|
70
|
+
// What the tap handler (`handlePushPayload` through the host's response
|
|
71
|
+
// listener) needs: the same ids a plain push carries on `data`.
|
|
72
|
+
const data = {
|
|
73
|
+
jid: envelope.jid,
|
|
74
|
+
msgID: envelope.msgID,
|
|
75
|
+
userJid: envelope.userJid,
|
|
76
|
+
sealed: true
|
|
77
|
+
};
|
|
78
|
+
if (!result.ok) {
|
|
79
|
+
return {
|
|
80
|
+
title: options.placeholderTitle || PLACEHOLDER_TITLE,
|
|
81
|
+
...(options.placeholderBody ? {
|
|
82
|
+
body: options.placeholderBody
|
|
83
|
+
} : {}),
|
|
84
|
+
data,
|
|
85
|
+
sound: 'default'
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const {
|
|
89
|
+
content
|
|
90
|
+
} = result;
|
|
91
|
+
return {
|
|
92
|
+
title: content.title || content.sender || content.chat || PLACEHOLDER_TITLE,
|
|
93
|
+
body: content.body,
|
|
94
|
+
data: {
|
|
95
|
+
...data,
|
|
96
|
+
chatJid: content.chatJid,
|
|
97
|
+
senderJid: content.senderJid
|
|
98
|
+
},
|
|
99
|
+
sound: 'default'
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Open a sealed push and show it. One notification per chat: the room JID
|
|
105
|
+
* is the identifier, so a newer message replaces the older one the way the
|
|
106
|
+
* service's collapse key does for plain pushes.
|
|
107
|
+
*/
|
|
108
|
+
export async function presentSealedPush(data, options = {}) {
|
|
109
|
+
const envelope = sealedPushEnvelopeOf(data);
|
|
110
|
+
if (!envelope) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
const notifications = loadNotifications();
|
|
114
|
+
if (!notifications?.scheduleNotificationAsync) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
const result = openSealedPush(envelope, await loadPushSecretKey());
|
|
118
|
+
// console, not only pushLog: with the app killed this runs headless and
|
|
119
|
+
// `adb logcat -s ReactNativeJS` is the only place to see it.
|
|
120
|
+
if (result.ok) {
|
|
121
|
+
console.log(`[push] sealed push kid=${envelope.kid} opened for ${envelope.jid}`);
|
|
122
|
+
} else {
|
|
123
|
+
console.warn(`[push] sealed push kid=${envelope.kid} not opened (${result.reason}), showing the placeholder`);
|
|
124
|
+
pushLog('warn', `push: sealed push not opened (${result.reason}), showing the placeholder`);
|
|
125
|
+
}
|
|
126
|
+
await notifications.scheduleNotificationAsync({
|
|
127
|
+
identifier: envelope.jid || envelope.msgID || undefined,
|
|
128
|
+
content: notificationContent(result, options),
|
|
129
|
+
trigger: options.channelId ? {
|
|
130
|
+
channelId: options.channelId
|
|
131
|
+
} : null
|
|
132
|
+
});
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Define and register the background task that renders sealed pushes on
|
|
138
|
+
* Android. Call it at module scope of a file the app loads first (the
|
|
139
|
+
* entry file, or the root layout with expo-router): a killed app is
|
|
140
|
+
* started headless to run it, and the task must exist by then. No-op on
|
|
141
|
+
* iOS and when expo-task-manager / expo-notifications are not installed.
|
|
142
|
+
*/
|
|
143
|
+
export function registerSealedPushTask(options = {}) {
|
|
144
|
+
if (Platform.OS !== 'android' || registered) {
|
|
145
|
+
return registered;
|
|
146
|
+
}
|
|
147
|
+
const taskManager = loadTaskManager();
|
|
148
|
+
const notifications = loadNotifications();
|
|
149
|
+
if (!taskManager?.defineTask || !notifications?.registerTaskAsync) {
|
|
150
|
+
pushLog('warn', 'push: expo-task-manager and expo-notifications are needed to show encrypted pushes on Android');
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
taskManager.defineTask(SEALED_PUSH_TASK, async ({
|
|
154
|
+
data,
|
|
155
|
+
error
|
|
156
|
+
}) => {
|
|
157
|
+
if (error) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const map = sealedPushDataOf(data);
|
|
161
|
+
if (!map) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
// In the foreground the room is live over XMPP; the host's
|
|
165
|
+
// notification handler decides about plain pushes there too.
|
|
166
|
+
if (AppState.currentState === 'active') {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
await presentSealedPush(map, options);
|
|
171
|
+
} catch (e) {
|
|
172
|
+
pushLog('warn', 'push: showing a sealed push failed', e);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
registered = true;
|
|
176
|
+
notifications.registerTaskAsync(SEALED_PUSH_TASK).catch(e => {
|
|
177
|
+
registered = false;
|
|
178
|
+
pushLog('warn', 'push: registering the sealed push task failed', e);
|
|
179
|
+
});
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Test seam only. */
|
|
184
|
+
export const __resetSealedPushTaskForTests = () => {
|
|
185
|
+
registered = false;
|
|
186
|
+
};
|
|
187
|
+
//# sourceMappingURL=sealedPushTask.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["AppState","Platform","pushLog","loadPushSecretKey","openSealedPush","sealedPushEnvelopeOf","SEALED_PUSH_TASK","PLACEHOLDER_TITLE","registered","loadTaskManager","require","loadNotifications","sealedPushDataOf","payload","root","candidates","data","dataString","unshift","JSON","parse","push","candidate","notificationContent","result","options","envelope","jid","msgID","userJid","sealed","ok","title","placeholderTitle","placeholderBody","body","sound","content","sender","chat","chatJid","senderJid","presentSealedPush","notifications","scheduleNotificationAsync","console","log","kid","warn","reason","identifier","undefined","trigger","channelId","registerSealedPushTask","OS","taskManager","defineTask","registerTaskAsync","error","map","currentState","e","catch","__resetSealedPushTaskForTests"],"sourceRoot":"../../../src","sources":["push/sealedPushTask.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -65,7 +65,10 @@ const sanitizeUser = user => {
|
|
|
65
65
|
// on reload before MAM catches up. That needs the fields the bubbles and
|
|
66
66
|
// room-list previews actually read, nothing else. Everything outside this
|
|
67
67
|
// list is either re-derived on render or re-fetched from the server.
|
|
68
|
-
const PERSISTED_MESSAGE_FIELDS = ['id', 'xmppId', 'xmppFrom', 'body', 'date', 'timestamp', 'roomJid', 'isSystemMessage', 'isMediafile', 'isDeleted', 'isEdited', 'isReply', 'showInChannel', 'mainMessage', 'mimetype', 'location', 'locationPreview', 'fileName', 'originalName', 'size',
|
|
68
|
+
const PERSISTED_MESSAGE_FIELDS = ['id', 'xmppId', 'xmppFrom', 'body', 'date', 'timestamp', 'roomJid', 'isSystemMessage', 'isMediafile', 'isDeleted', 'isEdited', 'isReply', 'showInChannel', 'mainMessage', 'mimetype', 'location', 'locationPreview', 'fileName', 'originalName', 'size',
|
|
69
|
+
// Every file of a multi-attach message and its caption: without them a
|
|
70
|
+
// restored album paints as its first file only (see compactAttachments).
|
|
71
|
+
'attachments', 'caption', 'captionMentions', 'langSource',
|
|
69
72
|
// `translations` MUST be here. A previous version of this list dropped
|
|
70
73
|
// it, on the theory that it mirrored the web SDK's persist contract —
|
|
71
74
|
// it doesn't: web's redux-persist config (web/src/roomStore/index.ts)
|
|
@@ -131,8 +134,27 @@ const pickDefined = (source, keys) => {
|
|
|
131
134
|
}
|
|
132
135
|
return out;
|
|
133
136
|
};
|
|
137
|
+
|
|
138
|
+
// What a restored attachment is rendered from. Ownership and timestamp
|
|
139
|
+
// fields the storage returns, and the sender's local upload state, are not
|
|
140
|
+
// ours to cache and would repeat across up to ten files of every message.
|
|
141
|
+
const PERSISTED_ATTACHMENT_FIELDS = ['attachmentId', 'location', 'locationPreview', 'mimetype', 'originalName', 'fileName', 'size', 'duration', 'width', 'height'];
|
|
142
|
+
const compactAttachments = attachments => {
|
|
143
|
+
if (!Array.isArray(attachments) || attachments.length === 0) {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
return attachments.filter(item => item && typeof item === 'object').map(item => pickDefined(item, PERSISTED_ATTACHMENT_FIELDS));
|
|
147
|
+
};
|
|
134
148
|
const compactMessageForPersist = message => {
|
|
135
149
|
const compact = pickDefined(message, PERSISTED_MESSAGE_FIELDS);
|
|
150
|
+
if (compact.attachments !== undefined) {
|
|
151
|
+
const attachments = compactAttachments(compact.attachments);
|
|
152
|
+
if (attachments) {
|
|
153
|
+
compact.attachments = attachments;
|
|
154
|
+
} else {
|
|
155
|
+
delete compact.attachments;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
136
158
|
const user = message?.user;
|
|
137
159
|
if (user) {
|
|
138
160
|
compact.user = pickDefined(user, PERSISTED_MESSAGE_USER_FIELDS);
|
|
@@ -150,11 +172,14 @@ const sanitizeMessages = messages => {
|
|
|
150
172
|
/**
|
|
151
173
|
* Room fields that describe ONE session and must never be written to disk:
|
|
152
174
|
* the server's unread snapshot (a moment-in-time number the next /chats/my
|
|
153
|
-
* re-seeds). Volatile slice state that is not on the room at all (jumpWindow,
|
|
175
|
+
* re-seeds) and the read watermarks other occupants sent this session. Volatile slice state that is not on the room at all (jumpWindow,
|
|
154
176
|
* pendingJump, archivedMessage, usersSet) is never persisted because only
|
|
155
177
|
* `rooms.rooms` is picked from the slice.
|
|
156
178
|
*/
|
|
157
|
-
const SESSION_ROOM_FIELDS = ['apiUnreadCount', 'apiUnreadSeededAt'
|
|
179
|
+
const SESSION_ROOM_FIELDS = ['apiUnreadCount', 'apiUnreadSeededAt',
|
|
180
|
+
// XEP-0333 read watermarks: a peer's "seen" state is re-learned from the
|
|
181
|
+
// live marker stanzas, never restored from a previous session.
|
|
182
|
+
'lastDisplayedByUser'];
|
|
158
183
|
|
|
159
184
|
/**
|
|
160
185
|
* Rehydrate step: a persisted room must be refetched once per session, so
|
|
@@ -250,6 +275,27 @@ const sanitizeMediaData = data => {
|
|
|
250
275
|
}
|
|
251
276
|
return out;
|
|
252
277
|
};
|
|
278
|
+
|
|
279
|
+
// Every file of a multi-file failed send, trimmed the same way as `data`.
|
|
280
|
+
// Capped so a corrupt payload cannot grow the cache without bound.
|
|
281
|
+
const MAX_PERSISTED_FAILED_FILES = 10;
|
|
282
|
+
const sanitizeMediaFiles = files => {
|
|
283
|
+
if (!Array.isArray(files)) {
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
286
|
+
const out = [];
|
|
287
|
+
for (const file of files.slice(0, MAX_PERSISTED_FAILED_FILES)) {
|
|
288
|
+
const clean = sanitizeMediaData(file);
|
|
289
|
+
if (typeof file?.duration === 'number') {
|
|
290
|
+
clean.duration = file.duration;
|
|
291
|
+
}
|
|
292
|
+
if (typeof file?.waveForm === 'string') {
|
|
293
|
+
clean.waveForm = file.waveForm;
|
|
294
|
+
}
|
|
295
|
+
out.push(clean);
|
|
296
|
+
}
|
|
297
|
+
return out.length > 0 ? out : undefined;
|
|
298
|
+
};
|
|
253
299
|
const sanitizeFailedMessagePayload = payload => {
|
|
254
300
|
if (!payload || typeof payload !== 'object') {
|
|
255
301
|
return null;
|
|
@@ -261,6 +307,8 @@ const sanitizeFailedMessagePayload = payload => {
|
|
|
261
307
|
return null;
|
|
262
308
|
}
|
|
263
309
|
if (payload.kind === 'media') {
|
|
310
|
+
const files = sanitizeMediaFiles(payload.files);
|
|
311
|
+
const caption = typeof payload.caption === 'string' ? payload.caption : undefined;
|
|
264
312
|
return {
|
|
265
313
|
kind: 'media',
|
|
266
314
|
id: payload.id,
|
|
@@ -269,7 +317,16 @@ const sanitizeFailedMessagePayload = payload => {
|
|
|
269
317
|
type: typeof payload.type === 'string' ? payload.type : '',
|
|
270
318
|
isReply: payload.isReply,
|
|
271
319
|
isChecked: payload.isChecked,
|
|
272
|
-
mainMessage: payload.mainMessage
|
|
320
|
+
mainMessage: payload.mainMessage,
|
|
321
|
+
...(files ? {
|
|
322
|
+
files
|
|
323
|
+
} : {}),
|
|
324
|
+
...(caption ? {
|
|
325
|
+
caption
|
|
326
|
+
} : {}),
|
|
327
|
+
...(caption && Array.isArray(payload.captionMentions) ? {
|
|
328
|
+
captionMentions: payload.captionMentions
|
|
329
|
+
} : {})
|
|
273
330
|
};
|
|
274
331
|
}
|
|
275
332
|
if (payload.kind === 'text') {
|
|
@@ -328,6 +385,16 @@ export function reconstructFailedMessagePayload(message, roomJID) {
|
|
|
328
385
|
const isChecked = message.showInChannel === 'true';
|
|
329
386
|
const mainMessage = message.mainMessage;
|
|
330
387
|
if (message.isMediafile === 'true') {
|
|
388
|
+
// A multi-file send keeps every file. Only a file still on this device
|
|
389
|
+
// has a uri to replay; an uploaded one has no local copy left to send.
|
|
390
|
+
const localUri = value => typeof value === 'string' && /^(file|content|ph|assets-library):\/\//.test(value) ? value : undefined;
|
|
391
|
+
const attachments = Array.isArray(message.attachments) ? message.attachments : [];
|
|
392
|
+
const files = attachments.length > 1 ? attachments.slice(0, MAX_PERSISTED_FAILED_FILES).map(attachment => ({
|
|
393
|
+
uri: localUri(attachment.localUrl) ?? localUri(attachment.location),
|
|
394
|
+
name: attachment.originalName || attachment.fileName,
|
|
395
|
+
type: attachment.mimetype,
|
|
396
|
+
size: attachment.size !== undefined && attachment.size !== null ? Number(attachment.size) : undefined
|
|
397
|
+
})) : undefined;
|
|
331
398
|
return {
|
|
332
399
|
kind: 'media',
|
|
333
400
|
id: message.id,
|
|
@@ -341,7 +408,13 @@ export function reconstructFailedMessagePayload(message, roomJID) {
|
|
|
341
408
|
type: message.mimetype || '',
|
|
342
409
|
isReply,
|
|
343
410
|
isChecked,
|
|
344
|
-
mainMessage
|
|
411
|
+
mainMessage,
|
|
412
|
+
...(files ? {
|
|
413
|
+
files
|
|
414
|
+
} : {}),
|
|
415
|
+
...(message.caption ? {
|
|
416
|
+
caption: message.caption
|
|
417
|
+
} : {})
|
|
345
418
|
};
|
|
346
419
|
}
|
|
347
420
|
return {
|