@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
|
@@ -36,6 +36,13 @@ interface CustomMessageImageProps {
|
|
|
36
36
|
* route; rendering it inline is what left the bubbles blank.
|
|
37
37
|
*/
|
|
38
38
|
locationPreview?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Album mode: fill a tile of exactly this size (cover, square corners; the
|
|
41
|
+
* tile clips) instead of sizing itself to the picture.
|
|
42
|
+
*/
|
|
43
|
+
tile?: { width: number; height: number };
|
|
44
|
+
/** Replaces the default "open this one file" tap, e.g. to open an album at an index. */
|
|
45
|
+
onOpen?: () => void;
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
/** Keep the personal file token out of the logs. */
|
|
@@ -51,6 +58,8 @@ const CustomMessageImage: React.FC<CustomMessageImageProps> = ({
|
|
|
51
58
|
originalName,
|
|
52
59
|
mimetype,
|
|
53
60
|
locationPreview,
|
|
61
|
+
tile,
|
|
62
|
+
onOpen,
|
|
54
63
|
}) => {
|
|
55
64
|
// Preview first, original only as a fallback — mirrors the web client,
|
|
56
65
|
// where the <img> src is the preview and `fileURL` is reserved for the
|
|
@@ -59,12 +68,13 @@ const CustomMessageImage: React.FC<CustomMessageImageProps> = ({
|
|
|
59
68
|
const [loading, setLoading] = useState(true);
|
|
60
69
|
// Start with a neutral box; swap to the real aspect ratio once the
|
|
61
70
|
// image's natural dimensions are known so it's shown scaled, not cropped.
|
|
62
|
-
const [
|
|
71
|
+
const [measured, setDims] = useState<MediaDims>(defaultMediaDims());
|
|
72
|
+
const dims: MediaDims = tile ?? measured;
|
|
63
73
|
|
|
64
74
|
const dispatch = useDispatch();
|
|
65
75
|
|
|
66
76
|
useEffect(() => {
|
|
67
|
-
if (!displayURL) {
|
|
77
|
+
if (!displayURL || tile) {
|
|
68
78
|
return;
|
|
69
79
|
}
|
|
70
80
|
let active = true;
|
|
@@ -88,15 +98,21 @@ const CustomMessageImage: React.FC<CustomMessageImageProps> = ({
|
|
|
88
98
|
return () => {
|
|
89
99
|
active = false;
|
|
90
100
|
};
|
|
91
|
-
}, [displayURL]);
|
|
101
|
+
}, [displayURL, tile]);
|
|
92
102
|
|
|
93
103
|
const handleOpen = () => {
|
|
104
|
+
if (onOpen) {
|
|
105
|
+
onOpen();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
94
108
|
dispatch(setActiveFile({ fileName, fileURL, mimetype, originalName }));
|
|
95
109
|
dispatch(setActiveModal(MODAL_TYPES.FILE_PREVIEW));
|
|
96
110
|
};
|
|
97
111
|
|
|
98
112
|
return (
|
|
99
|
-
<Container
|
|
113
|
+
<Container
|
|
114
|
+
style={tile ? { width: dims.width, borderRadius: 0 } : { width: dims.width }}
|
|
115
|
+
>
|
|
100
116
|
<TouchableOpacity onPress={handleOpen} activeOpacity={0.9}>
|
|
101
117
|
{loading && (
|
|
102
118
|
<View
|
|
@@ -117,7 +133,7 @@ const CustomMessageImage: React.FC<CustomMessageImageProps> = ({
|
|
|
117
133
|
style={{
|
|
118
134
|
width: dims.width,
|
|
119
135
|
height: dims.height,
|
|
120
|
-
borderRadius: 10,
|
|
136
|
+
borderRadius: tile ? 0 : 10,
|
|
121
137
|
backgroundColor: 'rgba(0, 0, 0, 0)',
|
|
122
138
|
}}
|
|
123
139
|
onLoadEnd={() => setLoading(false)}
|
|
@@ -17,6 +17,7 @@ import { getElementFont } from '../../helpers/getElementFont';
|
|
|
17
17
|
import { useTheme } from '../../hooks/useTheme';
|
|
18
18
|
import { useT } from '../../i18n/useT';
|
|
19
19
|
import { getMediaLibrary } from '../../helpers/mediaLibraryRuntime';
|
|
20
|
+
import { mediaFileCap, mediaSizeLimit } from '../../helpers/mediaLimits';
|
|
20
21
|
import * as ImagePicker from 'expo-image-picker';
|
|
21
22
|
import * as DocumentPicker from 'expo-document-picker';
|
|
22
23
|
import * as ImageManipulator from 'expo-image-manipulator';
|
|
@@ -44,14 +45,6 @@ import {
|
|
|
44
45
|
// receivers draw it without downloading and decoding the clip first.
|
|
45
46
|
const VOICE_PRESET = { ...RecordingPresets.HIGH_QUALITY, isMeteringEnabled: true };
|
|
46
47
|
|
|
47
|
-
// iOS photos default to HEIC; many web backends (incl. ours) 500 on
|
|
48
|
-
// HEIC uploads because they can't decode it. Convert to JPEG before
|
|
49
|
-
// upload — JPEG is universally supported. JPEGs pass through.
|
|
50
|
-
// Server rejects uploads over its body-size cap with HTTP 413 (Request
|
|
51
|
-
// Entity Too Large). Guard client-side so the user gets a clear message
|
|
52
|
-
// instead of a silent failed bubble. 50 MB matches the backend limit.
|
|
53
|
-
const MAX_MEDIA_BYTES = 50 * 1024 * 1024;
|
|
54
|
-
|
|
55
48
|
const formatBytes = (bytes: number): string => {
|
|
56
49
|
if (bytes >= 1024 * 1024) {
|
|
57
50
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
@@ -89,7 +82,11 @@ interface SendInputProps {
|
|
|
89
82
|
sendMessage: (message: string) => void;
|
|
90
83
|
isLoading: boolean;
|
|
91
84
|
editMessage?: string;
|
|
92
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Sends one message carrying `data` (a file or the picked list). The typed
|
|
87
|
+
* text, when there is any, goes with it as the caption.
|
|
88
|
+
*/
|
|
89
|
+
sendMedia: (data: any, type: string, caption?: { text: string }) => unknown;
|
|
93
90
|
config?: IConfig;
|
|
94
91
|
onFocus?: () => void;
|
|
95
92
|
onBlur?: () => void;
|
|
@@ -164,25 +161,64 @@ const SendInput: React.FC<SendInputProps> = ({
|
|
|
164
161
|
// "Document" row or a dev-mode double-run of the sheet's close callback.
|
|
165
162
|
const pickingDocRef = useRef(false);
|
|
166
163
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
164
|
+
/**
|
|
165
|
+
* Adds picked files to the tray. The tray is a list, not a slot: a second
|
|
166
|
+
* pick or a camera shot appends. Enforces the per-message file cap and the
|
|
167
|
+
* per-file size cap before anything reaches the network; the size cap is
|
|
168
|
+
* the upload limit (50 MB, or the sealed-file limit in an e2ee room),
|
|
169
|
+
* lowered by `config.attachments.maxSizeMB`. The backend rejects oversized
|
|
170
|
+
* bodies with HTTP 413, so catching them here gives a clear message.
|
|
171
|
+
*/
|
|
172
|
+
const handleFileSelect = (incoming: MediaFile[]) => {
|
|
173
|
+
if (incoming.length === 0) {return;}
|
|
174
|
+
const limit = mediaSizeLimit(config, maxMediaBytes);
|
|
175
|
+
const fitting = incoming.filter((file) => {
|
|
176
|
+
if (typeof file.size === 'number' && file.size > limit) {
|
|
177
|
+
Alert.alert(
|
|
178
|
+
t('attachment.tooLarge', {
|
|
179
|
+
name: file.name,
|
|
180
|
+
max: formatBytes(limit),
|
|
181
|
+
})
|
|
182
|
+
);
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
return true;
|
|
186
|
+
});
|
|
187
|
+
const cap = mediaFileCap(config);
|
|
188
|
+
const room = cap - filePreviewsRef.current.length;
|
|
189
|
+
if (fitting.length === 0) {return;}
|
|
190
|
+
if (room <= 0 || fitting.length > room) {
|
|
191
|
+
Alert.alert(t('attachment.limit', { count: cap }));
|
|
183
192
|
}
|
|
184
|
-
|
|
185
|
-
|
|
193
|
+
const accepted = fitting.slice(0, Math.max(0, room));
|
|
194
|
+
if (accepted.length === 0) {return;}
|
|
195
|
+
const next = [...filePreviewsRef.current, ...accepted];
|
|
196
|
+
filePreviewsRef.current = next;
|
|
197
|
+
setFilePreviews(next);
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
/** One picked image or video as the file the tray and the upload carry. */
|
|
201
|
+
const assetToFile = async (
|
|
202
|
+
asset: ImagePicker.ImagePickerAsset,
|
|
203
|
+
fallbackPrefix: string
|
|
204
|
+
): Promise<MediaFile> => {
|
|
205
|
+
if (asset.type === 'video') {
|
|
206
|
+
return {
|
|
207
|
+
uri: asset.uri,
|
|
208
|
+
type: asset.mimeType || 'video/mp4',
|
|
209
|
+
name: asset.fileName || asset.uri.split('/').pop() || `${fallbackPrefix}_${Date.now()}.mp4`,
|
|
210
|
+
size: (asset as any).fileSize,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
// Videos pass through unchanged; only images get the HEIC→JPEG
|
|
214
|
+
// normalization (manipulator is image-only).
|
|
215
|
+
const normalized = await normalizeImageAsset(asset);
|
|
216
|
+
return {
|
|
217
|
+
uri: normalized.uri,
|
|
218
|
+
type: normalized.mime,
|
|
219
|
+
name: normalized.name,
|
|
220
|
+
size: (asset as any).fileSize,
|
|
221
|
+
};
|
|
186
222
|
};
|
|
187
223
|
|
|
188
224
|
// Both pickers use expo-image-picker / expo-document-picker. They
|
|
@@ -205,33 +241,14 @@ const SendInput: React.FC<SendInputProps> = ({
|
|
|
205
241
|
}
|
|
206
242
|
|
|
207
243
|
try {
|
|
244
|
+
// Camera is a single shot per tap; the shot joins the tray.
|
|
208
245
|
const result = await ImagePicker.launchCameraAsync({
|
|
209
246
|
mediaTypes: ['images', 'videos'],
|
|
210
247
|
quality: 0.9,
|
|
211
248
|
videoQuality: ImagePicker.UIImagePickerControllerQualityType.Medium,
|
|
212
249
|
});
|
|
213
250
|
if (result.canceled || !result.assets?.[0]) {return;}
|
|
214
|
-
|
|
215
|
-
if (asset.type === 'video') {
|
|
216
|
-
handleFileSelect([
|
|
217
|
-
{
|
|
218
|
-
uri: asset.uri,
|
|
219
|
-
type: asset.mimeType || 'video/mp4',
|
|
220
|
-
name: asset.fileName || asset.uri.split('/').pop() || `camera_${Date.now()}.mp4`,
|
|
221
|
-
size: (asset as any).fileSize,
|
|
222
|
-
},
|
|
223
|
-
]);
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
const normalized = await normalizeImageAsset(asset);
|
|
227
|
-
handleFileSelect([
|
|
228
|
-
{
|
|
229
|
-
uri: normalized.uri,
|
|
230
|
-
type: normalized.mime,
|
|
231
|
-
name: normalized.name,
|
|
232
|
-
size: (asset as any).fileSize,
|
|
233
|
-
},
|
|
234
|
-
]);
|
|
251
|
+
handleFileSelect([await assetToFile(result.assets[0], 'camera')]);
|
|
235
252
|
} catch (error) {
|
|
236
253
|
console.error('Camera error:', error);
|
|
237
254
|
}
|
|
@@ -244,38 +261,29 @@ const SendInput: React.FC<SendInputProps> = ({
|
|
|
244
261
|
return;
|
|
245
262
|
}
|
|
246
263
|
|
|
264
|
+
// The library takes as many as the tray has room for (at least one).
|
|
265
|
+
const remaining = mediaFileCap(config) - filePreviewsRef.current.length;
|
|
266
|
+
if (remaining <= 0) {
|
|
267
|
+
Alert.alert(t('attachment.limit', { count: mediaFileCap(config) }));
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
247
271
|
try {
|
|
248
272
|
const result = await ImagePicker.launchImageLibraryAsync({
|
|
249
273
|
mediaTypes: ['images', 'videos'],
|
|
250
274
|
allowsEditing: false,
|
|
251
275
|
quality: 0.9,
|
|
276
|
+
allowsMultipleSelection: true,
|
|
277
|
+
selectionLimit: remaining,
|
|
252
278
|
preferredAssetRepresentationMode:
|
|
253
279
|
ImagePicker.UIImagePickerPreferredAssetRepresentationMode.Compatible,
|
|
254
280
|
});
|
|
255
|
-
if (result.canceled || !result.assets?.
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if (asset.type === 'video') {
|
|
260
|
-
handleFileSelect([
|
|
261
|
-
{
|
|
262
|
-
uri: asset.uri,
|
|
263
|
-
type: asset.mimeType || 'video/mp4',
|
|
264
|
-
name: asset.fileName || asset.uri.split('/').pop() || `gallery_${Date.now()}.mp4`,
|
|
265
|
-
size: (asset as any).fileSize,
|
|
266
|
-
},
|
|
267
|
-
]);
|
|
268
|
-
return;
|
|
281
|
+
if (result.canceled || !result.assets?.length) {return;}
|
|
282
|
+
const picked: MediaFile[] = [];
|
|
283
|
+
for (const asset of result.assets) {
|
|
284
|
+
picked.push(await assetToFile(asset, 'gallery'));
|
|
269
285
|
}
|
|
270
|
-
|
|
271
|
-
handleFileSelect([
|
|
272
|
-
{
|
|
273
|
-
uri: normalized.uri,
|
|
274
|
-
type: normalized.mime,
|
|
275
|
-
name: normalized.name,
|
|
276
|
-
size: (asset as any).fileSize,
|
|
277
|
-
},
|
|
278
|
-
]);
|
|
286
|
+
handleFileSelect(picked);
|
|
279
287
|
} catch (error) {
|
|
280
288
|
console.error('Gallery error:', error);
|
|
281
289
|
}
|
|
@@ -336,19 +344,18 @@ const SendInput: React.FC<SendInputProps> = ({
|
|
|
336
344
|
try {
|
|
337
345
|
const result = await DocumentPicker.getDocumentAsync({
|
|
338
346
|
type: '*/*',
|
|
339
|
-
multiple:
|
|
347
|
+
multiple: true,
|
|
340
348
|
copyToCacheDirectory: true,
|
|
341
349
|
});
|
|
342
|
-
if (result.canceled || !result.assets?.
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
{
|
|
350
|
+
if (result.canceled || !result.assets?.length) {return;}
|
|
351
|
+
handleFileSelect(
|
|
352
|
+
result.assets.map((asset) => ({
|
|
346
353
|
uri: asset.uri,
|
|
347
354
|
type: asset.mimeType || 'application/octet-stream',
|
|
348
355
|
name: asset.name || `file_${Date.now()}`,
|
|
349
356
|
size: asset.size ?? undefined,
|
|
350
|
-
}
|
|
351
|
-
|
|
357
|
+
}))
|
|
358
|
+
);
|
|
352
359
|
} catch (err) {
|
|
353
360
|
console.error('DocumentPicker error:', err);
|
|
354
361
|
} finally {
|
|
@@ -383,6 +390,11 @@ const SendInput: React.FC<SendInputProps> = ({
|
|
|
383
390
|
const libraryPermissionCheckedRef = useRef(false);
|
|
384
391
|
|
|
385
392
|
const handleAttachPress = () => {
|
|
393
|
+
const cap = mediaFileCap(config);
|
|
394
|
+
if (filePreviewsRef.current.length >= cap) {
|
|
395
|
+
Alert.alert(t('attachment.limit', { count: cap }));
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
386
398
|
if (libraryPermissionCheckedRef.current) {
|
|
387
399
|
setShowMediaMenu(true);
|
|
388
400
|
return;
|
|
@@ -594,17 +606,19 @@ const SendInput: React.FC<SendInputProps> = ({
|
|
|
594
606
|
setFilePreviews([]);
|
|
595
607
|
|
|
596
608
|
try {
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
if (messageToSend) {
|
|
609
|
+
if (filesToSend.length > 0) {
|
|
610
|
+
// The files go as ONE message. The text typed with them is its
|
|
611
|
+
// caption, never a separate text message.
|
|
612
|
+
await sendMedia(
|
|
613
|
+
filesToSend,
|
|
614
|
+
filesToSend[0]!.type,
|
|
615
|
+
messageToSend ? { text: messageToSend } : undefined
|
|
616
|
+
);
|
|
617
|
+
} else if (messageToSend) {
|
|
606
618
|
sendMessage(messageToSend);
|
|
607
619
|
}
|
|
620
|
+
} catch (err) {
|
|
621
|
+
console.error(err);
|
|
608
622
|
} finally {
|
|
609
623
|
sendingRef.current = false;
|
|
610
624
|
}
|
|
@@ -29,6 +29,10 @@ interface CustomMessageVideoProps {
|
|
|
29
29
|
mimetype: string;
|
|
30
30
|
/** The backend-generated poster frame (message.locationPreview). */
|
|
31
31
|
previewURL?: string;
|
|
32
|
+
/** Album mode: fill a tile of exactly this size (square corners; the tile clips). */
|
|
33
|
+
tile?: { width: number; height: number };
|
|
34
|
+
/** Replaces the default "open this one file" tap, e.g. to open an album at an index. */
|
|
35
|
+
onOpen?: () => void;
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
const PlayBadge: React.FC = () => (
|
|
@@ -51,11 +55,13 @@ const PosterVideo: React.FC<{
|
|
|
51
55
|
previewURL: string;
|
|
52
56
|
onOpen: () => void;
|
|
53
57
|
onPosterError: () => void;
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
tile?: { width: number; height: number };
|
|
59
|
+
}> = ({ previewURL, onOpen, onPosterError, tile }) => {
|
|
60
|
+
const [measured, setDims] = useState<MediaDims>(defaultMediaDims());
|
|
61
|
+
const dims: MediaDims = tile ?? measured;
|
|
56
62
|
|
|
57
63
|
useEffect(() => {
|
|
58
|
-
if (!previewURL) {
|
|
64
|
+
if (!previewURL || tile) {
|
|
59
65
|
return;
|
|
60
66
|
}
|
|
61
67
|
let active = true;
|
|
@@ -73,12 +79,16 @@ const PosterVideo: React.FC<{
|
|
|
73
79
|
return () => {
|
|
74
80
|
active = false;
|
|
75
81
|
};
|
|
76
|
-
}, [previewURL]);
|
|
82
|
+
}, [previewURL, tile]);
|
|
77
83
|
|
|
78
84
|
return (
|
|
79
85
|
<Pressable
|
|
80
86
|
onPress={onOpen}
|
|
81
|
-
style={[
|
|
87
|
+
style={[
|
|
88
|
+
styles.wrapper,
|
|
89
|
+
{ width: dims.width, height: dims.height },
|
|
90
|
+
tile && styles.tileWrapper,
|
|
91
|
+
]}
|
|
82
92
|
accessibilityRole="button"
|
|
83
93
|
accessibilityLabel="Play video"
|
|
84
94
|
>
|
|
@@ -115,13 +125,18 @@ const PosterVideo: React.FC<{
|
|
|
115
125
|
*/
|
|
116
126
|
const PlaceholderVideo: React.FC<{
|
|
117
127
|
onOpen: () => void;
|
|
118
|
-
|
|
119
|
-
|
|
128
|
+
tile?: { width: number; height: number };
|
|
129
|
+
}> = ({ onOpen, tile }) => {
|
|
130
|
+
const dims = tile ?? defaultMediaDims();
|
|
120
131
|
|
|
121
132
|
return (
|
|
122
133
|
<Pressable
|
|
123
134
|
onPress={onOpen}
|
|
124
|
-
style={[
|
|
135
|
+
style={[
|
|
136
|
+
styles.wrapper,
|
|
137
|
+
{ width: dims.width, height: dims.height },
|
|
138
|
+
tile && styles.tileWrapper,
|
|
139
|
+
]}
|
|
125
140
|
accessibilityRole="button"
|
|
126
141
|
accessibilityLabel="Play video"
|
|
127
142
|
>
|
|
@@ -136,11 +151,17 @@ const CustomMessageVideo: React.FC<CustomMessageVideoProps> = ({
|
|
|
136
151
|
fileURL,
|
|
137
152
|
mimetype,
|
|
138
153
|
previewURL,
|
|
154
|
+
tile,
|
|
155
|
+
onOpen,
|
|
139
156
|
}) => {
|
|
140
157
|
const dispatch = useDispatch();
|
|
141
158
|
const [posterFailed, setPosterFailed] = useState(false);
|
|
142
159
|
|
|
143
160
|
const handleOpen = () => {
|
|
161
|
+
if (onOpen) {
|
|
162
|
+
onOpen();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
144
165
|
dispatch(setActiveFile({ fileName, fileURL, mimetype, originalName }));
|
|
145
166
|
dispatch(setActiveModal(MODAL_TYPES.FILE_PREVIEW));
|
|
146
167
|
};
|
|
@@ -150,6 +171,7 @@ const CustomMessageVideo: React.FC<CustomMessageVideoProps> = ({
|
|
|
150
171
|
<PosterVideo
|
|
151
172
|
previewURL={previewURL}
|
|
152
173
|
onOpen={handleOpen}
|
|
174
|
+
tile={tile}
|
|
153
175
|
onPosterError={() => {
|
|
154
176
|
if (isSecureFileUrl(previewURL)) {
|
|
155
177
|
requestFileTokenRecovery();
|
|
@@ -160,7 +182,7 @@ const CustomMessageVideo: React.FC<CustomMessageVideoProps> = ({
|
|
|
160
182
|
);
|
|
161
183
|
}
|
|
162
184
|
|
|
163
|
-
return <PlaceholderVideo onOpen={handleOpen} />;
|
|
185
|
+
return <PlaceholderVideo onOpen={handleOpen} tile={tile} />;
|
|
164
186
|
};
|
|
165
187
|
|
|
166
188
|
export default CustomMessageVideo;
|
|
@@ -171,6 +193,9 @@ const styles = StyleSheet.create({
|
|
|
171
193
|
overflow: 'hidden',
|
|
172
194
|
backgroundColor: '#000',
|
|
173
195
|
},
|
|
196
|
+
tileWrapper: {
|
|
197
|
+
borderRadius: 0,
|
|
198
|
+
},
|
|
174
199
|
video: {
|
|
175
200
|
width: '100%',
|
|
176
201
|
height: '100%',
|
|
@@ -63,6 +63,7 @@ import { updateMessagesTillLast } from '../helpers/updateMessagesTillLast';
|
|
|
63
63
|
import { secureUserStorage } from '../helpers/secureUserStorage';
|
|
64
64
|
import { clearPersistedState } from '../roomStore/persistence';
|
|
65
65
|
import { pushLog as devPushLog } from '../utils/devLogger';
|
|
66
|
+
import { computeReadWatermark } from '../helpers/readReceipts';
|
|
66
67
|
|
|
67
68
|
interface InitMode {
|
|
68
69
|
current: 'provider' | 'chat';
|
|
@@ -794,6 +795,26 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config, is
|
|
|
794
795
|
let lastMessagesRef: any = null;
|
|
795
796
|
let lastStampedMs = 0;
|
|
796
797
|
let flushTimer: ReturnType<typeof setTimeout> | null = null;
|
|
798
|
+
// Room whose XEP-0333 marker is waiting on the 2 s debounce below. Set
|
|
799
|
+
// only while that room is the one on screen.
|
|
800
|
+
let pendingMarkerJid: string | null = null;
|
|
801
|
+
|
|
802
|
+
// Sends the waiting marker now: the debounce is cut short because the
|
|
803
|
+
// user left the room or the app, so it would otherwise be lost. The room
|
|
804
|
+
// counts as on screen for this one call (it was when the marker was
|
|
805
|
+
// queued); the jump window, the scroll boundary and the disable flags
|
|
806
|
+
// still apply inside computeReadWatermark.
|
|
807
|
+
const flushPendingMarker = () => {
|
|
808
|
+
const jid = pendingMarkerJid;
|
|
809
|
+
pendingMarkerJid = null;
|
|
810
|
+
if (!jid) {return;}
|
|
811
|
+
const st = store.getState();
|
|
812
|
+
const watermark = computeReadWatermark(
|
|
813
|
+
{ ...st, rooms: { ...st.rooms, visibleRoomJID: jid } } as any,
|
|
814
|
+
jid
|
|
815
|
+
);
|
|
816
|
+
if (watermark) {client?.sendDisplayedMarkerStanza?.(watermark, jid);}
|
|
817
|
+
};
|
|
797
818
|
|
|
798
819
|
const advance = () => {
|
|
799
820
|
const s = store.getState();
|
|
@@ -826,9 +847,13 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config, is
|
|
|
826
847
|
setLastViewedTimestamp({ chatJID: jid, timestamp: newest })
|
|
827
848
|
);
|
|
828
849
|
|
|
850
|
+
// Another room took over the debounce: send the old room's marker first.
|
|
851
|
+
if (pendingMarkerJid && pendingMarkerJid !== jid) {flushPendingMarker();}
|
|
829
852
|
if (flushTimer) {clearTimeout(flushTimer);}
|
|
830
853
|
if (client?.flushLastViewedToPrivateStoreStanza) {
|
|
854
|
+
if (s.rooms?.visibleRoomJID === jid) {pendingMarkerJid = jid;}
|
|
831
855
|
flushTimer = setTimeout(() => {
|
|
856
|
+
pendingMarkerJid = null;
|
|
832
857
|
// Re-read fresh state rather than closing over `boundaryTs`:
|
|
833
858
|
// the user may have scrolled back to the bottom (clearing the
|
|
834
859
|
// boundary) in the 2s between the stamp and this flush, and
|
|
@@ -845,6 +870,12 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config, is
|
|
|
845
870
|
),
|
|
846
871
|
})
|
|
847
872
|
.catch(() => {});
|
|
873
|
+
// XEP-0333 read marker, on the same debounce as the private-store
|
|
874
|
+
// write above. computeReadWatermark sends nothing unless the room is
|
|
875
|
+
// still on screen, no jump window is open and the newest message
|
|
876
|
+
// is someone else's.
|
|
877
|
+
const watermark = computeReadWatermark(freshState, jid);
|
|
878
|
+
if (watermark) {client.sendDisplayedMarkerStanza?.(watermark, jid);}
|
|
848
879
|
}, 2000);
|
|
849
880
|
}
|
|
850
881
|
};
|
|
@@ -854,6 +885,7 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config, is
|
|
|
854
885
|
return () => {
|
|
855
886
|
unsub();
|
|
856
887
|
if (flushTimer) {clearTimeout(flushTimer);}
|
|
888
|
+
flushPendingMarker();
|
|
857
889
|
};
|
|
858
890
|
}, [isVisible, client]);
|
|
859
891
|
|
package/src/e2ee/fileEnvelope.ts
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
type Progress,
|
|
13
13
|
} from './fileCipher';
|
|
14
14
|
|
|
15
|
-
export { parseSealedMediaBody } from './sealedBody';
|
|
15
|
+
export { parseSealedMediaBody, parseSealedMediaPayload } from './sealedBody';
|
|
16
16
|
|
|
17
17
|
const MAGIC = Uint8Array.from([0x45, 0x54, 0x48, 0x4f, 0x46, 0x49, 0x4c, 0x45]);
|
|
18
18
|
const VERSION = 1;
|
package/src/e2ee/sealedBody.ts
CHANGED
|
@@ -1,17 +1,56 @@
|
|
|
1
|
-
|
|
1
|
+
import type { IMentionSpan } from '../types/models/message.model';
|
|
2
|
+
import { normalizeCaptionInput } from '../helpers/caption';
|
|
3
|
+
|
|
4
|
+
/** What a sealed media <body> carries besides the keys. */
|
|
5
|
+
export interface SealedMediaPayload {
|
|
6
|
+
keys: string[];
|
|
7
|
+
/** The text typed with the files. Sealed here, never on <data>. */
|
|
8
|
+
caption?: string;
|
|
9
|
+
captionMentions?: IMentionSpan[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Pulls the attachment keys (and the caption, when there is one) out of a
|
|
14
|
+
* decrypted media <body>: `{v:1,keys:[...],caption?,captionMentions?}`, which
|
|
15
|
+
* the sender writes in place of the usual literal "media". Undefined for
|
|
16
|
+
* anything else, so an ordinary media message falls through untouched.
|
|
17
|
+
*/
|
|
18
|
+
export function parseSealedMediaPayload(
|
|
19
|
+
body?: string
|
|
20
|
+
): SealedMediaPayload | undefined {
|
|
2
21
|
const text = String(body || '').trim();
|
|
3
22
|
if (!text.startsWith('{')) {return undefined;}
|
|
4
23
|
|
|
5
24
|
try {
|
|
6
|
-
const parsed = JSON.parse(text) as {
|
|
25
|
+
const parsed = JSON.parse(text) as {
|
|
26
|
+
v?: unknown;
|
|
27
|
+
keys?: unknown;
|
|
28
|
+
caption?: unknown;
|
|
29
|
+
captionMentions?: unknown;
|
|
30
|
+
};
|
|
7
31
|
if (parsed?.v !== 1 || !Array.isArray(parsed.keys)) {return undefined;}
|
|
8
32
|
const keys = parsed.keys.filter(
|
|
9
33
|
(k): k is string => typeof k === 'string' && k.length > 0
|
|
10
34
|
);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
35
|
+
if (!(keys.length === parsed.keys.length && keys.length > 0)) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const caption = normalizeCaptionInput({
|
|
40
|
+
text: typeof parsed.caption === 'string' ? parsed.caption : '',
|
|
41
|
+
mentions: parsed.captionMentions as IMentionSpan[] | undefined,
|
|
42
|
+
});
|
|
43
|
+
return {
|
|
44
|
+
keys,
|
|
45
|
+
...(caption ? { caption: caption.caption } : {}),
|
|
46
|
+
...(caption?.mentions ? { captionMentions: caption.mentions } : {}),
|
|
47
|
+
};
|
|
14
48
|
} catch {
|
|
15
49
|
return undefined;
|
|
16
50
|
}
|
|
17
51
|
}
|
|
52
|
+
|
|
53
|
+
/** The attachment keys out of a sealed media <body>; see parseSealedMediaPayload. */
|
|
54
|
+
export function parseSealedMediaBody(body?: string): string[] | undefined {
|
|
55
|
+
return parseSealedMediaPayload(body)?.keys;
|
|
56
|
+
}
|