@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
|
@@ -3,21 +3,38 @@ import { Platform } from 'react-native';
|
|
|
3
3
|
import { v4 as uuidv4 } from 'uuid';
|
|
4
4
|
import { useXmppClient } from '../context/xmppProvider';
|
|
5
5
|
import { useDispatch, useSelector, useStore } from 'react-redux';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
addRoomMessage,
|
|
8
|
+
setEditAction,
|
|
9
|
+
setMediaUploadState,
|
|
10
|
+
setMessageAttachments,
|
|
11
|
+
} from '../roomStore/roomsSlice';
|
|
7
12
|
import {
|
|
8
13
|
addMessageToHeap,
|
|
9
14
|
clearMessageFailure,
|
|
10
15
|
markMessageFailed,
|
|
11
16
|
} from '../roomStore/roomHeapSlice';
|
|
17
|
+
import type { FailedMediaFile, FailedMessagePayload } from '../roomStore/roomHeapSlice';
|
|
12
18
|
import { uploadFileV2 } from '../networking/api-requests/auth.api';
|
|
13
19
|
import { enqueueOutboundSend, removeOutboundSend } from '../networking/outboundQueue';
|
|
14
20
|
import { RootState, store } from '../roomStore';
|
|
15
21
|
import { getGlobalXmppClient } from '../utils/clientRegistry';
|
|
16
22
|
import { useEventHandlers } from './useEventHandlers';
|
|
17
|
-
import type { IConfig, IMessage } from '../types/types';
|
|
23
|
+
import type { IAttachment, IConfig, IMessage } from '../types/types';
|
|
18
24
|
import { canEditMessage, canSendMediaToRoom, canSendToRoom } from '../e2ee';
|
|
19
25
|
import { shouldEncrypt } from '../e2ee/send';
|
|
20
26
|
import type { SealedUpload } from '../e2ee/sealedFiles';
|
|
27
|
+
import { MAX_SEAL_BYTES } from '../e2ee/limits';
|
|
28
|
+
import { mapWithConcurrency } from '../helpers/mapWithConcurrency';
|
|
29
|
+
import { normalizeCaptionInput, type CaptionInput } from '../helpers/caption';
|
|
30
|
+
import { toUploadedAttachment } from '../helpers/attachments';
|
|
31
|
+
import { mediaFileCap, mediaSizeLimit } from '../helpers/mediaLimits';
|
|
32
|
+
import {
|
|
33
|
+
hasMediaRetry,
|
|
34
|
+
registerMediaRetry,
|
|
35
|
+
retryMediaSend,
|
|
36
|
+
unregisterMediaRetry,
|
|
37
|
+
} from '../helpers/mediaSendRetry';
|
|
21
38
|
|
|
22
39
|
// Watchdog: if a message stays "pending" (no server echo) for this
|
|
23
40
|
// long after the optimistic dispatch, mark it failed so the bubble
|
|
@@ -52,10 +69,42 @@ let __sendIdSeq = 0;
|
|
|
52
69
|
const nextStanzaId = (prefix: string): string =>
|
|
53
70
|
`${prefix}-${Date.now()}-${(__sendIdSeq = (__sendIdSeq + 1) >>> 0)}`;
|
|
54
71
|
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
const
|
|
72
|
+
// Uploads in flight per send. A sealed (e2ee) file is encrypted on the device
|
|
73
|
+
// before it goes up, which is slow, so an e2ee send runs fewer at once.
|
|
74
|
+
const MEDIA_UPLOAD_CONCURRENCY = 4;
|
|
75
|
+
const MEDIA_E2EE_UPLOAD_CONCURRENCY = 2;
|
|
76
|
+
|
|
77
|
+
/** The local id of a tile until the upload gives it the server's `_id`. */
|
|
78
|
+
const localAttachmentId = (messageId: string, index: number): string =>
|
|
79
|
+
`${messageId}:${index}`;
|
|
80
|
+
|
|
81
|
+
/** A tile without its local upload state (the file itself is unchanged). */
|
|
82
|
+
const withoutUploadState = (attachment: IAttachment): IAttachment => {
|
|
83
|
+
const copy: IAttachment = { ...attachment };
|
|
84
|
+
delete copy.uploadStatus;
|
|
85
|
+
delete copy.uploadProgress;
|
|
86
|
+
return copy;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/** A picker uri as a plain file uri (iOS asset-library scheme rewritten). */
|
|
90
|
+
const normalizeFileUri = (uri: unknown): string =>
|
|
91
|
+
typeof uri === 'string' ? uri.replace(/^assets-library:\/\//, 'file://') : '';
|
|
92
|
+
|
|
93
|
+
/** The file's own uri when it is on this device, for the sender's preview. */
|
|
94
|
+
const localPreviewUrl = (uri: unknown): string | undefined => {
|
|
95
|
+
const normalized = normalizeFileUri(uri);
|
|
96
|
+
return /^(file|content):\/\//.test(normalized) ? normalized : undefined;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/** One file as a failed send keeps it: plain fields only, so it persists. */
|
|
100
|
+
const toFailedFile = (file: any, fallbackType: string): FailedMediaFile => ({
|
|
101
|
+
uri: typeof file?.uri === 'string' ? file.uri : undefined,
|
|
102
|
+
type: file?.type || file?.mimeType || fallbackType || undefined,
|
|
103
|
+
name: file?.name || file?.fileName || undefined,
|
|
104
|
+
...(typeof file?.size === 'number' ? { size: file.size } : {}),
|
|
105
|
+
...(typeof file?.duration === 'number' ? { duration: file.duration } : {}),
|
|
106
|
+
...(typeof file?.waveForm === 'string' ? { waveForm: file.waveForm } : {}),
|
|
107
|
+
});
|
|
59
108
|
|
|
60
109
|
export const useSendMessage = (_configOverride?: IConfig) => {
|
|
61
110
|
const { client } = useXmppClient();
|
|
@@ -381,58 +430,100 @@ export const useSendMessage = (_configOverride?: IConfig) => {
|
|
|
381
430
|
isReply: boolean = false,
|
|
382
431
|
isChecked: boolean = false,
|
|
383
432
|
mainMessage: string = '',
|
|
384
|
-
existingId?: string
|
|
385
|
-
|
|
433
|
+
existingId?: string,
|
|
434
|
+
caption?: CaptionInput
|
|
435
|
+
): Promise<boolean> => {
|
|
386
436
|
if (!canSendMediaToRoom(activeRoomJID)) {
|
|
387
437
|
console.warn('media not sent: room is end-to-end encrypted');
|
|
388
|
-
return;
|
|
438
|
+
return false;
|
|
389
439
|
}
|
|
390
440
|
|
|
391
441
|
client?.onCriticalSend?.(activeRoomJID);
|
|
392
442
|
|
|
393
|
-
//
|
|
443
|
+
// One send = one message, whatever the caller handed us: a single file,
|
|
444
|
+
// the composer's array, or the saved list on a retry. Extra files past
|
|
445
|
+
// the cap are dropped here as well as in the composer.
|
|
446
|
+
const files: any[] = (Array.isArray(data) ? data : [data])
|
|
447
|
+
.filter((file) => !!file && typeof file === 'object')
|
|
448
|
+
.slice(0, mediaFileCap(config));
|
|
449
|
+
if (files.length === 0) {return false;}
|
|
450
|
+
const first = files[0];
|
|
451
|
+
|
|
452
|
+
const isE2ee = shouldEncrypt(activeRoomJID);
|
|
453
|
+
const sizeLimit = mediaSizeLimit(config, isE2ee ? MAX_SEAL_BYTES : undefined);
|
|
454
|
+
const captionFields = normalizeCaptionInput(caption);
|
|
455
|
+
const id = existingId || `send-media-message:${uuidv4()}`;
|
|
456
|
+
|
|
457
|
+
// Everything a retry needs to replay this send, in selection order.
|
|
458
|
+
const failedPayload = (): FailedMessagePayload => ({
|
|
459
|
+
kind: 'media',
|
|
460
|
+
id,
|
|
461
|
+
roomJID: activeRoomJID,
|
|
462
|
+
data: toFailedFile(first, type),
|
|
463
|
+
type,
|
|
464
|
+
isReply,
|
|
465
|
+
isChecked,
|
|
466
|
+
mainMessage,
|
|
467
|
+
files: files.map((file) => toFailedFile(file, type)),
|
|
468
|
+
...(captionFields
|
|
469
|
+
? {
|
|
470
|
+
caption: captionFields.caption,
|
|
471
|
+
...(captionFields.mentions ? { captionMentions: captionFields.mentions } : {}),
|
|
472
|
+
}
|
|
473
|
+
: {}),
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
// Defensive size guard on every send path (the composer blocks it at
|
|
394
477
|
// pick time, but retries replay the saved payload straight here).
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
478
|
+
const oversize = files.find(
|
|
479
|
+
(file) => typeof file?.size === 'number' && file.size > sizeLimit
|
|
480
|
+
);
|
|
481
|
+
if (oversize) {
|
|
482
|
+
console.warn('media exceeds the size cap - not uploading', {
|
|
483
|
+
name: oversize?.name,
|
|
484
|
+
size: oversize?.size,
|
|
399
485
|
});
|
|
486
|
+
const error = new Error(
|
|
487
|
+
`File exceeds the ${Math.round(sizeLimit / (1024 * 1024))} MB upload limit`
|
|
488
|
+
);
|
|
400
489
|
if (existingId) {
|
|
401
|
-
dispatch(
|
|
402
|
-
markMessageFailed({
|
|
403
|
-
kind: 'media',
|
|
404
|
-
id: existingId,
|
|
405
|
-
roomJID: activeRoomJID,
|
|
406
|
-
data,
|
|
407
|
-
type,
|
|
408
|
-
isReply,
|
|
409
|
-
isChecked,
|
|
410
|
-
mainMessage,
|
|
411
|
-
})
|
|
412
|
-
);
|
|
490
|
+
dispatch(markMessageFailed(failedPayload()));
|
|
413
491
|
}
|
|
414
492
|
handleMessageFailed({
|
|
415
493
|
message: 'media',
|
|
416
494
|
roomJID: activeRoomJID,
|
|
417
|
-
error
|
|
495
|
+
error,
|
|
418
496
|
messageType: 'media',
|
|
419
497
|
});
|
|
420
|
-
return;
|
|
498
|
+
return false;
|
|
421
499
|
}
|
|
422
500
|
|
|
423
|
-
const id = existingId || `send-media-message:${uuidv4()}`;
|
|
424
501
|
const optimisticTimestamp = Date.now();
|
|
425
502
|
const optimisticDate = new Date(optimisticTimestamp).toISOString();
|
|
426
503
|
const selfId =
|
|
427
504
|
(user as any)?.xmppUsername || (user as any)?.walletAddress || '';
|
|
428
505
|
const displayName =
|
|
429
506
|
`${user?.firstName || ''} ${user?.lastName || ''}`.trim() || selfId;
|
|
430
|
-
const
|
|
507
|
+
const normalizedFileUri = normalizeFileUri(first?.uri);
|
|
508
|
+
const fileSizeStr = first?.size != null ? String(first.size) : '';
|
|
431
509
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
510
|
+
// One tile per file, in selection order. A tile's id is local until the
|
|
511
|
+
// upload returns the server's `_id`; the tile's upload state is keyed on it.
|
|
512
|
+
const tiles: IAttachment[] = files.map((file, index) => {
|
|
513
|
+
const localUrl = localPreviewUrl(file?.uri);
|
|
514
|
+
return {
|
|
515
|
+
attachmentId: localAttachmentId(id, index),
|
|
516
|
+
location: '',
|
|
517
|
+
mimetype: file?.type || file?.mimeType || type,
|
|
518
|
+
originalName: file?.name,
|
|
519
|
+
fileName: file?.name,
|
|
520
|
+
size: file?.size != null ? String(file.size) : undefined,
|
|
521
|
+
duration: file?.duration != null ? String(file.duration) : undefined,
|
|
522
|
+
...(localUrl ? { localUrl } : {}),
|
|
523
|
+
uploadStatus: 'uploading',
|
|
524
|
+
uploadProgress: 0,
|
|
525
|
+
};
|
|
526
|
+
});
|
|
436
527
|
|
|
437
528
|
if (!config?.disableSentLogic && !existingId) {
|
|
438
529
|
dispatch(
|
|
@@ -454,17 +545,24 @@ export const useSendMessage = (_configOverride?: IConfig) => {
|
|
|
454
545
|
xmppId: id,
|
|
455
546
|
xmppFrom: `${activeRoomJID}/${selfId}`,
|
|
456
547
|
isMediafile: 'true',
|
|
457
|
-
fileName:
|
|
548
|
+
fileName: first?.name,
|
|
458
549
|
location: normalizedFileUri,
|
|
459
550
|
locationPreview: normalizedFileUri,
|
|
460
551
|
mimetype: type,
|
|
461
|
-
originalName:
|
|
552
|
+
originalName: first?.name,
|
|
462
553
|
size: fileSizeStr,
|
|
463
|
-
duration:
|
|
464
|
-
waveForm:
|
|
554
|
+
duration: first?.duration,
|
|
555
|
+
waveForm: first?.waveForm,
|
|
556
|
+
attachments: tiles,
|
|
465
557
|
isReply: isReply ? 'true' : 'false',
|
|
466
558
|
showInChannel: `${isChecked}`,
|
|
467
559
|
mainMessage,
|
|
560
|
+
...(captionFields
|
|
561
|
+
? {
|
|
562
|
+
caption: captionFields.caption,
|
|
563
|
+
captionMentions: captionFields.mentions,
|
|
564
|
+
}
|
|
565
|
+
: {}),
|
|
468
566
|
} as any as IMessage,
|
|
469
567
|
})
|
|
470
568
|
);
|
|
@@ -488,193 +586,267 @@ export const useSendMessage = (_configOverride?: IConfig) => {
|
|
|
488
586
|
);
|
|
489
587
|
}
|
|
490
588
|
|
|
491
|
-
// Silent-failure watchdog for media-send (bug #18).
|
|
492
|
-
//
|
|
493
|
-
//
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
`Media-send watchdog fired — ${id} stuck pending > ${PENDING_WATCHDOG_MS}ms; marking failed`
|
|
589
|
+
// Silent-failure watchdog for media-send (bug #18). Re-armed on every
|
|
590
|
+
// retry, so a retried send gets its own window. While files are still
|
|
591
|
+
// going up it is not a failure: it waits one more window instead.
|
|
592
|
+
const armWatchdog = (): ReturnType<typeof setTimeout> =>
|
|
593
|
+
setTimeout(() => {
|
|
594
|
+
if (running) {
|
|
595
|
+
mediaWatchdog = armWatchdog();
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
const state = reduxStore.getState();
|
|
599
|
+
const roomMsgs = state.rooms?.rooms?.[activeRoomJID]?.messages || [];
|
|
600
|
+
const stillPending = roomMsgs.find(
|
|
601
|
+
(m: any) => m.id === id && m.pending
|
|
505
602
|
);
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
603
|
+
const alreadyFailed = state.roomHeapSlice?.failedMessages?.[id];
|
|
604
|
+
if (stillPending && !alreadyFailed) {
|
|
605
|
+
console.warn(
|
|
606
|
+
`Media-send watchdog fired - ${id} stuck pending > ${MEDIA_PENDING_WATCHDOG_MS}ms; marking failed`
|
|
607
|
+
);
|
|
608
|
+
// See the text-send watchdog above: drop the buffered replay so
|
|
609
|
+
// a later onOnline flush can't silently resend behind the
|
|
610
|
+
// "failed / tap to retry" bubble.
|
|
611
|
+
removeOutboundSend(id);
|
|
612
|
+
dispatch(markMessageFailed(failedPayload()));
|
|
613
|
+
handleMessageFailed({
|
|
614
|
+
message: 'media',
|
|
514
615
|
roomJID: activeRoomJID,
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
616
|
+
error: new Error('Media send timed out (no server echo)'),
|
|
617
|
+
messageType: 'media',
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
}, MEDIA_PENDING_WATCHDOG_MS);
|
|
621
|
+
const uploaded: Array<IAttachment | undefined> = files.map(() => undefined);
|
|
622
|
+
const rawResults: Array<any | undefined> = files.map(() => undefined);
|
|
623
|
+
const sealedUploads: Array<SealedUpload | undefined> = files.map(() => undefined);
|
|
624
|
+
// Per-file key material, kept apart from the sealed upload: the upload
|
|
625
|
+
// is disposed once the message is assembled, but a send that fails after
|
|
626
|
+
// that point is retried and must still carry every key.
|
|
627
|
+
const keyMaterials: Array<string | undefined> = files.map(() => undefined);
|
|
628
|
+
let delivered = false;
|
|
629
|
+
let running = false;
|
|
630
|
+
const concurrency = isE2ee ? MEDIA_E2EE_UPLOAD_CONCURRENCY : MEDIA_UPLOAD_CONCURRENCY;
|
|
631
|
+
let mediaWatchdog = armWatchdog();
|
|
632
|
+
|
|
633
|
+
const publishTile = (
|
|
634
|
+
index: number,
|
|
635
|
+
patch: { uploadStatus?: 'uploading' | 'failed' | null; uploadProgress?: number }
|
|
636
|
+
) => {
|
|
637
|
+
const attachmentId = tiles[index]?.attachmentId;
|
|
638
|
+
if (config?.disableSentLogic || !attachmentId) {return;}
|
|
639
|
+
dispatch(
|
|
640
|
+
setMediaUploadState({
|
|
524
641
|
roomJID: activeRoomJID,
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
// Help RN's FormData polyfill build a proper multipart/file part
|
|
532
|
-
// (instead of serialising the JS object as JSON). On iOS the
|
|
533
|
-
// picker can return ph:// or assets-library:// URIs for some
|
|
534
|
-
// legacy library items — those can't be read as files. Coerce
|
|
535
|
-
// to a file:// scheme when possible so the body is a real blob.
|
|
536
|
-
const fileBlob = {
|
|
537
|
-
uri: normalizedFileUri || data?.uri,
|
|
538
|
-
type:
|
|
539
|
-
data?.type ||
|
|
540
|
-
data?.mimeType ||
|
|
541
|
-
type ||
|
|
542
|
-
'application/octet-stream',
|
|
543
|
-
name: data?.name || data?.fileName || `media_${Date.now()}`,
|
|
642
|
+
messageId: id,
|
|
643
|
+
attachmentId,
|
|
644
|
+
...patch,
|
|
645
|
+
})
|
|
646
|
+
);
|
|
544
647
|
};
|
|
545
648
|
|
|
546
|
-
|
|
649
|
+
const missingIndices = () =>
|
|
650
|
+
uploaded.reduce<number[]>(
|
|
651
|
+
(acc, item, index) => (item ? acc : [...acc, index]),
|
|
652
|
+
[]
|
|
653
|
+
);
|
|
547
654
|
|
|
548
|
-
const
|
|
549
|
-
|
|
655
|
+
const uploadOne = async (index: number): Promise<void> => {
|
|
656
|
+
const file = files[index];
|
|
657
|
+
publishTile(index, { uploadStatus: 'uploading', uploadProgress: 0 });
|
|
658
|
+
|
|
659
|
+
// Helps RN's FormData polyfill build a proper multipart/file part
|
|
660
|
+
// (instead of serialising the JS object as JSON). On iOS the picker
|
|
661
|
+
// can return ph:// or assets-library:// URIs; coerce to file:// when
|
|
662
|
+
// possible so the body is a real blob.
|
|
663
|
+
const fileBlob = {
|
|
664
|
+
uri: normalizeFileUri(file?.uri) || file?.uri,
|
|
665
|
+
type: file?.type || file?.mimeType || type || 'application/octet-stream',
|
|
666
|
+
name: file?.name || file?.fileName || `media_${Date.now()}_${index}`,
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
// In an e2ee room each file is sealed before it leaves the device:
|
|
670
|
+
// the server only ever sees opaque bytes under a random name. The
|
|
671
|
+
// per-file keys ride in the encrypted <body> (see sendMediaMessage).
|
|
672
|
+
if (isE2ee) {
|
|
673
|
+
sealedUploads[index]?.dispose();
|
|
674
|
+
sealedUploads[index] = undefined;
|
|
550
675
|
const { sealPickedFile } =
|
|
551
676
|
require('../e2ee/sealedFiles') as typeof import('../e2ee/sealedFiles');
|
|
552
|
-
|
|
677
|
+
sealedUploads[index] = await sealPickedFile({
|
|
553
678
|
uri: fileBlob.uri,
|
|
554
679
|
name: fileBlob.name,
|
|
555
680
|
type: fileBlob.type,
|
|
556
|
-
size: typeof
|
|
681
|
+
size: typeof file?.size === 'number' ? file.size : undefined,
|
|
557
682
|
});
|
|
558
683
|
}
|
|
684
|
+
const sealed = sealedUploads[index];
|
|
685
|
+
if (sealed) {keyMaterials[index] = sealed.keyMaterial;}
|
|
559
686
|
const part = sealed
|
|
560
687
|
? { uri: sealed.uri, type: 'application/octet-stream', name: sealed.name }
|
|
561
688
|
: fileBlob;
|
|
562
|
-
const options = {
|
|
563
|
-
|
|
564
|
-
|
|
689
|
+
const options = {
|
|
690
|
+
clientEncrypted: !!sealed,
|
|
691
|
+
onProgress: (percent: number) =>
|
|
692
|
+
publishTile(index, { uploadProgress: percent }),
|
|
693
|
+
};
|
|
694
|
+
const post = (field: 'files' | 'file') => {
|
|
695
|
+
const fd = new FormData();
|
|
696
|
+
fd.append(field, part as any);
|
|
697
|
+
return uploadFileV2(fd, activeRoomJID, options);
|
|
698
|
+
};
|
|
699
|
+
|
|
700
|
+
let response: { data: any };
|
|
565
701
|
try {
|
|
566
|
-
|
|
702
|
+
response = await post('files');
|
|
567
703
|
} catch (err: any) {
|
|
568
|
-
if (err?.response?.status
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
fd2.append('file', part as any);
|
|
575
|
-
return await uploadFileV2(fd2, activeRoomJID, options);
|
|
576
|
-
}
|
|
577
|
-
throw err;
|
|
704
|
+
if (err?.response?.status !== 500) {throw err;}
|
|
705
|
+
console.warn(
|
|
706
|
+
'upload "files" field returned 500 - retrying with "file" (singular)',
|
|
707
|
+
{ name: part.name, type: part.type }
|
|
708
|
+
);
|
|
709
|
+
response = await post('file');
|
|
578
710
|
}
|
|
711
|
+
|
|
712
|
+
const raw = Array.isArray(response?.data?.results)
|
|
713
|
+
? response.data.results[0]
|
|
714
|
+
: undefined;
|
|
715
|
+
if (!raw) {throw new Error('media_upload_empty_response');}
|
|
716
|
+
|
|
717
|
+
uploaded[index] = toUploadedAttachment(raw);
|
|
718
|
+
rawResults[index] = raw;
|
|
719
|
+
// The bytes are up; the message waits for its siblings.
|
|
720
|
+
publishTile(index, { uploadStatus: 'uploading', uploadProgress: 100 });
|
|
579
721
|
};
|
|
580
722
|
|
|
581
|
-
|
|
582
|
-
const
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
723
|
+
const sendAssembled = async (): Promise<void> => {
|
|
724
|
+
const raws = rawResults as any[];
|
|
725
|
+
const attachments = uploaded as IAttachment[];
|
|
726
|
+
const head = raws[0];
|
|
727
|
+
|
|
728
|
+
const messagePayload = {
|
|
729
|
+
firstName: user.firstName,
|
|
730
|
+
lastName: user.lastName,
|
|
731
|
+
walletAddress: user.walletAddress,
|
|
732
|
+
createdAt: head.createdAt,
|
|
733
|
+
expiresAt: head.expiresAt,
|
|
734
|
+
fileName: head.filename,
|
|
735
|
+
isVisible: head?.isVisible,
|
|
736
|
+
location: head.location,
|
|
737
|
+
locationPreview: head.locationPreview,
|
|
738
|
+
mimetype: head.mimetype,
|
|
739
|
+
originalName: head?.originalname,
|
|
740
|
+
ownerKey: head?.ownerKey,
|
|
741
|
+
size: head.size,
|
|
742
|
+
duration: head?.duration ?? first?.duration,
|
|
743
|
+
waveForm: first?.waveForm,
|
|
744
|
+
updatedAt: head?.updatedAt,
|
|
745
|
+
userId: head?.userId,
|
|
746
|
+
attachmentId: head?._id,
|
|
747
|
+
// Only when there is more than one file: sendMediaMessage stamps
|
|
748
|
+
// the list on <data> and leaves a single-file stanza untouched.
|
|
749
|
+
attachments: attachments.length > 1 ? attachments : undefined,
|
|
750
|
+
caption: captionFields?.caption,
|
|
751
|
+
captionMentions: captionFields?.mentions,
|
|
752
|
+
wrappable: true,
|
|
753
|
+
roomJid: activeRoomJID,
|
|
754
|
+
showInChannel: isChecked,
|
|
755
|
+
isReply,
|
|
756
|
+
mainMessage,
|
|
757
|
+
isPrivate: head?.isPrivate,
|
|
758
|
+
__v: head.__v,
|
|
759
|
+
// One key per sealed file, in selection order.
|
|
760
|
+
e2eeKeys: isE2ee
|
|
761
|
+
? (keyMaterials as string[])
|
|
762
|
+
: undefined,
|
|
763
|
+
};
|
|
764
|
+
|
|
765
|
+
// The local copies of sealed files are remembered for the sender and
|
|
766
|
+
// the scratch ciphertext is dropped.
|
|
767
|
+
if (isE2ee) {
|
|
768
|
+
const { rememberOpened } =
|
|
769
|
+
require('../e2ee/sealedFiles') as typeof import('../e2ee/sealedFiles');
|
|
770
|
+
for (let index = 0; index < files.length; index++) {
|
|
771
|
+
const item = attachments[index];
|
|
619
772
|
try {
|
|
620
|
-
const { rememberOpened } =
|
|
621
|
-
require('../e2ee/sealedFiles') as typeof import('../e2ee/sealedFiles');
|
|
622
773
|
await rememberOpened(item.location, {
|
|
623
|
-
uri:
|
|
624
|
-
name:
|
|
625
|
-
type:
|
|
626
|
-
size: typeof
|
|
774
|
+
uri: normalizeFileUri(files[index]?.uri) || files[index]?.uri,
|
|
775
|
+
name: files[index]?.name,
|
|
776
|
+
type: files[index]?.type || files[index]?.mimeType || type,
|
|
777
|
+
size: typeof files[index]?.size === 'number' ? files[index].size : undefined,
|
|
627
778
|
});
|
|
628
779
|
} catch (err) {
|
|
629
780
|
console.warn('could not keep the sent attachment', err);
|
|
630
781
|
}
|
|
631
|
-
|
|
782
|
+
sealedUploads[index]?.dispose();
|
|
783
|
+
sealedUploads[index] = undefined;
|
|
632
784
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
store.getState()?.chatSettingStore?.client,
|
|
643
|
-
].find((c: any) => c && c.status === 'online');
|
|
644
|
-
if (effectiveMediaClient) {
|
|
645
|
-
effectiveMediaClient.setActiveRoomJid?.(activeRoomJID);
|
|
646
|
-
await Promise.resolve(
|
|
647
|
-
effectiveMediaClient.prioritizeRoomPresence?.(activeRoomJID)
|
|
648
|
-
).catch(() => false);
|
|
649
|
-
effectiveMediaClient.sendMediaMessageStanza(
|
|
650
|
-
activeRoomJID,
|
|
651
|
-
messagePayload,
|
|
652
|
-
id
|
|
653
|
-
);
|
|
654
|
-
try {
|
|
655
|
-
effectiveMediaClient.ensureStreamAlive?.();
|
|
656
|
-
} catch {}
|
|
657
|
-
} else {
|
|
658
|
-
const anyMediaClient: any =
|
|
659
|
-
client ||
|
|
660
|
-
getGlobalXmppClient() ||
|
|
661
|
-
store.getState()?.chatSettingStore?.client;
|
|
662
|
-
try {
|
|
663
|
-
anyMediaClient?.forceReconnect?.();
|
|
664
|
-
} catch {}
|
|
665
|
-
enqueueOutboundSend({
|
|
666
|
-
optimisticId: id,
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// Replace the optimistic tiles with the uploaded files. A sealed
|
|
788
|
+
// message keeps its local tiles (the server copies are ciphertext) and
|
|
789
|
+
// only drops the upload state. `pending` is left alone: only the
|
|
790
|
+
// server echo confirms the message.
|
|
791
|
+
if (!config?.disableSentLogic) {
|
|
792
|
+
dispatch(
|
|
793
|
+
setMessageAttachments({
|
|
667
794
|
roomJID: activeRoomJID,
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
795
|
+
messageId: id,
|
|
796
|
+
attachments: isE2ee
|
|
797
|
+
? tiles.map(withoutUploadState)
|
|
798
|
+
: attachments.map((attachment, index) => {
|
|
799
|
+
const localUrl = tiles[index]?.localUrl;
|
|
800
|
+
return localUrl ? { ...attachment, localUrl } : attachment;
|
|
801
|
+
}),
|
|
802
|
+
})
|
|
803
|
+
);
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
const effectiveMediaClient: any = [
|
|
807
|
+
client,
|
|
808
|
+
getGlobalXmppClient(),
|
|
809
|
+
store.getState()?.chatSettingStore?.client,
|
|
810
|
+
].find((c: any) => c && c.status === 'online');
|
|
811
|
+
if (effectiveMediaClient) {
|
|
812
|
+
effectiveMediaClient.setActiveRoomJid?.(activeRoomJID);
|
|
813
|
+
await Promise.resolve(
|
|
814
|
+
effectiveMediaClient.prioritizeRoomPresence?.(activeRoomJID)
|
|
815
|
+
).catch(() => false);
|
|
816
|
+
effectiveMediaClient.sendMediaMessageStanza(activeRoomJID, messagePayload, id);
|
|
817
|
+
try {
|
|
818
|
+
effectiveMediaClient.ensureStreamAlive?.();
|
|
819
|
+
} catch {}
|
|
820
|
+
} else {
|
|
821
|
+
// Same online-only guard as the text path: a surviving-but-offline
|
|
822
|
+
// client must NOT be used. Buffer the stanza for the reconnect flush.
|
|
823
|
+
const anyMediaClient: any =
|
|
824
|
+
client ||
|
|
825
|
+
getGlobalXmppClient() ||
|
|
826
|
+
store.getState()?.chatSettingStore?.client;
|
|
827
|
+
try {
|
|
828
|
+
anyMediaClient?.forceReconnect?.();
|
|
829
|
+
} catch {}
|
|
830
|
+
enqueueOutboundSend({
|
|
831
|
+
optimisticId: id,
|
|
832
|
+
roomJID: activeRoomJID,
|
|
833
|
+
enqueuedAt: Date.now(),
|
|
834
|
+
// Match the media watchdog so a reconnect within the (longer)
|
|
835
|
+
// media window still auto-replays the buffered stanza.
|
|
836
|
+
ttlMs: MEDIA_PENDING_WATCHDOG_MS,
|
|
837
|
+
send: (c) => c.sendMediaMessageStanza(activeRoomJID, messagePayload, id),
|
|
838
|
+
});
|
|
676
839
|
}
|
|
677
840
|
|
|
841
|
+
// The upload time is spent: the wait for the server echo gets a full
|
|
842
|
+
// watchdog window of its own, not what was left of the upload's.
|
|
843
|
+
clearTimeout(mediaWatchdog);
|
|
844
|
+
mediaWatchdog = armWatchdog();
|
|
845
|
+
|
|
846
|
+
delivered = true;
|
|
847
|
+
unregisterMediaRetry(id);
|
|
848
|
+
dispatch(clearMessageFailure(id));
|
|
849
|
+
|
|
678
850
|
await handleMessageSent({
|
|
679
851
|
message: 'media',
|
|
680
852
|
roomJID: activeRoomJID,
|
|
@@ -687,63 +859,79 @@ export const useSendMessage = (_configOverride?: IConfig) => {
|
|
|
687
859
|
fileData: data,
|
|
688
860
|
fileType: type,
|
|
689
861
|
messageId: id,
|
|
690
|
-
uploadResults:
|
|
862
|
+
uploadResults: raws,
|
|
691
863
|
},
|
|
692
864
|
});
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
//
|
|
699
|
-
|
|
700
|
-
// with status code N"; the actual reason lives in
|
|
701
|
-
// `error.response.data`).
|
|
702
|
-
const uriStr = typeof data?.uri === 'string' ? data.uri : '';
|
|
703
|
-
const uriScheme = uriStr.match(/^([a-z]+):/i)?.[1] ?? 'unknown';
|
|
704
|
-
console.error('upload failed', {
|
|
705
|
-
status: error?.response?.status,
|
|
706
|
-
statusText: error?.response?.statusText,
|
|
707
|
-
serverBody: error?.response?.data,
|
|
708
|
-
requestUrl: error?.config?.url,
|
|
709
|
-
axiosMessage: error?.message,
|
|
710
|
-
axiosCode: error?.code,
|
|
711
|
-
axiosCauseMessage: error?.cause?.message,
|
|
712
|
-
uri: uriStr,
|
|
713
|
-
uriScheme,
|
|
714
|
-
mime: fileBlob.type,
|
|
715
|
-
fileSizeBytes: data?.size ?? null,
|
|
716
|
-
fileName: fileBlob.name,
|
|
717
|
-
platformOS: Platform.OS,
|
|
718
|
-
platformVersion: Platform.Version,
|
|
719
|
-
});
|
|
720
|
-
dispatch(
|
|
721
|
-
markMessageFailed({
|
|
722
|
-
kind: 'media',
|
|
723
|
-
id,
|
|
724
|
-
roomJID: activeRoomJID,
|
|
725
|
-
data,
|
|
726
|
-
type,
|
|
727
|
-
isReply,
|
|
728
|
-
isChecked,
|
|
729
|
-
mainMessage,
|
|
730
|
-
})
|
|
731
|
-
);
|
|
865
|
+
};
|
|
866
|
+
|
|
867
|
+
const failMessage = (error: unknown) => {
|
|
868
|
+
console.error('Media send failed:', error);
|
|
869
|
+
// Every file stays held: the bubble shows Failed, and Retry replays
|
|
870
|
+
// the files that did not make it (in-session) or the whole list.
|
|
871
|
+
dispatch(markMessageFailed(failedPayload()));
|
|
732
872
|
handleMessageFailed({
|
|
733
873
|
message: 'media',
|
|
734
874
|
roomJID: activeRoomJID,
|
|
735
875
|
error: error as Error,
|
|
736
876
|
messageType: 'media',
|
|
737
877
|
});
|
|
738
|
-
}
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* Uploads `indices` (a retry passes only the files that failed), then
|
|
882
|
+
* sends once nothing is missing. Any failure leaves the message failed
|
|
883
|
+
* with its tiles marked, ready for a retry.
|
|
884
|
+
*/
|
|
885
|
+
const run = async (indices: number[]): Promise<boolean> => {
|
|
886
|
+
if (delivered || running) {return false;}
|
|
887
|
+
running = true;
|
|
888
|
+
clearTimeout(mediaWatchdog);
|
|
889
|
+
mediaWatchdog = armWatchdog();
|
|
890
|
+
try {
|
|
891
|
+
await mapWithConcurrency(indices, concurrency, async (index) => {
|
|
892
|
+
try {
|
|
893
|
+
await uploadOne(index);
|
|
894
|
+
} catch (error: any) {
|
|
895
|
+
console.error('upload failed', {
|
|
896
|
+
status: error?.response?.status,
|
|
897
|
+
serverBody: error?.response?.data,
|
|
898
|
+
axiosMessage: error?.message,
|
|
899
|
+
fileName: files[index]?.name,
|
|
900
|
+
fileSizeBytes: files[index]?.size ?? null,
|
|
901
|
+
platformOS: Platform.OS,
|
|
902
|
+
});
|
|
903
|
+
publishTile(index, { uploadStatus: 'failed', uploadProgress: 0 });
|
|
904
|
+
}
|
|
905
|
+
});
|
|
906
|
+
if (missingIndices().length > 0) {
|
|
907
|
+
throw new Error('media_upload_failed');
|
|
908
|
+
}
|
|
909
|
+
await sendAssembled();
|
|
910
|
+
return true;
|
|
911
|
+
} catch (error) {
|
|
912
|
+
failMessage(error);
|
|
913
|
+
return false;
|
|
914
|
+
} finally {
|
|
915
|
+
running = false;
|
|
916
|
+
}
|
|
917
|
+
};
|
|
918
|
+
|
|
919
|
+
registerMediaRetry(id, (indices?: number[]) =>
|
|
920
|
+
run(
|
|
921
|
+
missingIndices().filter(
|
|
922
|
+
(position) => indices === undefined || indices.includes(position)
|
|
923
|
+
)
|
|
924
|
+
)
|
|
925
|
+
);
|
|
926
|
+
|
|
927
|
+
return run(files.map((_, index) => index));
|
|
739
928
|
},
|
|
740
929
|
[client, config, user, dispatch, handleMessageSent, handleMessageFailed, reduxStore]
|
|
741
930
|
);
|
|
742
931
|
|
|
743
|
-
// Retry a previously-failed send
|
|
744
|
-
//
|
|
745
|
-
//
|
|
746
|
-
// it. No-op when the id isn't in the failed map.
|
|
932
|
+
// Retry a previously-failed send. An in-session send re-runs only the files
|
|
933
|
+
// still missing (its runner is still registered); after a restart the
|
|
934
|
+
// saved payload is replayed as a whole. No-op when the id isn't failed.
|
|
747
935
|
const retryMessage = useCallback(
|
|
748
936
|
async (failedId: string) => {
|
|
749
937
|
const payload =
|
|
@@ -783,17 +971,28 @@ export const useSendMessage = (_configOverride?: IConfig) => {
|
|
|
783
971
|
// instead of spawning a duplicate bubble.
|
|
784
972
|
failedId
|
|
785
973
|
);
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
payload.isReply,
|
|
792
|
-
payload.isChecked,
|
|
793
|
-
payload.mainMessage,
|
|
794
|
-
failedId
|
|
795
|
-
);
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
if (hasMediaRetry(failedId)) {
|
|
977
|
+
await retryMediaSend(failedId);
|
|
978
|
+
return;
|
|
796
979
|
}
|
|
980
|
+
const list =
|
|
981
|
+
Array.isArray(payload.files) && payload.files.length > 0
|
|
982
|
+
? payload.files
|
|
983
|
+
: payload.data;
|
|
984
|
+
await sendMedia(
|
|
985
|
+
list,
|
|
986
|
+
payload.type,
|
|
987
|
+
payload.roomJID,
|
|
988
|
+
payload.isReply,
|
|
989
|
+
payload.isChecked,
|
|
990
|
+
payload.mainMessage,
|
|
991
|
+
failedId,
|
|
992
|
+
payload.caption
|
|
993
|
+
? { text: payload.caption, mentions: payload.captionMentions }
|
|
994
|
+
: undefined
|
|
995
|
+
);
|
|
797
996
|
},
|
|
798
997
|
[reduxStore, dispatch, handleMessageRetry, sendMessage, sendMedia]
|
|
799
998
|
);
|