@ethora/chat-component-rn 26.10.4 → 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/VideoCalls/ActiveCallBar.js +61 -16
- package/lib/commonjs/components/VideoCalls/ActiveCallBar.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/CallIcons.js +41 -16
- package/lib/commonjs/components/VideoCalls/CallIcons.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/VideoCallOverlay.js +183 -9
- package/lib/commonjs/components/VideoCalls/VideoCallOverlay.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/VideoCallSession.js +40 -15
- package/lib/commonjs/components/VideoCalls/VideoCallSession.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/callSessionController.js +49 -26
- package/lib/commonjs/components/VideoCalls/callSessionController.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/callUiStore.js +33 -4
- package/lib/commonjs/components/VideoCalls/callUiStore.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/livekitRuntime.js +9 -1
- package/lib/commonjs/components/VideoCalls/livekitRuntime.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/useCallKeep.js +197 -28
- package/lib/commonjs/components/VideoCalls/useCallKeep.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/useOngoingCallNotification.js +88 -0
- package/lib/commonjs/components/VideoCalls/useOngoingCallNotification.js.map +1 -0
- package/lib/commonjs/components/VideoCalls/usePipTile.js +232 -0
- package/lib/commonjs/components/VideoCalls/usePipTile.js.map +1 -0
- 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/updateMessagesTillLast.js +3 -1
- package/lib/commonjs/helpers/updateMessagesTillLast.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 +144 -0
- package/lib/commonjs/i18n/strings.js.map +1 -1
- 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/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/VideoCalls/ActiveCallBar.js +63 -18
- package/lib/module/components/VideoCalls/ActiveCallBar.js.map +1 -1
- package/lib/module/components/VideoCalls/CallIcons.js +38 -15
- package/lib/module/components/VideoCalls/CallIcons.js.map +1 -1
- package/lib/module/components/VideoCalls/VideoCallOverlay.js +185 -11
- package/lib/module/components/VideoCalls/VideoCallOverlay.js.map +1 -1
- package/lib/module/components/VideoCalls/VideoCallSession.js +41 -16
- package/lib/module/components/VideoCalls/VideoCallSession.js.map +1 -1
- package/lib/module/components/VideoCalls/callSessionController.js +46 -24
- package/lib/module/components/VideoCalls/callSessionController.js.map +1 -1
- package/lib/module/components/VideoCalls/callUiStore.js +30 -3
- package/lib/module/components/VideoCalls/callUiStore.js.map +1 -1
- package/lib/module/components/VideoCalls/livekitRuntime.js +9 -1
- package/lib/module/components/VideoCalls/livekitRuntime.js.map +1 -1
- package/lib/module/components/VideoCalls/useCallKeep.js +198 -29
- package/lib/module/components/VideoCalls/useCallKeep.js.map +1 -1
- package/lib/module/components/VideoCalls/useOngoingCallNotification.js +84 -0
- package/lib/module/components/VideoCalls/useOngoingCallNotification.js.map +1 -0
- package/lib/module/components/VideoCalls/usePipTile.js +228 -0
- package/lib/module/components/VideoCalls/usePipTile.js.map +1 -0
- 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/updateMessagesTillLast.js +3 -1
- package/lib/module/helpers/updateMessagesTillLast.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 +144 -0
- package/lib/module/i18n/strings.js.map +1 -1
- 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/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/VideoCalls/ActiveCallBar.d.ts +9 -3
- package/lib/typescript/components/VideoCalls/ActiveCallBar.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/CallIcons.d.ts +9 -0
- package/lib/typescript/components/VideoCalls/CallIcons.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/VideoCallOverlay.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/VideoCallSession.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/callSessionController.d.ts +7 -0
- package/lib/typescript/components/VideoCalls/callSessionController.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/callUiStore.d.ts +13 -0
- package/lib/typescript/components/VideoCalls/callUiStore.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/livekitRuntime.d.ts +5 -0
- package/lib/typescript/components/VideoCalls/livekitRuntime.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/useCallKeep.d.ts +12 -0
- package/lib/typescript/components/VideoCalls/useCallKeep.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/useOngoingCallNotification.d.ts +10 -0
- package/lib/typescript/components/VideoCalls/useOngoingCallNotification.d.ts.map +1 -0
- package/lib/typescript/components/VideoCalls/usePipTile.d.ts +46 -0
- package/lib/typescript/components/VideoCalls/usePipTile.d.ts.map +1 -0
- 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/updateMessagesTillLast.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 +1 -1
- 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/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 +69 -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/VideoCalls/ActiveCallBar.tsx +56 -10
- package/src/components/VideoCalls/CallIcons.tsx +36 -12
- package/src/components/VideoCalls/VideoCallOverlay.tsx +205 -8
- package/src/components/VideoCalls/VideoCallSession.tsx +45 -13
- package/src/components/VideoCalls/callSessionController.ts +51 -20
- package/src/components/VideoCalls/callUiStore.ts +33 -3
- package/src/components/VideoCalls/livekitRuntime.ts +13 -0
- package/src/components/VideoCalls/useCallKeep.ts +204 -33
- package/src/components/VideoCalls/useOngoingCallNotification.ts +113 -0
- package/src/components/VideoCalls/usePipTile.ts +214 -0
- 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/updateMessagesTillLast.tsx +2 -1
- package/src/hooks/useSendMessage.tsx +448 -249
- package/src/i18n/strings.ts +144 -0
- package/src/main.ts +3 -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 +83 -1
- package/src/version.ts +1 -1
- package/lib/commonjs/components/VideoCalls/useDraggable.js +0 -59
- package/lib/commonjs/components/VideoCalls/useDraggable.js.map +0 -1
- package/lib/module/components/VideoCalls/useDraggable.js +0 -55
- package/lib/module/components/VideoCalls/useDraggable.js.map +0 -1
- package/lib/typescript/components/VideoCalls/useDraggable.d.ts +0 -19
- package/lib/typescript/components/VideoCalls/useDraggable.d.ts.map +0 -1
- package/src/components/VideoCalls/useDraggable.ts +0 -46
|
@@ -5,7 +5,7 @@ import { Platform } from 'react-native';
|
|
|
5
5
|
import { v4 as uuidv4 } from 'uuid';
|
|
6
6
|
import { useXmppClient } from '../context/xmppProvider';
|
|
7
7
|
import { useDispatch, useSelector, useStore } from 'react-redux';
|
|
8
|
-
import { addRoomMessage, setEditAction } from '../roomStore/roomsSlice';
|
|
8
|
+
import { addRoomMessage, setEditAction, setMediaUploadState, setMessageAttachments } from '../roomStore/roomsSlice';
|
|
9
9
|
import { addMessageToHeap, clearMessageFailure, markMessageFailed } from '../roomStore/roomHeapSlice';
|
|
10
10
|
import { uploadFileV2 } from '../networking/api-requests/auth.api';
|
|
11
11
|
import { enqueueOutboundSend, removeOutboundSend } from '../networking/outboundQueue';
|
|
@@ -14,6 +14,13 @@ import { getGlobalXmppClient } from '../utils/clientRegistry';
|
|
|
14
14
|
import { useEventHandlers } from './useEventHandlers';
|
|
15
15
|
import { canEditMessage, canSendMediaToRoom, canSendToRoom } from '../e2ee';
|
|
16
16
|
import { shouldEncrypt } from '../e2ee/send';
|
|
17
|
+
import { MAX_SEAL_BYTES } from '../e2ee/limits';
|
|
18
|
+
import { mapWithConcurrency } from '../helpers/mapWithConcurrency';
|
|
19
|
+
import { normalizeCaptionInput } from '../helpers/caption';
|
|
20
|
+
import { toUploadedAttachment } from '../helpers/attachments';
|
|
21
|
+
import { mediaFileCap, mediaSizeLimit } from '../helpers/mediaLimits';
|
|
22
|
+
import { hasMediaRetry, registerMediaRetry, retryMediaSend, unregisterMediaRetry } from '../helpers/mediaSendRetry';
|
|
23
|
+
|
|
17
24
|
// Watchdog: if a message stays "pending" (no server echo) for this
|
|
18
25
|
// long after the optimistic dispatch, mark it failed so the bubble
|
|
19
26
|
// flips out of the "sending..." state. Covers the silent-failure
|
|
@@ -46,10 +53,48 @@ const MEDIA_PENDING_WATCHDOG_MS = 30_000;
|
|
|
46
53
|
let __sendIdSeq = 0;
|
|
47
54
|
const nextStanzaId = prefix => `${prefix}-${Date.now()}-${__sendIdSeq = __sendIdSeq + 1 >>> 0}`;
|
|
48
55
|
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
const
|
|
56
|
+
// Uploads in flight per send. A sealed (e2ee) file is encrypted on the device
|
|
57
|
+
// before it goes up, which is slow, so an e2ee send runs fewer at once.
|
|
58
|
+
const MEDIA_UPLOAD_CONCURRENCY = 4;
|
|
59
|
+
const MEDIA_E2EE_UPLOAD_CONCURRENCY = 2;
|
|
60
|
+
|
|
61
|
+
/** The local id of a tile until the upload gives it the server's `_id`. */
|
|
62
|
+
const localAttachmentId = (messageId, index) => `${messageId}:${index}`;
|
|
63
|
+
|
|
64
|
+
/** A tile without its local upload state (the file itself is unchanged). */
|
|
65
|
+
const withoutUploadState = attachment => {
|
|
66
|
+
const copy = {
|
|
67
|
+
...attachment
|
|
68
|
+
};
|
|
69
|
+
delete copy.uploadStatus;
|
|
70
|
+
delete copy.uploadProgress;
|
|
71
|
+
return copy;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/** A picker uri as a plain file uri (iOS asset-library scheme rewritten). */
|
|
75
|
+
const normalizeFileUri = uri => typeof uri === 'string' ? uri.replace(/^assets-library:\/\//, 'file://') : '';
|
|
76
|
+
|
|
77
|
+
/** The file's own uri when it is on this device, for the sender's preview. */
|
|
78
|
+
const localPreviewUrl = uri => {
|
|
79
|
+
const normalized = normalizeFileUri(uri);
|
|
80
|
+
return /^(file|content):\/\//.test(normalized) ? normalized : undefined;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/** One file as a failed send keeps it: plain fields only, so it persists. */
|
|
84
|
+
const toFailedFile = (file, fallbackType) => ({
|
|
85
|
+
uri: typeof file?.uri === 'string' ? file.uri : undefined,
|
|
86
|
+
type: file?.type || file?.mimeType || fallbackType || undefined,
|
|
87
|
+
name: file?.name || file?.fileName || undefined,
|
|
88
|
+
...(typeof file?.size === 'number' ? {
|
|
89
|
+
size: file.size
|
|
90
|
+
} : {}),
|
|
91
|
+
...(typeof file?.duration === 'number' ? {
|
|
92
|
+
duration: file.duration
|
|
93
|
+
} : {}),
|
|
94
|
+
...(typeof file?.waveForm === 'string' ? {
|
|
95
|
+
waveForm: file.waveForm
|
|
96
|
+
} : {})
|
|
97
|
+
});
|
|
53
98
|
export const useSendMessage = _configOverride => {
|
|
54
99
|
const {
|
|
55
100
|
client
|
|
@@ -300,47 +345,91 @@ export const useSendMessage = _configOverride => {
|
|
|
300
345
|
});
|
|
301
346
|
}
|
|
302
347
|
}, [editAction, client, user, langSource, dispatch, handleMessageSent, handleMessageEdited, handleMessageFailed, reduxStore]);
|
|
303
|
-
const sendMedia = useCallback(async (data, type, activeRoomJID, isReply = false, isChecked = false, mainMessage = '', existingId) => {
|
|
348
|
+
const sendMedia = useCallback(async (data, type, activeRoomJID, isReply = false, isChecked = false, mainMessage = '', existingId, caption) => {
|
|
304
349
|
if (!canSendMediaToRoom(activeRoomJID)) {
|
|
305
350
|
console.warn('media not sent: room is end-to-end encrypted');
|
|
306
|
-
return;
|
|
351
|
+
return false;
|
|
307
352
|
}
|
|
308
353
|
client?.onCriticalSend?.(activeRoomJID);
|
|
309
354
|
|
|
310
|
-
//
|
|
355
|
+
// One send = one message, whatever the caller handed us: a single file,
|
|
356
|
+
// the composer's array, or the saved list on a retry. Extra files past
|
|
357
|
+
// the cap are dropped here as well as in the composer.
|
|
358
|
+
const files = (Array.isArray(data) ? data : [data]).filter(file => !!file && typeof file === 'object').slice(0, mediaFileCap(config));
|
|
359
|
+
if (files.length === 0) {
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
const first = files[0];
|
|
363
|
+
const isE2ee = shouldEncrypt(activeRoomJID);
|
|
364
|
+
const sizeLimit = mediaSizeLimit(config, isE2ee ? MAX_SEAL_BYTES : undefined);
|
|
365
|
+
const captionFields = normalizeCaptionInput(caption);
|
|
366
|
+
const id = existingId || `send-media-message:${uuidv4()}`;
|
|
367
|
+
|
|
368
|
+
// Everything a retry needs to replay this send, in selection order.
|
|
369
|
+
const failedPayload = () => ({
|
|
370
|
+
kind: 'media',
|
|
371
|
+
id,
|
|
372
|
+
roomJID: activeRoomJID,
|
|
373
|
+
data: toFailedFile(first, type),
|
|
374
|
+
type,
|
|
375
|
+
isReply,
|
|
376
|
+
isChecked,
|
|
377
|
+
mainMessage,
|
|
378
|
+
files: files.map(file => toFailedFile(file, type)),
|
|
379
|
+
...(captionFields ? {
|
|
380
|
+
caption: captionFields.caption,
|
|
381
|
+
...(captionFields.mentions ? {
|
|
382
|
+
captionMentions: captionFields.mentions
|
|
383
|
+
} : {})
|
|
384
|
+
} : {})
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
// Defensive size guard on every send path (the composer blocks it at
|
|
311
388
|
// pick time, but retries replay the saved payload straight here).
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
389
|
+
const oversize = files.find(file => typeof file?.size === 'number' && file.size > sizeLimit);
|
|
390
|
+
if (oversize) {
|
|
391
|
+
console.warn('media exceeds the size cap - not uploading', {
|
|
392
|
+
name: oversize?.name,
|
|
393
|
+
size: oversize?.size
|
|
316
394
|
});
|
|
395
|
+
const error = new Error(`File exceeds the ${Math.round(sizeLimit / (1024 * 1024))} MB upload limit`);
|
|
317
396
|
if (existingId) {
|
|
318
|
-
dispatch(markMessageFailed(
|
|
319
|
-
kind: 'media',
|
|
320
|
-
id: existingId,
|
|
321
|
-
roomJID: activeRoomJID,
|
|
322
|
-
data,
|
|
323
|
-
type,
|
|
324
|
-
isReply,
|
|
325
|
-
isChecked,
|
|
326
|
-
mainMessage
|
|
327
|
-
}));
|
|
397
|
+
dispatch(markMessageFailed(failedPayload()));
|
|
328
398
|
}
|
|
329
399
|
handleMessageFailed({
|
|
330
400
|
message: 'media',
|
|
331
401
|
roomJID: activeRoomJID,
|
|
332
|
-
error
|
|
402
|
+
error,
|
|
333
403
|
messageType: 'media'
|
|
334
404
|
});
|
|
335
|
-
return;
|
|
405
|
+
return false;
|
|
336
406
|
}
|
|
337
|
-
const id = existingId || `send-media-message:${uuidv4()}`;
|
|
338
407
|
const optimisticTimestamp = Date.now();
|
|
339
408
|
const optimisticDate = new Date(optimisticTimestamp).toISOString();
|
|
340
409
|
const selfId = user?.xmppUsername || user?.walletAddress || '';
|
|
341
410
|
const displayName = `${user?.firstName || ''} ${user?.lastName || ''}`.trim() || selfId;
|
|
342
|
-
const
|
|
343
|
-
const
|
|
411
|
+
const normalizedFileUri = normalizeFileUri(first?.uri);
|
|
412
|
+
const fileSizeStr = first?.size != null ? String(first.size) : '';
|
|
413
|
+
|
|
414
|
+
// One tile per file, in selection order. A tile's id is local until the
|
|
415
|
+
// upload returns the server's `_id`; the tile's upload state is keyed on it.
|
|
416
|
+
const tiles = files.map((file, index) => {
|
|
417
|
+
const localUrl = localPreviewUrl(file?.uri);
|
|
418
|
+
return {
|
|
419
|
+
attachmentId: localAttachmentId(id, index),
|
|
420
|
+
location: '',
|
|
421
|
+
mimetype: file?.type || file?.mimeType || type,
|
|
422
|
+
originalName: file?.name,
|
|
423
|
+
fileName: file?.name,
|
|
424
|
+
size: file?.size != null ? String(file.size) : undefined,
|
|
425
|
+
duration: file?.duration != null ? String(file.duration) : undefined,
|
|
426
|
+
...(localUrl ? {
|
|
427
|
+
localUrl
|
|
428
|
+
} : {}),
|
|
429
|
+
uploadStatus: 'uploading',
|
|
430
|
+
uploadProgress: 0
|
|
431
|
+
};
|
|
432
|
+
});
|
|
344
433
|
if (!config?.disableSentLogic && !existingId) {
|
|
345
434
|
dispatch(addRoomMessage({
|
|
346
435
|
roomJID: activeRoomJID,
|
|
@@ -360,17 +449,22 @@ export const useSendMessage = _configOverride => {
|
|
|
360
449
|
xmppId: id,
|
|
361
450
|
xmppFrom: `${activeRoomJID}/${selfId}`,
|
|
362
451
|
isMediafile: 'true',
|
|
363
|
-
fileName:
|
|
452
|
+
fileName: first?.name,
|
|
364
453
|
location: normalizedFileUri,
|
|
365
454
|
locationPreview: normalizedFileUri,
|
|
366
455
|
mimetype: type,
|
|
367
|
-
originalName:
|
|
456
|
+
originalName: first?.name,
|
|
368
457
|
size: fileSizeStr,
|
|
369
|
-
duration:
|
|
370
|
-
waveForm:
|
|
458
|
+
duration: first?.duration,
|
|
459
|
+
waveForm: first?.waveForm,
|
|
460
|
+
attachments: tiles,
|
|
371
461
|
isReply: isReply ? 'true' : 'false',
|
|
372
462
|
showInChannel: `${isChecked}`,
|
|
373
|
-
mainMessage
|
|
463
|
+
mainMessage,
|
|
464
|
+
...(captionFields ? {
|
|
465
|
+
caption: captionFields.caption,
|
|
466
|
+
captionMentions: captionFields.mentions
|
|
467
|
+
} : {})
|
|
374
468
|
}
|
|
375
469
|
}));
|
|
376
470
|
dispatch(addMessageToHeap({
|
|
@@ -391,31 +485,25 @@ export const useSendMessage = _configOverride => {
|
|
|
391
485
|
}));
|
|
392
486
|
}
|
|
393
487
|
|
|
394
|
-
// Silent-failure watchdog for media-send (bug #18).
|
|
395
|
-
//
|
|
396
|
-
//
|
|
397
|
-
|
|
398
|
-
|
|
488
|
+
// Silent-failure watchdog for media-send (bug #18). Re-armed on every
|
|
489
|
+
// retry, so a retried send gets its own window. While files are still
|
|
490
|
+
// going up it is not a failure: it waits one more window instead.
|
|
491
|
+
const armWatchdog = () => setTimeout(() => {
|
|
492
|
+
if (running) {
|
|
493
|
+
mediaWatchdog = armWatchdog();
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
399
496
|
const state = reduxStore.getState();
|
|
400
497
|
const roomMsgs = state.rooms?.rooms?.[activeRoomJID]?.messages || [];
|
|
401
498
|
const stillPending = roomMsgs.find(m => m.id === id && m.pending);
|
|
402
499
|
const alreadyFailed = state.roomHeapSlice?.failedMessages?.[id];
|
|
403
500
|
if (stillPending && !alreadyFailed) {
|
|
404
|
-
console.warn(`Media-send watchdog fired
|
|
501
|
+
console.warn(`Media-send watchdog fired - ${id} stuck pending > ${MEDIA_PENDING_WATCHDOG_MS}ms; marking failed`);
|
|
405
502
|
// See the text-send watchdog above: drop the buffered replay so
|
|
406
503
|
// a later onOnline flush can't silently resend behind the
|
|
407
504
|
// "failed / tap to retry" bubble.
|
|
408
505
|
removeOutboundSend(id);
|
|
409
|
-
dispatch(markMessageFailed(
|
|
410
|
-
kind: 'media',
|
|
411
|
-
id,
|
|
412
|
-
roomJID: activeRoomJID,
|
|
413
|
-
data,
|
|
414
|
-
type,
|
|
415
|
-
isReply,
|
|
416
|
-
isChecked,
|
|
417
|
-
mainMessage
|
|
418
|
-
}));
|
|
506
|
+
dispatch(markMessageFailed(failedPayload()));
|
|
419
507
|
handleMessageFailed({
|
|
420
508
|
message: 'media',
|
|
421
509
|
roomJID: activeRoomJID,
|
|
@@ -424,138 +512,220 @@ export const useSendMessage = _configOverride => {
|
|
|
424
512
|
});
|
|
425
513
|
}
|
|
426
514
|
}, MEDIA_PENDING_WATCHDOG_MS);
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
//
|
|
431
|
-
//
|
|
432
|
-
//
|
|
433
|
-
const
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
515
|
+
const uploaded = files.map(() => undefined);
|
|
516
|
+
const rawResults = files.map(() => undefined);
|
|
517
|
+
const sealedUploads = files.map(() => undefined);
|
|
518
|
+
// Per-file key material, kept apart from the sealed upload: the upload
|
|
519
|
+
// is disposed once the message is assembled, but a send that fails after
|
|
520
|
+
// that point is retried and must still carry every key.
|
|
521
|
+
const keyMaterials = files.map(() => undefined);
|
|
522
|
+
let delivered = false;
|
|
523
|
+
let running = false;
|
|
524
|
+
const concurrency = isE2ee ? MEDIA_E2EE_UPLOAD_CONCURRENCY : MEDIA_UPLOAD_CONCURRENCY;
|
|
525
|
+
let mediaWatchdog = armWatchdog();
|
|
526
|
+
const publishTile = (index, patch) => {
|
|
527
|
+
const attachmentId = tiles[index]?.attachmentId;
|
|
528
|
+
if (config?.disableSentLogic || !attachmentId) {
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
dispatch(setMediaUploadState({
|
|
532
|
+
roomJID: activeRoomJID,
|
|
533
|
+
messageId: id,
|
|
534
|
+
attachmentId,
|
|
535
|
+
...patch
|
|
536
|
+
}));
|
|
437
537
|
};
|
|
438
|
-
|
|
439
|
-
const
|
|
440
|
-
|
|
538
|
+
const missingIndices = () => uploaded.reduce((acc, item, index) => item ? acc : [...acc, index], []);
|
|
539
|
+
const uploadOne = async index => {
|
|
540
|
+
const file = files[index];
|
|
541
|
+
publishTile(index, {
|
|
542
|
+
uploadStatus: 'uploading',
|
|
543
|
+
uploadProgress: 0
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
// Helps RN's FormData polyfill build a proper multipart/file part
|
|
547
|
+
// (instead of serialising the JS object as JSON). On iOS the picker
|
|
548
|
+
// can return ph:// or assets-library:// URIs; coerce to file:// when
|
|
549
|
+
// possible so the body is a real blob.
|
|
550
|
+
const fileBlob = {
|
|
551
|
+
uri: normalizeFileUri(file?.uri) || file?.uri,
|
|
552
|
+
type: file?.type || file?.mimeType || type || 'application/octet-stream',
|
|
553
|
+
name: file?.name || file?.fileName || `media_${Date.now()}_${index}`
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
// In an e2ee room each file is sealed before it leaves the device:
|
|
557
|
+
// the server only ever sees opaque bytes under a random name. The
|
|
558
|
+
// per-file keys ride in the encrypted <body> (see sendMediaMessage).
|
|
559
|
+
if (isE2ee) {
|
|
560
|
+
sealedUploads[index]?.dispose();
|
|
561
|
+
sealedUploads[index] = undefined;
|
|
441
562
|
const {
|
|
442
563
|
sealPickedFile
|
|
443
564
|
} = require('../e2ee/sealedFiles');
|
|
444
|
-
|
|
565
|
+
sealedUploads[index] = await sealPickedFile({
|
|
445
566
|
uri: fileBlob.uri,
|
|
446
567
|
name: fileBlob.name,
|
|
447
568
|
type: fileBlob.type,
|
|
448
|
-
size: typeof
|
|
569
|
+
size: typeof file?.size === 'number' ? file.size : undefined
|
|
449
570
|
});
|
|
450
571
|
}
|
|
572
|
+
const sealed = sealedUploads[index];
|
|
573
|
+
if (sealed) {
|
|
574
|
+
keyMaterials[index] = sealed.keyMaterial;
|
|
575
|
+
}
|
|
451
576
|
const part = sealed ? {
|
|
452
577
|
uri: sealed.uri,
|
|
453
578
|
type: 'application/octet-stream',
|
|
454
579
|
name: sealed.name
|
|
455
580
|
} : fileBlob;
|
|
456
581
|
const options = {
|
|
457
|
-
clientEncrypted: !!sealed
|
|
582
|
+
clientEncrypted: !!sealed,
|
|
583
|
+
onProgress: percent => publishTile(index, {
|
|
584
|
+
uploadProgress: percent
|
|
585
|
+
})
|
|
586
|
+
};
|
|
587
|
+
const post = field => {
|
|
588
|
+
const fd = new FormData();
|
|
589
|
+
fd.append(field, part);
|
|
590
|
+
return uploadFileV2(fd, activeRoomJID, options);
|
|
458
591
|
};
|
|
459
|
-
|
|
460
|
-
fd.append('files', part);
|
|
592
|
+
let response;
|
|
461
593
|
try {
|
|
462
|
-
|
|
594
|
+
response = await post('files');
|
|
463
595
|
} catch (err) {
|
|
464
|
-
if (err?.response?.status
|
|
465
|
-
|
|
466
|
-
name: part.name,
|
|
467
|
-
type: part.type
|
|
468
|
-
});
|
|
469
|
-
const fd2 = new FormData();
|
|
470
|
-
fd2.append('file', part);
|
|
471
|
-
return await uploadFileV2(fd2, activeRoomJID, options);
|
|
596
|
+
if (err?.response?.status !== 500) {
|
|
597
|
+
throw err;
|
|
472
598
|
}
|
|
473
|
-
|
|
599
|
+
console.warn('upload "files" field returned 500 - retrying with "file" (singular)', {
|
|
600
|
+
name: part.name,
|
|
601
|
+
type: part.type
|
|
602
|
+
});
|
|
603
|
+
response = await post('file');
|
|
474
604
|
}
|
|
605
|
+
const raw = Array.isArray(response?.data?.results) ? response.data.results[0] : undefined;
|
|
606
|
+
if (!raw) {
|
|
607
|
+
throw new Error('media_upload_empty_response');
|
|
608
|
+
}
|
|
609
|
+
uploaded[index] = toUploadedAttachment(raw);
|
|
610
|
+
rawResults[index] = raw;
|
|
611
|
+
// The bytes are up; the message waits for its siblings.
|
|
612
|
+
publishTile(index, {
|
|
613
|
+
uploadStatus: 'uploading',
|
|
614
|
+
uploadProgress: 100
|
|
615
|
+
});
|
|
475
616
|
};
|
|
476
|
-
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
617
|
+
const sendAssembled = async () => {
|
|
618
|
+
const raws = rawResults;
|
|
619
|
+
const attachments = uploaded;
|
|
620
|
+
const head = raws[0];
|
|
621
|
+
const messagePayload = {
|
|
622
|
+
firstName: user.firstName,
|
|
623
|
+
lastName: user.lastName,
|
|
624
|
+
walletAddress: user.walletAddress,
|
|
625
|
+
createdAt: head.createdAt,
|
|
626
|
+
expiresAt: head.expiresAt,
|
|
627
|
+
fileName: head.filename,
|
|
628
|
+
isVisible: head?.isVisible,
|
|
629
|
+
location: head.location,
|
|
630
|
+
locationPreview: head.locationPreview,
|
|
631
|
+
mimetype: head.mimetype,
|
|
632
|
+
originalName: head?.originalname,
|
|
633
|
+
ownerKey: head?.ownerKey,
|
|
634
|
+
size: head.size,
|
|
635
|
+
duration: head?.duration ?? first?.duration,
|
|
636
|
+
waveForm: first?.waveForm,
|
|
637
|
+
updatedAt: head?.updatedAt,
|
|
638
|
+
userId: head?.userId,
|
|
639
|
+
attachmentId: head?._id,
|
|
640
|
+
// Only when there is more than one file: sendMediaMessage stamps
|
|
641
|
+
// the list on <data> and leaves a single-file stanza untouched.
|
|
642
|
+
attachments: attachments.length > 1 ? attachments : undefined,
|
|
643
|
+
caption: captionFields?.caption,
|
|
644
|
+
captionMentions: captionFields?.mentions,
|
|
645
|
+
wrappable: true,
|
|
646
|
+
roomJid: activeRoomJID,
|
|
647
|
+
showInChannel: isChecked,
|
|
648
|
+
isReply,
|
|
649
|
+
mainMessage,
|
|
650
|
+
isPrivate: head?.isPrivate,
|
|
651
|
+
__v: head.__v,
|
|
652
|
+
// One key per sealed file, in selection order.
|
|
653
|
+
e2eeKeys: isE2ee ? keyMaterials : undefined
|
|
654
|
+
};
|
|
655
|
+
|
|
656
|
+
// The local copies of sealed files are remembered for the sender and
|
|
657
|
+
// the scratch ciphertext is dropped.
|
|
658
|
+
if (isE2ee) {
|
|
659
|
+
const {
|
|
660
|
+
rememberOpened
|
|
661
|
+
} = require('../e2ee/sealedFiles');
|
|
662
|
+
for (let index = 0; index < files.length; index++) {
|
|
663
|
+
const item = attachments[index];
|
|
514
664
|
try {
|
|
515
|
-
const {
|
|
516
|
-
rememberOpened
|
|
517
|
-
} = require('../e2ee/sealedFiles');
|
|
518
665
|
await rememberOpened(item.location, {
|
|
519
|
-
uri:
|
|
520
|
-
name:
|
|
521
|
-
type:
|
|
522
|
-
size: typeof
|
|
666
|
+
uri: normalizeFileUri(files[index]?.uri) || files[index]?.uri,
|
|
667
|
+
name: files[index]?.name,
|
|
668
|
+
type: files[index]?.type || files[index]?.mimeType || type,
|
|
669
|
+
size: typeof files[index]?.size === 'number' ? files[index].size : undefined
|
|
523
670
|
});
|
|
524
671
|
} catch (err) {
|
|
525
672
|
console.warn('could not keep the sent attachment', err);
|
|
526
673
|
}
|
|
527
|
-
|
|
674
|
+
sealedUploads[index]?.dispose();
|
|
675
|
+
sealedUploads[index] = undefined;
|
|
528
676
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// Replace the optimistic tiles with the uploaded files. A sealed
|
|
680
|
+
// message keeps its local tiles (the server copies are ciphertext) and
|
|
681
|
+
// only drops the upload state. `pending` is left alone: only the
|
|
682
|
+
// server echo confirms the message.
|
|
683
|
+
if (!config?.disableSentLogic) {
|
|
684
|
+
dispatch(setMessageAttachments({
|
|
685
|
+
roomJID: activeRoomJID,
|
|
686
|
+
messageId: id,
|
|
687
|
+
attachments: isE2ee ? tiles.map(withoutUploadState) : attachments.map((attachment, index) => {
|
|
688
|
+
const localUrl = tiles[index]?.localUrl;
|
|
689
|
+
return localUrl ? {
|
|
690
|
+
...attachment,
|
|
691
|
+
localUrl
|
|
692
|
+
} : attachment;
|
|
693
|
+
})
|
|
694
|
+
}));
|
|
695
|
+
}
|
|
696
|
+
const effectiveMediaClient = [client, getGlobalXmppClient(), store.getState()?.chatSettingStore?.client].find(c => c && c.status === 'online');
|
|
697
|
+
if (effectiveMediaClient) {
|
|
698
|
+
effectiveMediaClient.setActiveRoomJid?.(activeRoomJID);
|
|
699
|
+
await Promise.resolve(effectiveMediaClient.prioritizeRoomPresence?.(activeRoomJID)).catch(() => false);
|
|
700
|
+
effectiveMediaClient.sendMediaMessageStanza(activeRoomJID, messagePayload, id);
|
|
701
|
+
try {
|
|
702
|
+
effectiveMediaClient.ensureStreamAlive?.();
|
|
703
|
+
} catch {}
|
|
704
|
+
} else {
|
|
532
705
|
// Same online-only guard as the text path: a surviving-but-offline
|
|
533
|
-
// client must NOT be used
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
} catch {}
|
|
548
|
-
enqueueOutboundSend({
|
|
549
|
-
optimisticId: id,
|
|
550
|
-
roomJID: activeRoomJID,
|
|
551
|
-
enqueuedAt: Date.now(),
|
|
552
|
-
// Match the media watchdog so a reconnect within the (longer)
|
|
553
|
-
// media window still auto-replays the buffered stanza.
|
|
554
|
-
ttlMs: MEDIA_PENDING_WATCHDOG_MS,
|
|
555
|
-
send: c => c.sendMediaMessageStanza(activeRoomJID, messagePayload, id)
|
|
556
|
-
});
|
|
557
|
-
}
|
|
706
|
+
// client must NOT be used. Buffer the stanza for the reconnect flush.
|
|
707
|
+
const anyMediaClient = client || getGlobalXmppClient() || store.getState()?.chatSettingStore?.client;
|
|
708
|
+
try {
|
|
709
|
+
anyMediaClient?.forceReconnect?.();
|
|
710
|
+
} catch {}
|
|
711
|
+
enqueueOutboundSend({
|
|
712
|
+
optimisticId: id,
|
|
713
|
+
roomJID: activeRoomJID,
|
|
714
|
+
enqueuedAt: Date.now(),
|
|
715
|
+
// Match the media watchdog so a reconnect within the (longer)
|
|
716
|
+
// media window still auto-replays the buffered stanza.
|
|
717
|
+
ttlMs: MEDIA_PENDING_WATCHDOG_MS,
|
|
718
|
+
send: c => c.sendMediaMessageStanza(activeRoomJID, messagePayload, id)
|
|
719
|
+
});
|
|
558
720
|
}
|
|
721
|
+
|
|
722
|
+
// The upload time is spent: the wait for the server echo gets a full
|
|
723
|
+
// watchdog window of its own, not what was left of the upload's.
|
|
724
|
+
clearTimeout(mediaWatchdog);
|
|
725
|
+
mediaWatchdog = armWatchdog();
|
|
726
|
+
delivered = true;
|
|
727
|
+
unregisterMediaRetry(id);
|
|
728
|
+
dispatch(clearMessageFailure(id));
|
|
559
729
|
await handleMessageSent({
|
|
560
730
|
message: 'media',
|
|
561
731
|
roomJID: activeRoomJID,
|
|
@@ -568,59 +738,73 @@ export const useSendMessage = _configOverride => {
|
|
|
568
738
|
fileData: data,
|
|
569
739
|
fileType: type,
|
|
570
740
|
messageId: id,
|
|
571
|
-
uploadResults:
|
|
741
|
+
uploadResults: raws
|
|
572
742
|
}
|
|
573
743
|
});
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
// was rejected (axios collapses the message to "Request failed
|
|
581
|
-
// with status code N"; the actual reason lives in
|
|
582
|
-
// `error.response.data`).
|
|
583
|
-
const uriStr = typeof data?.uri === 'string' ? data.uri : '';
|
|
584
|
-
const uriScheme = uriStr.match(/^([a-z]+):/i)?.[1] ?? 'unknown';
|
|
585
|
-
console.error('upload failed', {
|
|
586
|
-
status: error?.response?.status,
|
|
587
|
-
statusText: error?.response?.statusText,
|
|
588
|
-
serverBody: error?.response?.data,
|
|
589
|
-
requestUrl: error?.config?.url,
|
|
590
|
-
axiosMessage: error?.message,
|
|
591
|
-
axiosCode: error?.code,
|
|
592
|
-
axiosCauseMessage: error?.cause?.message,
|
|
593
|
-
uri: uriStr,
|
|
594
|
-
uriScheme,
|
|
595
|
-
mime: fileBlob.type,
|
|
596
|
-
fileSizeBytes: data?.size ?? null,
|
|
597
|
-
fileName: fileBlob.name,
|
|
598
|
-
platformOS: Platform.OS,
|
|
599
|
-
platformVersion: Platform.Version
|
|
600
|
-
});
|
|
601
|
-
dispatch(markMessageFailed({
|
|
602
|
-
kind: 'media',
|
|
603
|
-
id,
|
|
604
|
-
roomJID: activeRoomJID,
|
|
605
|
-
data,
|
|
606
|
-
type,
|
|
607
|
-
isReply,
|
|
608
|
-
isChecked,
|
|
609
|
-
mainMessage
|
|
610
|
-
}));
|
|
744
|
+
};
|
|
745
|
+
const failMessage = error => {
|
|
746
|
+
console.error('Media send failed:', error);
|
|
747
|
+
// Every file stays held: the bubble shows Failed, and Retry replays
|
|
748
|
+
// the files that did not make it (in-session) or the whole list.
|
|
749
|
+
dispatch(markMessageFailed(failedPayload()));
|
|
611
750
|
handleMessageFailed({
|
|
612
751
|
message: 'media',
|
|
613
752
|
roomJID: activeRoomJID,
|
|
614
753
|
error: error,
|
|
615
754
|
messageType: 'media'
|
|
616
755
|
});
|
|
617
|
-
}
|
|
756
|
+
};
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* Uploads `indices` (a retry passes only the files that failed), then
|
|
760
|
+
* sends once nothing is missing. Any failure leaves the message failed
|
|
761
|
+
* with its tiles marked, ready for a retry.
|
|
762
|
+
*/
|
|
763
|
+
const run = async indices => {
|
|
764
|
+
if (delivered || running) {
|
|
765
|
+
return false;
|
|
766
|
+
}
|
|
767
|
+
running = true;
|
|
768
|
+
clearTimeout(mediaWatchdog);
|
|
769
|
+
mediaWatchdog = armWatchdog();
|
|
770
|
+
try {
|
|
771
|
+
await mapWithConcurrency(indices, concurrency, async index => {
|
|
772
|
+
try {
|
|
773
|
+
await uploadOne(index);
|
|
774
|
+
} catch (error) {
|
|
775
|
+
console.error('upload failed', {
|
|
776
|
+
status: error?.response?.status,
|
|
777
|
+
serverBody: error?.response?.data,
|
|
778
|
+
axiosMessage: error?.message,
|
|
779
|
+
fileName: files[index]?.name,
|
|
780
|
+
fileSizeBytes: files[index]?.size ?? null,
|
|
781
|
+
platformOS: Platform.OS
|
|
782
|
+
});
|
|
783
|
+
publishTile(index, {
|
|
784
|
+
uploadStatus: 'failed',
|
|
785
|
+
uploadProgress: 0
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
});
|
|
789
|
+
if (missingIndices().length > 0) {
|
|
790
|
+
throw new Error('media_upload_failed');
|
|
791
|
+
}
|
|
792
|
+
await sendAssembled();
|
|
793
|
+
return true;
|
|
794
|
+
} catch (error) {
|
|
795
|
+
failMessage(error);
|
|
796
|
+
return false;
|
|
797
|
+
} finally {
|
|
798
|
+
running = false;
|
|
799
|
+
}
|
|
800
|
+
};
|
|
801
|
+
registerMediaRetry(id, indices => run(missingIndices().filter(position => indices === undefined || indices.includes(position))));
|
|
802
|
+
return run(files.map((_, index) => index));
|
|
618
803
|
}, [client, config, user, dispatch, handleMessageSent, handleMessageFailed, reduxStore]);
|
|
619
804
|
|
|
620
|
-
// Retry a previously-failed send
|
|
621
|
-
//
|
|
622
|
-
//
|
|
623
|
-
// it. No-op when the id isn't in the failed map.
|
|
805
|
+
// Retry a previously-failed send. An in-session send re-runs only the files
|
|
806
|
+
// still missing (its runner is still registered); after a restart the
|
|
807
|
+
// saved payload is replayed as a whole. No-op when the id isn't failed.
|
|
624
808
|
const retryMessage = useCallback(async failedId => {
|
|
625
809
|
const payload = reduxStore.getState().roomHeapSlice?.failedMessages?.[failedId];
|
|
626
810
|
if (!payload) {
|
|
@@ -649,9 +833,17 @@ export const useSendMessage = _configOverride => {
|
|
|
649
833
|
// Re-use the failed message's id so the retry resends in place
|
|
650
834
|
// instead of spawning a duplicate bubble.
|
|
651
835
|
failedId);
|
|
652
|
-
|
|
653
|
-
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
if (hasMediaRetry(failedId)) {
|
|
839
|
+
await retryMediaSend(failedId);
|
|
840
|
+
return;
|
|
654
841
|
}
|
|
842
|
+
const list = Array.isArray(payload.files) && payload.files.length > 0 ? payload.files : payload.data;
|
|
843
|
+
await sendMedia(list, payload.type, payload.roomJID, payload.isReply, payload.isChecked, payload.mainMessage, failedId, payload.caption ? {
|
|
844
|
+
text: payload.caption,
|
|
845
|
+
mentions: payload.captionMentions
|
|
846
|
+
} : undefined);
|
|
655
847
|
}, [reduxStore, dispatch, handleMessageRetry, sendMessage, sendMedia]);
|
|
656
848
|
|
|
657
849
|
// ChatRoom/ThreadWrapper consume this as the "edit branch" of send.
|