@ethora/chat-component-rn 26.10.5 → 26.10.6
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 +8 -0
- package/README.md +15 -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/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/networking/api-requests/auth.api.js +62 -3
- package/lib/commonjs/networking/api-requests/auth.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/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/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/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/networking/api-requests/auth.api.js +62 -3
- package/lib/module/networking/api-requests/auth.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/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/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/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/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/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/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/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 +29 -1
- package/lib/typescript/types/types.d.ts.map +1 -1
- package/lib/typescript/version.d.ts +1 -1
- package/package.json +4 -1
- package/plugins/notification-service/Info.plist +31 -0
- package/plugins/notification-service/NotificationService.swift +82 -0
- package/plugins/withNotificationServiceExtension.js +137 -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/hooks/useSendMessage.tsx +448 -249
- package/src/i18n/strings.ts +96 -0
- package/src/networking/api-requests/auth.api.ts +60 -3
- 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/roomStore/persistence.ts +94 -3
- package/src/roomStore/roomHeapSlice.ts +23 -1
- package/src/roomStore/roomsSlice.ts +112 -1
- 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 +35 -1
- package/src/version.ts +1 -1
|
@@ -17,6 +17,8 @@ import {
|
|
|
17
17
|
} from './xmpp/getHistory.xmpp';
|
|
18
18
|
import { sendTextMessage } from './xmpp/sendTextMessage.xmpp';
|
|
19
19
|
import { sendMessageReaction } from './xmpp/sendMessageReaction.xmpp';
|
|
20
|
+
import { sendReceivedReceipt } from './xmpp/sendReceivedReceipt.xmpp';
|
|
21
|
+
import { sendDisplayedMarker } from './xmpp/sendDisplayedMarker.xmpp';
|
|
20
22
|
import { sendTextMessageWithTranslateTag } from './xmpp/sendTextMessageWithTranslateTag.xmpp';
|
|
21
23
|
import { deleteMessage } from './xmpp/deleteMessage.xmpp';
|
|
22
24
|
import { RoomJoinManager } from './xmpp/roomJoinSweep';
|
|
@@ -48,6 +50,8 @@ import {
|
|
|
48
50
|
|
|
49
51
|
// Canonical production XMPP WSS endpoint (standard wss/443, no port suffix).
|
|
50
52
|
const DEFAULT_DEV_SERVER = 'xmpp.chat.ethora.com';
|
|
53
|
+
// Upper bound on remembered XEP-0184 acks (oldest forgotten first).
|
|
54
|
+
const RECEIPT_ACKED_CAP = 2000;
|
|
51
55
|
|
|
52
56
|
// How long to wait for the old client's stop() during a reconnect before
|
|
53
57
|
// giving up on it. Short on purpose: we are already tearing this client
|
|
@@ -279,6 +283,13 @@ export class XmppClient {
|
|
|
279
283
|
}
|
|
280
284
|
disableLastRead = false;
|
|
281
285
|
private activeRoomJID: string | null = null;
|
|
286
|
+
// XEP-0184 acks already sent, as `room:messageId`. A message is acked once,
|
|
287
|
+
// whether it arrived live or again inside a MAM page. Oldest entries drop
|
|
288
|
+
// first once the set passes the cap.
|
|
289
|
+
private receiptAckedIds: Set<string> = new Set();
|
|
290
|
+
// XEP-0333: the last read watermark sent per room. A repeat flush for the
|
|
291
|
+
// same newest message sends nothing.
|
|
292
|
+
private lastWatermarkSentByRoom: Map<string, string> = new Map();
|
|
282
293
|
private activeRoomBoostUntil = 0;
|
|
283
294
|
private softPauseUntil = 0;
|
|
284
295
|
private mamInFlightByRoom: Map<string, MamInFlightEntry> = new Map();
|
|
@@ -1594,6 +1605,43 @@ export class XmppClient {
|
|
|
1594
1605
|
return typeof result === 'string' ? result : '';
|
|
1595
1606
|
}
|
|
1596
1607
|
|
|
1608
|
+
/**
|
|
1609
|
+
* XEP-0184 delivery receipt for a message from someone else. Sent for live
|
|
1610
|
+
* messages and for messages a MAM page brings in; a message already acked
|
|
1611
|
+
* is skipped. When the stream is down (or `config.disableReadReceipts` is
|
|
1612
|
+
* on) nothing is recorded, so a later page can still ack it.
|
|
1613
|
+
*/
|
|
1614
|
+
sendReceivedReceiptStanza(messageId: string, roomJid: string) {
|
|
1615
|
+
// Privacy switch: with read receipts off this client stays silent, so
|
|
1616
|
+
// nobody learns that (or when) it received their message.
|
|
1617
|
+
if (store.getState().chatSettingStore.config?.disableReadReceipts) {return;}
|
|
1618
|
+
if (!messageId || !roomJid || !this.isStreamReady()) {return;}
|
|
1619
|
+
const jid = normalizeRoomJid(roomJid, this.conference);
|
|
1620
|
+
const key = `${jid}:${messageId}`;
|
|
1621
|
+
if (this.receiptAckedIds.has(key)) {return;}
|
|
1622
|
+
this.receiptAckedIds.add(key);
|
|
1623
|
+
if (this.receiptAckedIds.size > RECEIPT_ACKED_CAP) {
|
|
1624
|
+
const oldest = this.receiptAckedIds.values().next().value;
|
|
1625
|
+
if (oldest !== undefined) {this.receiptAckedIds.delete(oldest);}
|
|
1626
|
+
}
|
|
1627
|
+
sendReceivedReceipt(this.client, messageId, jid);
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
/**
|
|
1631
|
+
* XEP-0333 read watermark for a room: the newest message the user has
|
|
1632
|
+
* displayed. Off with `disableLastRead` or `config.disableReadReceipts`.
|
|
1633
|
+
* The caller decides when the room counts as read (see computeReadWatermark).
|
|
1634
|
+
*/
|
|
1635
|
+
sendDisplayedMarkerStanza(messageId: string, roomJid: string) {
|
|
1636
|
+
if (this.disableLastRead) {return;}
|
|
1637
|
+
if (store.getState().chatSettingStore.config?.disableReadReceipts) {return;}
|
|
1638
|
+
if (!messageId || !roomJid || !this.isStreamReady()) {return;}
|
|
1639
|
+
const jid = normalizeRoomJid(roomJid, this.conference);
|
|
1640
|
+
if (this.lastWatermarkSentByRoom.get(jid) === messageId) {return;}
|
|
1641
|
+
this.lastWatermarkSentByRoom.set(jid, messageId);
|
|
1642
|
+
sendDisplayedMarker(this.client, messageId, jid);
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1597
1645
|
sendMessageReactionStanza(
|
|
1598
1646
|
messageId: string,
|
|
1599
1647
|
roomJid: string,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Middleware } from '@reduxjs/toolkit';
|
|
2
2
|
import { IMessage, IRoom, User } from '../types/types';
|
|
3
|
-
import type { FailedMessagePayload } from './roomHeapSlice';
|
|
3
|
+
import type { FailedMediaFile, FailedMessagePayload } from './roomHeapSlice';
|
|
4
4
|
import {
|
|
5
5
|
getPersistBackend,
|
|
6
6
|
legacyAsyncStorageBackend,
|
|
@@ -107,6 +107,11 @@ const PERSISTED_MESSAGE_FIELDS: (keyof IMessage)[] = [
|
|
|
107
107
|
'fileName',
|
|
108
108
|
'originalName',
|
|
109
109
|
'size',
|
|
110
|
+
// Every file of a multi-attach message and its caption: without them a
|
|
111
|
+
// restored album paints as its first file only (see compactAttachments).
|
|
112
|
+
'attachments',
|
|
113
|
+
'caption',
|
|
114
|
+
'captionMentions',
|
|
110
115
|
'langSource',
|
|
111
116
|
// `translations` MUST be here. A previous version of this list dropped
|
|
112
117
|
// it, on the theory that it mirrored the web SDK's persist contract —
|
|
@@ -185,8 +190,45 @@ const pickDefined = <T extends object>(
|
|
|
185
190
|
return out;
|
|
186
191
|
};
|
|
187
192
|
|
|
193
|
+
// What a restored attachment is rendered from. Ownership and timestamp
|
|
194
|
+
// fields the storage returns, and the sender's local upload state, are not
|
|
195
|
+
// ours to cache and would repeat across up to ten files of every message.
|
|
196
|
+
const PERSISTED_ATTACHMENT_FIELDS = [
|
|
197
|
+
'attachmentId',
|
|
198
|
+
'location',
|
|
199
|
+
'locationPreview',
|
|
200
|
+
'mimetype',
|
|
201
|
+
'originalName',
|
|
202
|
+
'fileName',
|
|
203
|
+
'size',
|
|
204
|
+
'duration',
|
|
205
|
+
'width',
|
|
206
|
+
'height',
|
|
207
|
+
] as const;
|
|
208
|
+
|
|
209
|
+
const compactAttachments = (attachments: unknown): IMessage['attachments'] => {
|
|
210
|
+
if (!Array.isArray(attachments) || attachments.length === 0) {return undefined;}
|
|
211
|
+
return attachments
|
|
212
|
+
.filter((item) => item && typeof item === 'object')
|
|
213
|
+
.map(
|
|
214
|
+
(item) =>
|
|
215
|
+
pickDefined(
|
|
216
|
+
item,
|
|
217
|
+
PERSISTED_ATTACHMENT_FIELDS as unknown as readonly string[]
|
|
218
|
+
) as NonNullable<IMessage['attachments']>[number]
|
|
219
|
+
);
|
|
220
|
+
};
|
|
221
|
+
|
|
188
222
|
const compactMessageForPersist = (message: IMessage): IMessage => {
|
|
189
223
|
const compact = pickDefined(message, PERSISTED_MESSAGE_FIELDS) as IMessage;
|
|
224
|
+
if (compact.attachments !== undefined) {
|
|
225
|
+
const attachments = compactAttachments(compact.attachments);
|
|
226
|
+
if (attachments) {
|
|
227
|
+
compact.attachments = attachments;
|
|
228
|
+
} else {
|
|
229
|
+
delete compact.attachments;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
190
232
|
const user = message?.user as Record<string, any> | undefined;
|
|
191
233
|
if (user) {
|
|
192
234
|
compact.user = pickDefined(
|
|
@@ -209,11 +251,17 @@ const sanitizeMessages = (messages: IMessage[]): IMessage[] => {
|
|
|
209
251
|
/**
|
|
210
252
|
* Room fields that describe ONE session and must never be written to disk:
|
|
211
253
|
* the server's unread snapshot (a moment-in-time number the next /chats/my
|
|
212
|
-
* re-seeds). Volatile slice state that is not on the room at all (jumpWindow,
|
|
254
|
+
* re-seeds) and the read watermarks other occupants sent this session. Volatile slice state that is not on the room at all (jumpWindow,
|
|
213
255
|
* pendingJump, archivedMessage, usersSet) is never persisted because only
|
|
214
256
|
* `rooms.rooms` is picked from the slice.
|
|
215
257
|
*/
|
|
216
|
-
const SESSION_ROOM_FIELDS = [
|
|
258
|
+
const SESSION_ROOM_FIELDS = [
|
|
259
|
+
'apiUnreadCount',
|
|
260
|
+
'apiUnreadSeededAt',
|
|
261
|
+
// XEP-0333 read watermarks: a peer's "seen" state is re-learned from the
|
|
262
|
+
// live marker stanzas, never restored from a previous session.
|
|
263
|
+
'lastDisplayedByUser',
|
|
264
|
+
] as const;
|
|
217
265
|
|
|
218
266
|
/**
|
|
219
267
|
* Rehydrate step: a persisted room must be refetched once per session, so
|
|
@@ -303,6 +351,21 @@ const sanitizeMediaData = (
|
|
|
303
351
|
return out;
|
|
304
352
|
};
|
|
305
353
|
|
|
354
|
+
// Every file of a multi-file failed send, trimmed the same way as `data`.
|
|
355
|
+
// Capped so a corrupt payload cannot grow the cache without bound.
|
|
356
|
+
const MAX_PERSISTED_FAILED_FILES = 10;
|
|
357
|
+
const sanitizeMediaFiles = (files: unknown): FailedMediaFile[] | undefined => {
|
|
358
|
+
if (!Array.isArray(files)) {return undefined;}
|
|
359
|
+
const out: FailedMediaFile[] = [];
|
|
360
|
+
for (const file of files.slice(0, MAX_PERSISTED_FAILED_FILES)) {
|
|
361
|
+
const clean = sanitizeMediaData(file) as FailedMediaFile;
|
|
362
|
+
if (typeof (file as any)?.duration === 'number') {clean.duration = (file as any).duration;}
|
|
363
|
+
if (typeof (file as any)?.waveForm === 'string') {clean.waveForm = (file as any).waveForm;}
|
|
364
|
+
out.push(clean);
|
|
365
|
+
}
|
|
366
|
+
return out.length > 0 ? out : undefined;
|
|
367
|
+
};
|
|
368
|
+
|
|
306
369
|
const sanitizeFailedMessagePayload = (
|
|
307
370
|
payload: FailedMessagePayload
|
|
308
371
|
): FailedMessagePayload | null => {
|
|
@@ -310,6 +373,8 @@ const sanitizeFailedMessagePayload = (
|
|
|
310
373
|
if (!payload.id || typeof payload.id !== 'string') {return null;}
|
|
311
374
|
if (!payload.roomJID || typeof payload.roomJID !== 'string') {return null;}
|
|
312
375
|
if (payload.kind === 'media') {
|
|
376
|
+
const files = sanitizeMediaFiles(payload.files);
|
|
377
|
+
const caption = typeof payload.caption === 'string' ? payload.caption : undefined;
|
|
313
378
|
return {
|
|
314
379
|
kind: 'media',
|
|
315
380
|
id: payload.id,
|
|
@@ -319,6 +384,11 @@ const sanitizeFailedMessagePayload = (
|
|
|
319
384
|
isReply: payload.isReply,
|
|
320
385
|
isChecked: payload.isChecked,
|
|
321
386
|
mainMessage: payload.mainMessage,
|
|
387
|
+
...(files ? { files } : {}),
|
|
388
|
+
...(caption ? { caption } : {}),
|
|
389
|
+
...(caption && Array.isArray(payload.captionMentions)
|
|
390
|
+
? { captionMentions: payload.captionMentions }
|
|
391
|
+
: {}),
|
|
322
392
|
};
|
|
323
393
|
}
|
|
324
394
|
if (payload.kind === 'text') {
|
|
@@ -377,6 +447,25 @@ export function reconstructFailedMessagePayload(
|
|
|
377
447
|
const isChecked = message.showInChannel === 'true';
|
|
378
448
|
const mainMessage = message.mainMessage;
|
|
379
449
|
if (message.isMediafile === 'true') {
|
|
450
|
+
// A multi-file send keeps every file. Only a file still on this device
|
|
451
|
+
// has a uri to replay; an uploaded one has no local copy left to send.
|
|
452
|
+
const localUri = (value: unknown): string | undefined =>
|
|
453
|
+
typeof value === 'string' && /^(file|content|ph|assets-library):\/\//.test(value)
|
|
454
|
+
? value
|
|
455
|
+
: undefined;
|
|
456
|
+
const attachments = Array.isArray(message.attachments) ? message.attachments : [];
|
|
457
|
+
const files: FailedMediaFile[] | undefined =
|
|
458
|
+
attachments.length > 1
|
|
459
|
+
? attachments.slice(0, MAX_PERSISTED_FAILED_FILES).map((attachment) => ({
|
|
460
|
+
uri: localUri(attachment.localUrl) ?? localUri(attachment.location),
|
|
461
|
+
name: attachment.originalName || attachment.fileName,
|
|
462
|
+
type: attachment.mimetype,
|
|
463
|
+
size:
|
|
464
|
+
attachment.size !== undefined && attachment.size !== null
|
|
465
|
+
? Number(attachment.size)
|
|
466
|
+
: undefined,
|
|
467
|
+
}))
|
|
468
|
+
: undefined;
|
|
380
469
|
return {
|
|
381
470
|
kind: 'media',
|
|
382
471
|
id: message.id,
|
|
@@ -394,6 +483,8 @@ export function reconstructFailedMessagePayload(
|
|
|
394
483
|
isReply,
|
|
395
484
|
isChecked,
|
|
396
485
|
mainMessage,
|
|
486
|
+
...(files ? { files } : {}),
|
|
487
|
+
...(message.caption ? { caption: message.caption } : {}),
|
|
397
488
|
};
|
|
398
489
|
}
|
|
399
490
|
return {
|
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
import { createSlice, PayloadAction, type Slice } from '@reduxjs/toolkit';
|
|
2
2
|
import type { WritableDraft } from 'immer';
|
|
3
|
-
import { IMessage } from '../types/types';
|
|
3
|
+
import { IMentionSpan, IMessage } from '../types/types';
|
|
4
4
|
|
|
5
5
|
// Saved alongside a failed-message id so the retry button can resend
|
|
6
6
|
// without the bubble needing to remember the original draft itself.
|
|
7
7
|
// `kind === 'text'` carries the plain body; `kind === 'media'` carries
|
|
8
8
|
// the upload payload so we don't have to make the user re-pick the file.
|
|
9
|
+
/**
|
|
10
|
+
* One file of a failed media send, as the retry replays it. Only the fields
|
|
11
|
+
* an upload needs, so it persists as a plain JSON object.
|
|
12
|
+
*/
|
|
13
|
+
export interface FailedMediaFile {
|
|
14
|
+
uri?: string;
|
|
15
|
+
type?: string;
|
|
16
|
+
name?: string;
|
|
17
|
+
size?: number;
|
|
18
|
+
duration?: number;
|
|
19
|
+
waveForm?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
9
22
|
export type FailedMessagePayload =
|
|
10
23
|
| {
|
|
11
24
|
kind: 'text';
|
|
@@ -20,11 +33,20 @@ export type FailedMessagePayload =
|
|
|
20
33
|
kind: 'media';
|
|
21
34
|
id: string;
|
|
22
35
|
roomJID: string;
|
|
36
|
+
/** The first file (or the only one); kept for single-file readers. */
|
|
23
37
|
data: any;
|
|
24
38
|
type: string;
|
|
25
39
|
isReply?: boolean;
|
|
26
40
|
isChecked?: boolean;
|
|
27
41
|
mainMessage?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Every file of the message, in selection order. Retry replays the
|
|
44
|
+
* whole list; a message with no list falls back to `data`.
|
|
45
|
+
*/
|
|
46
|
+
files?: FailedMediaFile[];
|
|
47
|
+
/** The caption the files were sent with, if any. */
|
|
48
|
+
caption?: string;
|
|
49
|
+
captionMentions?: IMentionSpan[];
|
|
28
50
|
};
|
|
29
51
|
|
|
30
52
|
export interface roomHeapSliceState {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createAsyncThunk, createSlice, PayloadAction, type Slice } from '@reduxjs/toolkit';
|
|
2
2
|
import type { WritableDraft } from 'immer';
|
|
3
|
-
import { EditAction, HistoryPreloadState, IMessage, IRoom } from '../types/types';
|
|
3
|
+
import { EditAction, HistoryPreloadState, IAttachment, IMessage, IRoom } from '../types/types';
|
|
4
4
|
import { insertMessageWithDelimiter } from '../helpers/insertMessageWithDelimiter';
|
|
5
5
|
import { msgSortableMs } from '../helpers/msgSortableMs';
|
|
6
6
|
import type XmppClient from '../networking/xmppClient';
|
|
@@ -955,6 +955,66 @@ const reducers = {
|
|
|
955
955
|
message.translations[locale] = entry;
|
|
956
956
|
});
|
|
957
957
|
},
|
|
958
|
+
/**
|
|
959
|
+
* Local upload state of one file of an outgoing media message: its
|
|
960
|
+
* `uploadStatus` ('uploading' | 'failed') and `uploadProgress` (0-100).
|
|
961
|
+
* `null` for `uploadStatus` clears both, once the file is up. Matches the
|
|
962
|
+
* message in the room and in the jump window, like the other patches.
|
|
963
|
+
* Never persisted: the persist whitelist drops these fields.
|
|
964
|
+
*/
|
|
965
|
+
setMediaUploadState(
|
|
966
|
+
state: WritableDraft<RoomMessagesState>,
|
|
967
|
+
action: PayloadAction<{
|
|
968
|
+
roomJID: string;
|
|
969
|
+
messageId: string;
|
|
970
|
+
attachmentId: string;
|
|
971
|
+
uploadStatus?: 'uploading' | 'failed' | null;
|
|
972
|
+
uploadProgress?: number;
|
|
973
|
+
}>
|
|
974
|
+
) {
|
|
975
|
+
const { roomJID, messageId, attachmentId, uploadStatus, uploadProgress } =
|
|
976
|
+
action.payload;
|
|
977
|
+
if (!isSafeKey(roomJID) || !isSafeKey(messageId) || !isSafeKey(attachmentId)) {
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
forEachMessageCopy(state, roomJID, messageId, (message) => {
|
|
981
|
+
const target = message.attachments?.find(
|
|
982
|
+
(attachment) => attachment.attachmentId === attachmentId
|
|
983
|
+
);
|
|
984
|
+
if (!target) {return;}
|
|
985
|
+
if (uploadStatus === null) {
|
|
986
|
+
delete target.uploadStatus;
|
|
987
|
+
delete target.uploadProgress;
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
if (uploadStatus !== undefined) {target.uploadStatus = uploadStatus;}
|
|
991
|
+
if (typeof uploadProgress === 'number' && Number.isFinite(uploadProgress)) {
|
|
992
|
+
target.uploadProgress = Math.min(100, Math.max(0, uploadProgress));
|
|
993
|
+
}
|
|
994
|
+
});
|
|
995
|
+
},
|
|
996
|
+
/**
|
|
997
|
+
* Replaces the attachments of a message the sender already has, without
|
|
998
|
+
* touching its other fields: `pending` stays as it is, so the bubble is
|
|
999
|
+
* still waiting for the server echo. (addRoomMessage would count this as
|
|
1000
|
+
* the echo and clear `pending`.)
|
|
1001
|
+
*/
|
|
1002
|
+
setMessageAttachments(
|
|
1003
|
+
state: WritableDraft<RoomMessagesState>,
|
|
1004
|
+
action: PayloadAction<{
|
|
1005
|
+
roomJID: string;
|
|
1006
|
+
messageId: string;
|
|
1007
|
+
attachments: IAttachment[];
|
|
1008
|
+
}>
|
|
1009
|
+
) {
|
|
1010
|
+
const { roomJID, messageId, attachments } = action.payload;
|
|
1011
|
+
if (!isSafeKey(roomJID) || !isSafeKey(messageId) || !Array.isArray(attachments)) {
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
forEachMessageCopy(state, roomJID, messageId, (message) => {
|
|
1015
|
+
message.attachments = attachments.map((attachment) => ({ ...attachment }));
|
|
1016
|
+
});
|
|
1017
|
+
},
|
|
958
1018
|
addRoomMessage(
|
|
959
1019
|
state: WritableDraft<RoomMessagesState>,
|
|
960
1020
|
action: PayloadAction<{
|
|
@@ -1412,6 +1472,53 @@ const reducers = {
|
|
|
1412
1472
|
list.push({ from, reactions, data, ts: latestReactionTimestamp });
|
|
1413
1473
|
}
|
|
1414
1474
|
},
|
|
1475
|
+
/**
|
|
1476
|
+
* XEP-0184: an occupant's client acknowledged one of our messages. Only
|
|
1477
|
+
* the message id is matched (archive ids), on the live list and on a jump
|
|
1478
|
+
* window copy. Session-only: not persisted.
|
|
1479
|
+
*/
|
|
1480
|
+
setReceivedReceipt: (
|
|
1481
|
+
state: WritableDraft<RoomMessagesState>,
|
|
1482
|
+
action: PayloadAction<{
|
|
1483
|
+
roomJID: string;
|
|
1484
|
+
messageId: string;
|
|
1485
|
+
fromXmppUsername: string;
|
|
1486
|
+
timestamp: number;
|
|
1487
|
+
}>
|
|
1488
|
+
) => {
|
|
1489
|
+
const { roomJID, messageId, fromXmppUsername, timestamp } = action.payload;
|
|
1490
|
+
// Both keys become property names: an unsafe key must never reach the write.
|
|
1491
|
+
if (!isSafeKey(roomJID) || !isSafeKey(fromXmppUsername)) {return;}
|
|
1492
|
+
if (!state.rooms[roomJID] || !messageId) {return;}
|
|
1493
|
+
forEachMessageCopy(
|
|
1494
|
+
state,
|
|
1495
|
+
roomJID,
|
|
1496
|
+
(message) => message.id === messageId,
|
|
1497
|
+
(message) => {
|
|
1498
|
+
if (!message.receivedBy) {message.receivedBy = {};}
|
|
1499
|
+
message.receivedBy[fromXmppUsername] = timestamp;
|
|
1500
|
+
}
|
|
1501
|
+
);
|
|
1502
|
+
},
|
|
1503
|
+
/**
|
|
1504
|
+
* XEP-0333: an occupant displayed up to `messageId`. One watermark per
|
|
1505
|
+
* user and room, replaced on every marker. Session-only: not persisted.
|
|
1506
|
+
*/
|
|
1507
|
+
setDisplayedMarker: (
|
|
1508
|
+
state: WritableDraft<RoomMessagesState>,
|
|
1509
|
+
action: PayloadAction<{
|
|
1510
|
+
roomJID: string;
|
|
1511
|
+
xmppUsername: string;
|
|
1512
|
+
messageId: string;
|
|
1513
|
+
}>
|
|
1514
|
+
) => {
|
|
1515
|
+
const { roomJID, xmppUsername, messageId } = action.payload;
|
|
1516
|
+
if (!isSafeKey(roomJID) || !isSafeKey(xmppUsername)) {return;}
|
|
1517
|
+
const room = state.rooms[roomJID];
|
|
1518
|
+
if (!room || !messageId) {return;}
|
|
1519
|
+
if (!room.lastDisplayedByUser) {room.lastDisplayedByUser = {};}
|
|
1520
|
+
room.lastDisplayedByUser[xmppUsername] = messageId;
|
|
1521
|
+
},
|
|
1415
1522
|
setLogoutState: (state: WritableDraft<RoomMessagesState>) => {
|
|
1416
1523
|
state.rooms = {};
|
|
1417
1524
|
state.pendingReactions = {};
|
|
@@ -1640,6 +1747,8 @@ export const {
|
|
|
1640
1747
|
deleteAllRooms,
|
|
1641
1748
|
setRoomMessages,
|
|
1642
1749
|
addRoomMessage,
|
|
1750
|
+
setMediaUploadState,
|
|
1751
|
+
setMessageAttachments,
|
|
1643
1752
|
deleteRoomMessage,
|
|
1644
1753
|
setEditAction,
|
|
1645
1754
|
editRoomMessage,
|
|
@@ -1671,6 +1780,8 @@ export const {
|
|
|
1671
1780
|
setJoiningRoom,
|
|
1672
1781
|
clearJoiningRoom,
|
|
1673
1782
|
setReactions,
|
|
1783
|
+
setReceivedReceipt,
|
|
1784
|
+
setDisplayedMarker,
|
|
1674
1785
|
mergeUsersSet,
|
|
1675
1786
|
removeRoomMessage,
|
|
1676
1787
|
addRoomFromApi,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One uploaded file carried by a media message.
|
|
3
|
+
*
|
|
4
|
+
* Historically a media message described exactly one file through flat
|
|
5
|
+
* `IMessage` fields (`location`, `mimetype`, `originalName`, ...). Those
|
|
6
|
+
* fields are still written (they are what every already-shipped client
|
|
7
|
+
* reads), and they now double as attachment #0 of `IMessage.attachments`.
|
|
8
|
+
* Anything past #0 only exists in the `attachments` payload, so old clients
|
|
9
|
+
* degrade to "shows the first file" instead of breaking.
|
|
10
|
+
*/
|
|
11
|
+
export interface IAttachment {
|
|
12
|
+
/** `_id` returned by POST /files/ - stable per upload, used as a render key. */
|
|
13
|
+
attachmentId?: string;
|
|
14
|
+
/** Canonical file URL. */
|
|
15
|
+
location: string;
|
|
16
|
+
/** Server-side generated preview (images always, other types sometimes). */
|
|
17
|
+
locationPreview?: string;
|
|
18
|
+
mimetype?: string;
|
|
19
|
+
/** Name the user picked. */
|
|
20
|
+
originalName?: string;
|
|
21
|
+
/** Name on the storage side. */
|
|
22
|
+
fileName?: string;
|
|
23
|
+
/** Bytes, as a string - that is how it travels on the wire. */
|
|
24
|
+
size?: string;
|
|
25
|
+
duration?: string;
|
|
26
|
+
/** Natural pixel size, when the storage recorded it. */
|
|
27
|
+
width?: number;
|
|
28
|
+
height?: number;
|
|
29
|
+
ownerKey?: string;
|
|
30
|
+
userId?: string;
|
|
31
|
+
createdAt?: string;
|
|
32
|
+
updatedAt?: string;
|
|
33
|
+
expiresAt?: string;
|
|
34
|
+
isVisible?: boolean;
|
|
35
|
+
isPrivate?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Local-only state of an outgoing attachment, set by the sender while its
|
|
38
|
+
* message is still being sent. Never written to the wire.
|
|
39
|
+
* uploading - the file is going up (or every file is up and the message
|
|
40
|
+
* is waiting on its siblings); `uploadProgress` is 0 to 100.
|
|
41
|
+
* failed - this file did not upload; the message is held until it
|
|
42
|
+
* is retried.
|
|
43
|
+
*/
|
|
44
|
+
uploadStatus?: 'uploading' | 'failed';
|
|
45
|
+
uploadProgress?: number;
|
|
46
|
+
/**
|
|
47
|
+
* The file as it is on this device (`file://`), shown on the sender's own
|
|
48
|
+
* bubble while it uploads. Local only, like the upload state above.
|
|
49
|
+
*/
|
|
50
|
+
localUrl?: string;
|
|
51
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { IUser } from './user.model';
|
|
2
2
|
import { TranslationObject } from '../../helpers/transformTranslatations';
|
|
3
3
|
import { Iso639_1Codes } from './language.model';
|
|
4
|
+
import type { IAttachment } from './attachment.model';
|
|
4
5
|
|
|
5
6
|
export interface IMessage {
|
|
6
7
|
id: string;
|
|
@@ -35,6 +36,25 @@ export interface IMessage {
|
|
|
35
36
|
langSource?: Iso639_1Codes;
|
|
36
37
|
originalName?: string;
|
|
37
38
|
size?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Every file carried by this message. `attachments[0]` mirrors the flat
|
|
41
|
+
* `location`/`mimetype`/`originalName` fields above. Undefined on messages
|
|
42
|
+
* that predate multi-attach; read it through `getMessageAttachments()`.
|
|
43
|
+
*/
|
|
44
|
+
attachments?: IAttachment[];
|
|
45
|
+
/**
|
|
46
|
+
* Text sent with a media message's files. `body` stays "media". In a sealed
|
|
47
|
+
* message it comes out of the encrypted body, never from `<data>`. Read it
|
|
48
|
+
* through `getMessageCaption()`.
|
|
49
|
+
*/
|
|
50
|
+
caption?: string;
|
|
51
|
+
/** @-mention spans into `caption`. Stored, not rendered on RN. */
|
|
52
|
+
captionMentions?: IMentionSpan[];
|
|
53
|
+
/**
|
|
54
|
+
* XEP-0184 delivery receipts: xmpp username -> epoch ms when that
|
|
55
|
+
* occupant's client acknowledged the message. Session-only (never persisted).
|
|
56
|
+
*/
|
|
57
|
+
receivedBy?: Record<string, number>;
|
|
38
58
|
xmppId?: string;
|
|
39
59
|
xmppFrom?: string;
|
|
40
60
|
deleted?: boolean;
|
|
@@ -52,6 +72,15 @@ export interface IMessage {
|
|
|
52
72
|
};
|
|
53
73
|
}
|
|
54
74
|
|
|
75
|
+
// One @-mention span inside a caption (or text body). `offset`/`length` index
|
|
76
|
+
// into that string in UTF-16 code units.
|
|
77
|
+
export interface IMentionSpan {
|
|
78
|
+
jid: string;
|
|
79
|
+
name: string;
|
|
80
|
+
offset: number;
|
|
81
|
+
length: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
55
84
|
export interface ReactionMessage {
|
|
56
85
|
emoji: string[];
|
|
57
86
|
data: Record<string, string> | undefined;
|
|
@@ -9,4 +9,11 @@ export interface ModalFile {
|
|
|
9
9
|
originalName?: string;
|
|
10
10
|
duration?: number | string;
|
|
11
11
|
waveForm?: string;
|
|
12
|
+
/**
|
|
13
|
+
* The other files of the album this one was opened from, this one
|
|
14
|
+
* included, in order. With `index` it lets the viewer page through them.
|
|
15
|
+
*/
|
|
16
|
+
siblings?: ModalFile[];
|
|
17
|
+
/** Position of this file in `siblings`. */
|
|
18
|
+
index?: number;
|
|
12
19
|
}
|
|
@@ -52,6 +52,8 @@ export interface IRoom {
|
|
|
52
52
|
composing?: boolean;
|
|
53
53
|
composingList?: string[];
|
|
54
54
|
lastViewedTimestamp?: number;
|
|
55
|
+
/** XEP-0333 read watermark per user (last displayed message id). Session-only. */
|
|
56
|
+
lastDisplayedByUser?: Record<string, string>;
|
|
55
57
|
unreadMessages?: number;
|
|
56
58
|
/**
|
|
57
59
|
* Unread count the server reported for this room in `GET /v1/chats/my`
|
package/src/types/types.ts
CHANGED
|
@@ -43,6 +43,11 @@ export interface IRoom {
|
|
|
43
43
|
composing?: boolean;
|
|
44
44
|
composingList?: string[];
|
|
45
45
|
lastViewedTimestamp?: number;
|
|
46
|
+
/**
|
|
47
|
+
* XEP-0333 read watermark per user: the id of the newest message each other
|
|
48
|
+
* occupant has displayed. Session-only (never persisted). See readReceipts.ts.
|
|
49
|
+
*/
|
|
50
|
+
lastDisplayedByUser?: Record<string, string>;
|
|
46
51
|
unreadMessages?: number;
|
|
47
52
|
/**
|
|
48
53
|
* Unread count the server reported for this room in `GET /v1/chats/my`
|
|
@@ -473,6 +478,15 @@ export interface IConfig {
|
|
|
473
478
|
};
|
|
474
479
|
disableHeader?: boolean;
|
|
475
480
|
disableMedia?: boolean;
|
|
481
|
+
/**
|
|
482
|
+
* Limits for files picked in the composer. One message carries up to
|
|
483
|
+
* `maxFiles` files (hard ceiling 10, the default); `maxSizeMB` lowers the
|
|
484
|
+
* per-file cap below the built-in 50 MB upload limit.
|
|
485
|
+
*/
|
|
486
|
+
attachments?: {
|
|
487
|
+
maxFiles?: number;
|
|
488
|
+
maxSizeMB?: number;
|
|
489
|
+
};
|
|
476
490
|
/**
|
|
477
491
|
* Enable in-chat voice-message recording. **Opt-in — off by default.**
|
|
478
492
|
* When `true`, an idle input (no text, no attachments) shows a
|
|
@@ -580,6 +594,12 @@ export interface IConfig {
|
|
|
580
594
|
// ----- xmpp / QoS -----
|
|
581
595
|
xmppSettings?: xmppSettingsInterface;
|
|
582
596
|
disableLastRead?: boolean;
|
|
597
|
+
/**
|
|
598
|
+
* Stops this client from sending receipts: both the XEP-0184 delivery
|
|
599
|
+
* receipt and the XEP-0333 read marker, so other members see neither
|
|
600
|
+
* "delivered" nor "seen" for what this reader gets. Default false.
|
|
601
|
+
*/
|
|
602
|
+
disableReadReceipts?: boolean;
|
|
583
603
|
historyQoS?: HistoryQoSConfig;
|
|
584
604
|
/**
|
|
585
605
|
* Background history preload for the room list.
|
|
@@ -1129,13 +1149,27 @@ export interface ModalFile {
|
|
|
1129
1149
|
originalName?: string;
|
|
1130
1150
|
duration?: number | string;
|
|
1131
1151
|
waveForm?: string;
|
|
1152
|
+
/**
|
|
1153
|
+
* The other files of the album this one was opened from, this one
|
|
1154
|
+
* included, in order. With `index` it lets the viewer page through them.
|
|
1155
|
+
*/
|
|
1156
|
+
siblings?: ModalFile[];
|
|
1157
|
+
/** Position of this file in `siblings`. */
|
|
1158
|
+
index?: number;
|
|
1132
1159
|
}
|
|
1133
1160
|
|
|
1134
1161
|
// Re-export so middleware/hooks that historically import from `types`
|
|
1135
1162
|
// keep working without reaching into `types/models/...`.
|
|
1136
1163
|
export type { ReactionAction } from './models/action.model';
|
|
1137
1164
|
export type { Iso639_1Codes } from './models/language.model';
|
|
1138
|
-
export type {
|
|
1165
|
+
export type {
|
|
1166
|
+
IMessage,
|
|
1167
|
+
IReply,
|
|
1168
|
+
LastMessage,
|
|
1169
|
+
ReactionMessage,
|
|
1170
|
+
IMentionSpan,
|
|
1171
|
+
} from './models/message.model';
|
|
1172
|
+
export type { IAttachment } from './models/attachment.model';
|
|
1139
1173
|
export type { RoomMember, ApiRoom, ChatAccessOption } from './models/room.model';
|
|
1140
1174
|
export type { MediaFile } from './models/media.model';
|
|
1141
1175
|
export type { TranslationObject } from '../helpers/transformTranslatations';
|
package/src/version.ts
CHANGED