@ethora/chat-component-rn 26.5.5 → 26.5.10
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 +177 -0
- package/README.md +67 -10
- package/index.js +24 -0
- package/lib/commonjs/assets/icons.js +5 -1
- package/lib/commonjs/assets/icons.js.map +1 -1
- package/lib/commonjs/components/ContextMenu/ContextMenuComponents.js +11 -5
- package/lib/commonjs/components/ContextMenu/ContextMenuComponents.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ChatHeader.js +7 -1
- package/lib/commonjs/components/MainComponents/ChatHeader.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ChatRoom.js +66 -34
- package/lib/commonjs/components/MainComponents/ChatRoom.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ChatWrapper.js +59 -14
- package/lib/commonjs/components/MainComponents/ChatWrapper.js.map +1 -1
- package/lib/commonjs/components/MainComponents/MediaMessage.js +15 -7
- package/lib/commonjs/components/MainComponents/MediaMessage.js.map +1 -1
- package/lib/commonjs/components/MainComponents/MessageContainer.js +2 -2
- package/lib/commonjs/components/MainComponents/MessageContainer.js.map +1 -1
- package/lib/commonjs/components/MainComponents/MessageList.js +175 -30
- package/lib/commonjs/components/MainComponents/MessageList.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ReduxWrapper.js +17 -8
- package/lib/commonjs/components/MainComponents/ReduxWrapper.js.map +1 -1
- package/lib/commonjs/components/MainComponents/SystemMessage.js +34 -22
- package/lib/commonjs/components/MainComponents/SystemMessage.js.map +1 -1
- package/lib/commonjs/components/MessageBubble/Message.js +117 -19
- package/lib/commonjs/components/MessageBubble/Message.js.map +1 -1
- package/lib/commonjs/components/MessageBubble/MessageInteractions.js +82 -22
- package/lib/commonjs/components/MessageBubble/MessageInteractions.js.map +1 -1
- package/lib/commonjs/components/Modals/AttachSheet/AttachSheet.js +16 -4
- package/lib/commonjs/components/Modals/AttachSheet/AttachSheet.js.map +1 -1
- package/lib/commonjs/components/Modals/FilePreviewModal/DocumentViewer.js +104 -0
- package/lib/commonjs/components/Modals/FilePreviewModal/DocumentViewer.js.map +1 -0
- package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js +175 -40
- package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
- package/lib/commonjs/components/Modals/FilePreviewModal/PdfView.js +29 -1
- package/lib/commonjs/components/Modals/FilePreviewModal/PdfView.js.map +1 -1
- package/lib/commonjs/components/Modals/Modal/Modal.js +5 -1
- package/lib/commonjs/components/Modals/Modal/Modal.js.map +1 -1
- package/lib/commonjs/components/Modals/ModalWrapper/ModalWrapper.js +78 -39
- package/lib/commonjs/components/Modals/ModalWrapper/ModalWrapper.js.map +1 -1
- package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js +3 -3
- package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
- package/lib/commonjs/components/Modals/styledModalComponents.js +4 -9
- package/lib/commonjs/components/Modals/styledModalComponents.js.map +1 -1
- package/lib/commonjs/components/styled/MediaFilePreview.js +115 -5
- package/lib/commonjs/components/styled/MediaFilePreview.js.map +1 -1
- package/lib/commonjs/components/styled/MessageImage.js +49 -10
- package/lib/commonjs/components/styled/MessageImage.js.map +1 -1
- package/lib/commonjs/components/styled/NewMessageLabel.js +8 -6
- package/lib/commonjs/components/styled/NewMessageLabel.js.map +1 -1
- package/lib/commonjs/components/styled/SendInput.js +53 -29
- package/lib/commonjs/components/styled/SendInput.js.map +1 -1
- package/lib/commonjs/components/styled/StyledInputComponents/MediaComponents.js +14 -4
- package/lib/commonjs/components/styled/StyledInputComponents/MediaComponents.js.map +1 -1
- package/lib/commonjs/components/styled/StyledInputComponents/StyledInputComponents.js +7 -2
- package/lib/commonjs/components/styled/StyledInputComponents/StyledInputComponents.js.map +1 -1
- package/lib/commonjs/components/styled/UnsupportedType.js +5 -1
- package/lib/commonjs/components/styled/UnsupportedType.js.map +1 -1
- package/lib/commonjs/components/styled/VideoMessage.js +60 -22
- package/lib/commonjs/components/styled/VideoMessage.js.map +1 -1
- package/lib/commonjs/context/MessageNotificationContext.js +7 -7
- package/lib/commonjs/context/MessageNotificationContext.js.map +1 -1
- package/lib/commonjs/context/xmppProvider.js +315 -34
- package/lib/commonjs/context/xmppProvider.js.map +1 -1
- package/lib/commonjs/helpers/chatAutoEnterer.js +2 -7
- package/lib/commonjs/helpers/chatAutoEnterer.js.map +1 -1
- package/lib/commonjs/helpers/historyPreloadScheduler.js +4 -1
- package/lib/commonjs/helpers/historyPreloadScheduler.js.map +1 -1
- package/lib/commonjs/helpers/insertMessageWithDelimiter.js +15 -5
- package/lib/commonjs/helpers/insertMessageWithDelimiter.js.map +1 -1
- package/lib/commonjs/helpers/keyboardLayout.js +18 -0
- package/lib/commonjs/helpers/keyboardLayout.js.map +1 -0
- package/lib/commonjs/helpers/mediaDimensions.js +51 -0
- package/lib/commonjs/helpers/mediaDimensions.js.map +1 -0
- package/lib/commonjs/helpers/mimeToExtension.js +131 -0
- package/lib/commonjs/helpers/mimeToExtension.js.map +1 -0
- package/lib/commonjs/helpers/normalizeRoomJid.js +20 -0
- package/lib/commonjs/helpers/normalizeRoomJid.js.map +1 -0
- package/lib/commonjs/helpers/resolveInitBeforeLoadUser.js +73 -1
- package/lib/commonjs/helpers/resolveInitBeforeLoadUser.js.map +1 -1
- package/lib/commonjs/hooks/useChatRoomFocus.js +87 -0
- package/lib/commonjs/hooks/useChatRoomFocus.js.map +1 -0
- package/lib/commonjs/hooks/useChatWrapperInit.js +22 -10
- package/lib/commonjs/hooks/useChatWrapperInit.js.map +1 -1
- package/lib/commonjs/hooks/useEventHandlers.js +9 -1
- package/lib/commonjs/hooks/useEventHandlers.js.map +1 -1
- package/lib/commonjs/hooks/useHeapSender.js +1 -1
- package/lib/commonjs/hooks/useHeapSender.js.map +1 -1
- package/lib/commonjs/hooks/useLogout.js +3 -3
- package/lib/commonjs/hooks/useLogout.js.map +1 -1
- package/lib/commonjs/hooks/useMessageHeapState.js +6 -2
- package/lib/commonjs/hooks/useMessageHeapState.js.map +1 -1
- package/lib/commonjs/hooks/usePushNotifications.js +6 -2
- package/lib/commonjs/hooks/usePushNotifications.js.map +1 -1
- package/lib/commonjs/hooks/useRoomState.js +2 -0
- package/lib/commonjs/hooks/useRoomState.js.map +1 -1
- package/lib/commonjs/hooks/useSendMessage.js +249 -14
- package/lib/commonjs/hooks/useSendMessage.js.map +1 -1
- package/lib/commonjs/hooks/useUnreadMessagesCounter.js +12 -14
- package/lib/commonjs/hooks/useUnreadMessagesCounter.js.map +1 -1
- package/lib/commonjs/main.js +7 -0
- package/lib/commonjs/main.js.map +1 -1
- package/lib/commonjs/networking/api-requests/auth.api.js +74 -1
- package/lib/commonjs/networking/api-requests/auth.api.js.map +1 -1
- package/lib/commonjs/networking/apiClient.js +2 -1
- package/lib/commonjs/networking/apiClient.js.map +1 -1
- package/lib/commonjs/networking/outboundQueue.js +92 -0
- package/lib/commonjs/networking/outboundQueue.js.map +1 -0
- package/lib/commonjs/networking/xmpp/createTimeoutPromise.xmpp.js +4 -1
- package/lib/commonjs/networking/xmpp/createTimeoutPromise.xmpp.js.map +1 -1
- package/lib/commonjs/networking/xmpp/flushLastViewedToPrivateStore.js +7 -8
- package/lib/commonjs/networking/xmpp/flushLastViewedToPrivateStore.js.map +1 -1
- package/lib/commonjs/networking/xmpp/getRoomsPaged.xmpp.js +26 -3
- package/lib/commonjs/networking/xmpp/getRoomsPaged.xmpp.js.map +1 -1
- package/lib/commonjs/networking/xmpp/presenceInRoom.xmpp.js +26 -8
- package/lib/commonjs/networking/xmpp/presenceInRoom.xmpp.js.map +1 -1
- package/lib/commonjs/networking/xmpp/sendTextMessage.xmpp.js +2 -1
- package/lib/commonjs/networking/xmpp/sendTextMessage.xmpp.js.map +1 -1
- package/lib/commonjs/networking/xmppClient.js +371 -26
- package/lib/commonjs/networking/xmppClient.js.map +1 -1
- package/lib/commonjs/roomStore/Middleware/unreadMidlleware.js +22 -6
- package/lib/commonjs/roomStore/Middleware/unreadMidlleware.js.map +1 -1
- package/lib/commonjs/roomStore/roomHeapSlice.js +26 -8
- package/lib/commonjs/roomStore/roomHeapSlice.js.map +1 -1
- package/lib/commonjs/roomStore/roomsSlice.js +157 -23
- package/lib/commonjs/roomStore/roomsSlice.js.map +1 -1
- package/lib/commonjs/utils/installPromiseRejectionTracker.js +83 -0
- package/lib/commonjs/utils/installPromiseRejectionTracker.js.map +1 -0
- package/lib/commonjs/utils/resendMessage.js +1 -1
- package/lib/commonjs/utils/resendMessage.js.map +1 -1
- package/lib/module/assets/icons.js +5 -1
- package/lib/module/assets/icons.js.map +1 -1
- package/lib/module/components/ContextMenu/ContextMenuComponents.js +11 -5
- package/lib/module/components/ContextMenu/ContextMenuComponents.js.map +1 -1
- package/lib/module/components/MainComponents/ChatHeader.js +7 -1
- package/lib/module/components/MainComponents/ChatHeader.js.map +1 -1
- package/lib/module/components/MainComponents/ChatRoom.js +67 -35
- package/lib/module/components/MainComponents/ChatRoom.js.map +1 -1
- package/lib/module/components/MainComponents/ChatWrapper.js +59 -14
- package/lib/module/components/MainComponents/ChatWrapper.js.map +1 -1
- package/lib/module/components/MainComponents/MediaMessage.js +15 -7
- package/lib/module/components/MainComponents/MediaMessage.js.map +1 -1
- package/lib/module/components/MainComponents/MessageContainer.js +2 -2
- package/lib/module/components/MainComponents/MessageContainer.js.map +1 -1
- package/lib/module/components/MainComponents/MessageList.js +176 -31
- package/lib/module/components/MainComponents/MessageList.js.map +1 -1
- package/lib/module/components/MainComponents/ReduxWrapper.js +18 -8
- package/lib/module/components/MainComponents/ReduxWrapper.js.map +1 -1
- package/lib/module/components/MainComponents/SystemMessage.js +33 -21
- package/lib/module/components/MainComponents/SystemMessage.js.map +1 -1
- package/lib/module/components/MessageBubble/Message.js +118 -20
- package/lib/module/components/MessageBubble/Message.js.map +1 -1
- package/lib/module/components/MessageBubble/MessageInteractions.js +84 -24
- package/lib/module/components/MessageBubble/MessageInteractions.js.map +1 -1
- package/lib/module/components/Modals/AttachSheet/AttachSheet.js +15 -3
- package/lib/module/components/Modals/AttachSheet/AttachSheet.js.map +1 -1
- package/lib/module/components/Modals/FilePreviewModal/DocumentViewer.js +99 -0
- package/lib/module/components/Modals/FilePreviewModal/DocumentViewer.js.map +1 -0
- package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js +179 -42
- package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
- package/lib/module/components/Modals/FilePreviewModal/PdfView.js +31 -3
- package/lib/module/components/Modals/FilePreviewModal/PdfView.js.map +1 -1
- package/lib/module/components/Modals/Modal/Modal.js +5 -1
- package/lib/module/components/Modals/Modal/Modal.js.map +1 -1
- package/lib/module/components/Modals/ModalWrapper/ModalWrapper.js +80 -41
- package/lib/module/components/Modals/ModalWrapper/ModalWrapper.js.map +1 -1
- package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js +3 -3
- package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
- package/lib/module/components/Modals/styledModalComponents.js +4 -9
- package/lib/module/components/Modals/styledModalComponents.js.map +1 -1
- package/lib/module/components/styled/MediaFilePreview.js +116 -6
- package/lib/module/components/styled/MediaFilePreview.js.map +1 -1
- package/lib/module/components/styled/MessageImage.js +51 -12
- package/lib/module/components/styled/MessageImage.js.map +1 -1
- package/lib/module/components/styled/NewMessageLabel.js +8 -6
- package/lib/module/components/styled/NewMessageLabel.js.map +1 -1
- package/lib/module/components/styled/SendInput.js +54 -30
- package/lib/module/components/styled/SendInput.js.map +1 -1
- package/lib/module/components/styled/StyledInputComponents/MediaComponents.js +14 -4
- package/lib/module/components/styled/StyledInputComponents/MediaComponents.js.map +1 -1
- package/lib/module/components/styled/StyledInputComponents/StyledInputComponents.js +7 -2
- package/lib/module/components/styled/StyledInputComponents/StyledInputComponents.js.map +1 -1
- package/lib/module/components/styled/UnsupportedType.js +5 -1
- package/lib/module/components/styled/UnsupportedType.js.map +1 -1
- package/lib/module/components/styled/VideoMessage.js +61 -23
- package/lib/module/components/styled/VideoMessage.js.map +1 -1
- package/lib/module/context/MessageNotificationContext.js +7 -7
- package/lib/module/context/MessageNotificationContext.js.map +1 -1
- package/lib/module/context/xmppProvider.js +319 -37
- package/lib/module/context/xmppProvider.js.map +1 -1
- package/lib/module/helpers/chatAutoEnterer.js +2 -7
- package/lib/module/helpers/chatAutoEnterer.js.map +1 -1
- package/lib/module/helpers/historyPreloadScheduler.js +4 -1
- package/lib/module/helpers/historyPreloadScheduler.js.map +1 -1
- package/lib/module/helpers/insertMessageWithDelimiter.js +15 -5
- package/lib/module/helpers/insertMessageWithDelimiter.js.map +1 -1
- package/lib/module/helpers/keyboardLayout.js +12 -0
- package/lib/module/helpers/keyboardLayout.js.map +1 -0
- package/lib/module/helpers/mediaDimensions.js +46 -0
- package/lib/module/helpers/mediaDimensions.js.map +1 -0
- package/lib/module/helpers/mimeToExtension.js +124 -0
- package/lib/module/helpers/mimeToExtension.js.map +1 -0
- package/lib/module/helpers/normalizeRoomJid.js +15 -0
- package/lib/module/helpers/normalizeRoomJid.js.map +1 -0
- package/lib/module/helpers/resolveInitBeforeLoadUser.js +71 -0
- package/lib/module/helpers/resolveInitBeforeLoadUser.js.map +1 -1
- package/lib/module/hooks/useChatRoomFocus.js +82 -0
- package/lib/module/hooks/useChatRoomFocus.js.map +1 -0
- package/lib/module/hooks/useChatWrapperInit.js +22 -10
- package/lib/module/hooks/useChatWrapperInit.js.map +1 -1
- package/lib/module/hooks/useEventHandlers.js +9 -1
- package/lib/module/hooks/useEventHandlers.js.map +1 -1
- package/lib/module/hooks/useHeapSender.js +1 -1
- package/lib/module/hooks/useHeapSender.js.map +1 -1
- package/lib/module/hooks/useLogout.js +3 -3
- package/lib/module/hooks/useLogout.js.map +1 -1
- package/lib/module/hooks/useMessageHeapState.js +6 -2
- package/lib/module/hooks/useMessageHeapState.js.map +1 -1
- package/lib/module/hooks/usePushNotifications.js +6 -2
- package/lib/module/hooks/usePushNotifications.js.map +1 -1
- package/lib/module/hooks/useRoomState.js +2 -0
- package/lib/module/hooks/useRoomState.js.map +1 -1
- package/lib/module/hooks/useSendMessage.js +252 -17
- package/lib/module/hooks/useSendMessage.js.map +1 -1
- package/lib/module/hooks/useUnreadMessagesCounter.js +12 -14
- package/lib/module/hooks/useUnreadMessagesCounter.js.map +1 -1
- package/lib/module/main.js +1 -0
- package/lib/module/main.js.map +1 -1
- package/lib/module/networking/api-requests/auth.api.js +74 -2
- package/lib/module/networking/api-requests/auth.api.js.map +1 -1
- package/lib/module/networking/apiClient.js +2 -1
- package/lib/module/networking/apiClient.js.map +1 -1
- package/lib/module/networking/outboundQueue.js +84 -0
- package/lib/module/networking/outboundQueue.js.map +1 -0
- package/lib/module/networking/xmpp/createTimeoutPromise.xmpp.js +4 -1
- package/lib/module/networking/xmpp/createTimeoutPromise.xmpp.js.map +1 -1
- package/lib/module/networking/xmpp/flushLastViewedToPrivateStore.js +7 -8
- package/lib/module/networking/xmpp/flushLastViewedToPrivateStore.js.map +1 -1
- package/lib/module/networking/xmpp/getRoomsPaged.xmpp.js +26 -3
- package/lib/module/networking/xmpp/getRoomsPaged.xmpp.js.map +1 -1
- package/lib/module/networking/xmpp/presenceInRoom.xmpp.js +26 -8
- package/lib/module/networking/xmpp/presenceInRoom.xmpp.js.map +1 -1
- package/lib/module/networking/xmpp/sendTextMessage.xmpp.js +2 -1
- package/lib/module/networking/xmpp/sendTextMessage.xmpp.js.map +1 -1
- package/lib/module/networking/xmppClient.js +371 -26
- package/lib/module/networking/xmppClient.js.map +1 -1
- package/lib/module/roomStore/Middleware/unreadMidlleware.js +22 -6
- package/lib/module/roomStore/Middleware/unreadMidlleware.js.map +1 -1
- package/lib/module/roomStore/roomHeapSlice.js +24 -7
- package/lib/module/roomStore/roomHeapSlice.js.map +1 -1
- package/lib/module/roomStore/roomsSlice.js +154 -22
- package/lib/module/roomStore/roomsSlice.js.map +1 -1
- package/lib/module/utils/installPromiseRejectionTracker.js +78 -0
- package/lib/module/utils/installPromiseRejectionTracker.js.map +1 -0
- package/lib/module/utils/resendMessage.js +1 -1
- package/lib/module/utils/resendMessage.js.map +1 -1
- package/lib/typescript/assets/icons.d.ts +8 -4
- package/lib/typescript/assets/icons.d.ts.map +1 -1
- package/lib/typescript/components/ContextMenu/ContextMenuComponents.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/ChatHeader.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/ChatRoom.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/ChatWrapper.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/MediaMessage.d.ts +1 -0
- package/lib/typescript/components/MainComponents/MediaMessage.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/MessageContainer.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/MessageList.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/ReduxWrapper.d.ts +1 -0
- package/lib/typescript/components/MainComponents/ReduxWrapper.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/SystemMessage.d.ts +0 -7
- package/lib/typescript/components/MainComponents/SystemMessage.d.ts.map +1 -1
- package/lib/typescript/components/MessageBubble/Message.d.ts.map +1 -1
- package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts +4 -2
- package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts.map +1 -1
- package/lib/typescript/components/Modals/AttachSheet/AttachSheet.d.ts.map +1 -1
- package/lib/typescript/components/Modals/FilePreviewModal/DocumentViewer.d.ts +29 -0
- package/lib/typescript/components/Modals/FilePreviewModal/DocumentViewer.d.ts.map +1 -0
- package/lib/typescript/components/Modals/FilePreviewModal/FilePreviewModal.d.ts +0 -1
- package/lib/typescript/components/Modals/FilePreviewModal/FilePreviewModal.d.ts.map +1 -1
- package/lib/typescript/components/Modals/FilePreviewModal/PdfView.d.ts.map +1 -1
- package/lib/typescript/components/Modals/Modal/Modal.d.ts.map +1 -1
- package/lib/typescript/components/Modals/ModalWrapper/ModalWrapper.d.ts +6 -0
- package/lib/typescript/components/Modals/ModalWrapper/ModalWrapper.d.ts.map +1 -1
- package/lib/typescript/components/Modals/UserProfileModal/UserProfileModal.d.ts.map +1 -1
- package/lib/typescript/components/Modals/styledModalComponents.d.ts.map +1 -1
- package/lib/typescript/components/styled/MediaFilePreview.d.ts.map +1 -1
- package/lib/typescript/components/styled/MessageImage.d.ts.map +1 -1
- package/lib/typescript/components/styled/NewMessageLabel.d.ts +1 -5
- package/lib/typescript/components/styled/NewMessageLabel.d.ts.map +1 -1
- package/lib/typescript/components/styled/SendInput.d.ts.map +1 -1
- package/lib/typescript/components/styled/StyledInputComponents/MediaComponents.d.ts +9 -1
- package/lib/typescript/components/styled/StyledInputComponents/MediaComponents.d.ts.map +1 -1
- package/lib/typescript/components/styled/StyledInputComponents/StyledInputComponents.d.ts.map +1 -1
- package/lib/typescript/components/styled/UnsupportedType.d.ts +1 -0
- package/lib/typescript/components/styled/UnsupportedType.d.ts.map +1 -1
- package/lib/typescript/components/styled/VideoMessage.d.ts.map +1 -1
- package/lib/typescript/context/xmppProvider.d.ts +1 -0
- package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
- package/lib/typescript/helpers/chatAutoEnterer.d.ts.map +1 -1
- package/lib/typescript/helpers/historyPreloadScheduler.d.ts.map +1 -1
- package/lib/typescript/helpers/insertMessageWithDelimiter.d.ts.map +1 -1
- package/lib/typescript/helpers/keyboardLayout.d.ts +9 -0
- package/lib/typescript/helpers/keyboardLayout.d.ts.map +1 -0
- package/lib/typescript/helpers/mediaDimensions.d.ts +9 -0
- package/lib/typescript/helpers/mediaDimensions.d.ts.map +1 -0
- package/lib/typescript/helpers/mimeToExtension.d.ts +37 -0
- package/lib/typescript/helpers/mimeToExtension.d.ts.map +1 -0
- package/lib/typescript/helpers/normalizeRoomJid.d.ts +2 -0
- package/lib/typescript/helpers/normalizeRoomJid.d.ts.map +1 -0
- package/lib/typescript/helpers/resolveInitBeforeLoadUser.d.ts +18 -0
- package/lib/typescript/helpers/resolveInitBeforeLoadUser.d.ts.map +1 -1
- package/lib/typescript/hooks/useChatRoomFocus.d.ts +42 -0
- package/lib/typescript/hooks/useChatRoomFocus.d.ts.map +1 -0
- package/lib/typescript/hooks/useChatWrapperInit.d.ts.map +1 -1
- package/lib/typescript/hooks/useEventHandlers.d.ts +6 -0
- package/lib/typescript/hooks/useEventHandlers.d.ts.map +1 -1
- package/lib/typescript/hooks/useHeapSender.d.ts.map +1 -1
- package/lib/typescript/hooks/useMessageHeapState.d.ts +2 -0
- package/lib/typescript/hooks/useMessageHeapState.d.ts.map +1 -1
- package/lib/typescript/hooks/usePushNotifications.d.ts.map +1 -1
- package/lib/typescript/hooks/useRoomState.d.ts +1 -0
- package/lib/typescript/hooks/useRoomState.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/hooks/useUnreadMessagesCounter.d.ts.map +1 -1
- package/lib/typescript/main.d.ts +1 -0
- package/lib/typescript/main.d.ts.map +1 -1
- package/lib/typescript/networking/api-requests/auth.api.d.ts +3 -0
- package/lib/typescript/networking/api-requests/auth.api.d.ts.map +1 -1
- package/lib/typescript/networking/apiClient.d.ts.map +1 -1
- package/lib/typescript/networking/outboundQueue.d.ts +57 -0
- package/lib/typescript/networking/outboundQueue.d.ts.map +1 -0
- package/lib/typescript/networking/xmpp/createTimeoutPromise.xmpp.d.ts.map +1 -1
- package/lib/typescript/networking/xmpp/flushLastViewedToPrivateStore.d.ts +1 -1
- package/lib/typescript/networking/xmpp/flushLastViewedToPrivateStore.d.ts.map +1 -1
- package/lib/typescript/networking/xmpp/getRoomsPaged.xmpp.d.ts +1 -1
- package/lib/typescript/networking/xmpp/getRoomsPaged.xmpp.d.ts.map +1 -1
- package/lib/typescript/networking/xmpp/presenceInRoom.xmpp.d.ts.map +1 -1
- package/lib/typescript/networking/xmpp/sendTextMessage.xmpp.d.ts.map +1 -1
- package/lib/typescript/networking/xmppClient.d.ts +58 -5
- package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
- package/lib/typescript/roomStore/Middleware/unreadMidlleware.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 +8 -1
- package/lib/typescript/roomStore/roomsSlice.d.ts.map +1 -1
- package/lib/typescript/types/models/media.model.d.ts +1 -0
- package/lib/typescript/types/models/media.model.d.ts.map +1 -1
- package/lib/typescript/types/models/xmpp.model.d.ts +1 -1
- package/lib/typescript/types/models/xmpp.model.d.ts.map +1 -1
- package/lib/typescript/types/types.d.ts +10 -0
- package/lib/typescript/types/types.d.ts.map +1 -1
- package/lib/typescript/utils/installPromiseRejectionTracker.d.ts +20 -0
- package/lib/typescript/utils/installPromiseRejectionTracker.d.ts.map +1 -0
- package/lib/typescript/utils/resendMessage.d.ts.map +1 -1
- package/package.json +18 -2
- package/src/assets/icons.tsx +23 -14
- package/src/components/ContextMenu/ContextMenuComponents.tsx +10 -5
- package/src/components/MainComponents/ChatHeader.tsx +5 -1
- package/src/components/MainComponents/ChatRoom.tsx +81 -61
- package/src/components/MainComponents/ChatWrapper.tsx +100 -21
- package/src/components/MainComponents/MediaMessage.tsx +18 -8
- package/src/components/MainComponents/MessageContainer.tsx +4 -2
- package/src/components/MainComponents/MessageList.tsx +185 -31
- package/src/components/MainComponents/ReduxWrapper.tsx +19 -9
- package/src/components/MainComponents/SystemMessage.tsx +38 -26
- package/src/components/MessageBubble/Message.tsx +110 -27
- package/src/components/MessageBubble/MessageInteractions.tsx +98 -29
- package/src/components/Modals/AttachSheet/AttachSheet.tsx +17 -3
- package/src/components/Modals/FilePreviewModal/DocumentViewer.tsx +93 -0
- package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +182 -37
- package/src/components/Modals/FilePreviewModal/PdfView.tsx +32 -4
- package/src/components/Modals/Modal/Modal.tsx +4 -1
- package/src/components/Modals/ModalWrapper/ModalWrapper.tsx +77 -44
- package/src/components/Modals/UserProfileModal/UserProfileModal.tsx +19 -15
- package/src/components/Modals/styledModalComponents.tsx +4 -9
- package/src/components/styled/MediaFilePreview.tsx +111 -5
- package/src/components/styled/MessageImage.tsx +58 -14
- package/src/components/styled/NewMessageLabel.tsx +8 -5
- package/src/components/styled/SendInput.tsx +67 -29
- package/src/components/styled/StyledInputComponents/MediaComponents.tsx +15 -5
- package/src/components/styled/StyledInputComponents/StyledInputComponents.tsx +7 -2
- package/src/components/styled/UnsupportedType.tsx +7 -1
- package/src/components/styled/VideoMessage.tsx +59 -18
- package/src/context/MessageNotificationContext.tsx +8 -8
- package/src/context/xmppProvider.tsx +337 -32
- package/src/helpers/chatAutoEnterer.ts +2 -8
- package/src/helpers/historyPreloadScheduler.ts +4 -1
- package/src/helpers/insertMessageWithDelimiter.ts +22 -15
- package/src/helpers/keyboardLayout.ts +18 -0
- package/src/helpers/mediaDimensions.ts +47 -0
- package/src/helpers/mimeToExtension.ts +135 -0
- package/src/helpers/normalizeRoomJid.ts +6 -0
- package/src/helpers/resolveInitBeforeLoadUser.ts +79 -0
- package/src/hooks/useChatRoomFocus.ts +95 -0
- package/src/hooks/useChatWrapperInit.ts +51 -46
- package/src/hooks/useEventHandlers.tsx +24 -1
- package/src/hooks/useHeapSender.tsx +2 -1
- package/src/hooks/useLogout.tsx +3 -3
- package/src/hooks/useMessageHeapState.tsx +6 -2
- package/src/hooks/usePushNotifications.ts +21 -8
- package/src/hooks/useRoomState.tsx +4 -0
- package/src/hooks/useSendMessage.tsx +333 -17
- package/src/hooks/useUnreadMessagesCounter.ts +13 -16
- package/src/main.ts +1 -0
- package/src/networking/api-requests/auth.api.ts +60 -2
- package/src/networking/apiClient.ts +2 -1
- package/src/networking/outboundQueue.ts +98 -0
- package/src/networking/xmpp/createTimeoutPromise.xmpp.ts +5 -1
- package/src/networking/xmpp/flushLastViewedToPrivateStore.ts +11 -13
- package/src/networking/xmpp/getRoomsPaged.xmpp.ts +27 -3
- package/src/networking/xmpp/presenceInRoom.xmpp.ts +24 -9
- package/src/networking/xmpp/sendTextMessage.xmpp.ts +2 -1
- package/src/networking/xmppClient.ts +399 -31
- package/src/roomStore/Middleware/unreadMidlleware.tsx +23 -5
- package/src/roomStore/roomHeapSlice.ts +47 -4
- package/src/roomStore/roomsSlice.ts +177 -27
- package/src/types/models/media.model.ts +3 -0
- package/src/types/models/xmpp.model.ts +2 -1
- package/src/types/types.ts +25 -0
- package/src/utils/installPromiseRejectionTracker.ts +91 -0
- package/src/utils/resendMessage.ts +2 -1
|
@@ -22,10 +22,35 @@ import { inviteRoomRequest } from './xmpp/inviteRoomRequest.xmpp';
|
|
|
22
22
|
import { getRooms } from './xmpp/getRooms.xmpp';
|
|
23
23
|
import { handleStanza } from './xmpp/handleStanzas.xmpp';
|
|
24
24
|
import { pushLog as devPushLog } from '../utils/devLogger';
|
|
25
|
+
import { normalizeRoomJid } from '../helpers/normalizeRoomJid';
|
|
26
|
+
import {
|
|
27
|
+
clearOutboundSends,
|
|
28
|
+
enqueueOutboundSend,
|
|
29
|
+
flushOutboundSends,
|
|
30
|
+
} from './outboundQueue';
|
|
25
31
|
|
|
26
32
|
// Canonical production XMPP WSS endpoint (standard wss/443, no port suffix).
|
|
27
33
|
const DEFAULT_DEV_SERVER = 'xmpp.chat.ethora.com';
|
|
28
34
|
|
|
35
|
+
// @xmpp/client surfaces SASL/stream errors in a few shapes depending on
|
|
36
|
+
// where it fails — XMPPError on stream:error, SASLError on SASL bind, or
|
|
37
|
+
// a generic Error whose message contains "not-authorized". Match all of
|
|
38
|
+
// them so we trigger the credentials refresh path consistently.
|
|
39
|
+
function isNotAuthorizedError(err: any): boolean {
|
|
40
|
+
if (!err) {return false;}
|
|
41
|
+
const condition =
|
|
42
|
+
err?.condition ||
|
|
43
|
+
err?.name ||
|
|
44
|
+
err?.type ||
|
|
45
|
+
err?.error?.condition ||
|
|
46
|
+
'';
|
|
47
|
+
if (typeof condition === 'string' && condition.includes('not-authorized')) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
const message = (err?.message || err?.text || '').toString();
|
|
51
|
+
return /not-authorized|SASLError/i.test(message);
|
|
52
|
+
}
|
|
53
|
+
|
|
29
54
|
type HistorySource = 'active' | 'send_ack' | 'background' | 'default';
|
|
30
55
|
|
|
31
56
|
interface HistoryOptions {
|
|
@@ -40,6 +65,11 @@ interface MamInFlightEntry {
|
|
|
40
65
|
startedAt: number;
|
|
41
66
|
}
|
|
42
67
|
|
|
68
|
+
export type XmppCredentialsProvider = () => Promise<{
|
|
69
|
+
username: string;
|
|
70
|
+
password: string;
|
|
71
|
+
}>;
|
|
72
|
+
|
|
43
73
|
export class XmppClient {
|
|
44
74
|
client!: Client;
|
|
45
75
|
xmppSettings?: xmppSettingsInterface;
|
|
@@ -54,7 +84,35 @@ export class XmppClient {
|
|
|
54
84
|
reconnectAttempts = 0;
|
|
55
85
|
maxReconnectAttempts = 5;
|
|
56
86
|
reconnectDelay = 2000;
|
|
87
|
+
// Upper bound on the backoff delay. The old hard stop at
|
|
88
|
+
// maxReconnectAttempts gave up entirely after ~62s; now we keep retrying
|
|
89
|
+
// at a steady cadence on a long outage and just clamp the delay here.
|
|
90
|
+
maxReconnectDelay = 30000;
|
|
91
|
+
// Timestamp of the last forced reconnect so bursty external triggers
|
|
92
|
+
// (NetInfo + AppState + the provider watchdog firing together) can't
|
|
93
|
+
// spawn a reconnect storm.
|
|
94
|
+
private lastReconnectAt = 0;
|
|
57
95
|
suppressReconnect = false;
|
|
96
|
+
// Tracked so close()/disconnect() can cancel a pending reconnect —
|
|
97
|
+
// otherwise a scheduleReconnect() timer fires after logout and (until
|
|
98
|
+
// suppressReconnect short-circuits it) keeps the dead instance alive.
|
|
99
|
+
private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
|
100
|
+
// Single-flight guard for reconnect(). Multiple triggers can fire near-
|
|
101
|
+
// simultaneously (AppState 'active' + NetInfo restore + a scheduled
|
|
102
|
+
// timer). Without this, two overlapping reconnect()s each spawn a fresh
|
|
103
|
+
// underlying client and one is leaked with its `stanza` handler still
|
|
104
|
+
// bound → duplicate stanza dispatch into redux.
|
|
105
|
+
private reconnecting = false;
|
|
106
|
+
|
|
107
|
+
// Set when SASL fails with `not-authorized` (typically a stale JWT-
|
|
108
|
+
// derived XMPP password after idle). Triggers the credentialsProvider
|
|
109
|
+
// refresh path in reconnect().
|
|
110
|
+
lastAuthError: 'not-authorized' | null = null;
|
|
111
|
+
private credentialsProvider: XmppCredentialsProvider | null = null;
|
|
112
|
+
private credentialsRefreshInFlight: Promise<void> | null = null;
|
|
113
|
+
// Fired by the provider on every 'online' so it can re-join MUC rooms
|
|
114
|
+
// after a reconnect (bug #21 — reconnected but not in the room).
|
|
115
|
+
private onOnlineCallback: (() => void) | null = null;
|
|
58
116
|
|
|
59
117
|
// ---- QoS state (mirrors web XmppClient) ----------------------------
|
|
60
118
|
presencesReady = false;
|
|
@@ -74,6 +132,29 @@ export class XmppClient {
|
|
|
74
132
|
return this.client && this.client.status === 'online';
|
|
75
133
|
}
|
|
76
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Inject (or replace) the callback used by reconnect() to fetch fresh
|
|
137
|
+
* XMPP credentials after a `not-authorized` SASL failure. The provider
|
|
138
|
+
* is responsible for refreshing whatever upstream token is needed
|
|
139
|
+
* (REST JWT, /users/client, etc.) and returning the resulting
|
|
140
|
+
* username + password.
|
|
141
|
+
*/
|
|
142
|
+
setCredentialsProvider(provider: XmppCredentialsProvider | null) {
|
|
143
|
+
this.credentialsProvider = provider;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Install a callback invoked on every successful 'online'. The provider
|
|
147
|
+
* uses it to re-join MUC rooms after a reconnect. */
|
|
148
|
+
setOnOnline(cb: (() => void) | null) {
|
|
149
|
+
this.onOnlineCallback = cb;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Swap in fresh credentials before the next reconnect. */
|
|
153
|
+
updateCredentials(username: string, password: string) {
|
|
154
|
+
this.username = username;
|
|
155
|
+
this.password = password;
|
|
156
|
+
}
|
|
157
|
+
|
|
77
158
|
constructor(
|
|
78
159
|
username: string,
|
|
79
160
|
password: string,
|
|
@@ -229,12 +310,24 @@ export class XmppClient {
|
|
|
229
310
|
this.xmppSettings?.conference || `conference.${this.host}`;
|
|
230
311
|
this.status = 'connecting';
|
|
231
312
|
|
|
313
|
+
// Defensive: if a previous underlying client is still referenced
|
|
314
|
+
// (initializeClient reached from a path other than reconnect()),
|
|
315
|
+
// detach its handlers before we overwrite the ref so the orphaned
|
|
316
|
+
// client can't keep dispatching stanzas into redux.
|
|
317
|
+
try {
|
|
318
|
+
this.detachEventListeners();
|
|
319
|
+
} catch {}
|
|
320
|
+
|
|
232
321
|
this.client = xmpp.client({
|
|
233
322
|
service: url,
|
|
234
323
|
username: walletToUsername(this.username),
|
|
235
324
|
password: this.password,
|
|
236
325
|
});
|
|
237
326
|
|
|
327
|
+
try {
|
|
328
|
+
(this.client as any)?.reconnect?.stop?.();
|
|
329
|
+
} catch {}
|
|
330
|
+
|
|
238
331
|
// Concurrent room preloads each register their own short-lived
|
|
239
332
|
// 'stanza' handler (getHistory, getRooms, getChatsPrivateStore,
|
|
240
333
|
// ...). With 5+ rooms preloading in parallel, Node's default
|
|
@@ -265,7 +358,18 @@ export class XmppClient {
|
|
|
265
358
|
stanza?.toString ? stanza.toString() : undefined
|
|
266
359
|
);
|
|
267
360
|
} catch {}
|
|
268
|
-
|
|
361
|
+
const result = origSend(stanza);
|
|
362
|
+
// Most stanza helpers fire-and-forget `client.send(...)`
|
|
363
|
+
// without awaiting/catching. On @xmpp/client builds where
|
|
364
|
+
// send() returns a Promise, a transient send failure (e.g.
|
|
365
|
+
// mid-reconnect) then surfaces as a red-screen "Uncaught (in
|
|
366
|
+
// promise, id: N)" (#4). Attach a no-op catch so the
|
|
367
|
+
// rejection is considered handled — callers that DO await/
|
|
368
|
+
// catch still get it, because we return the SAME promise.
|
|
369
|
+
if (result && typeof (result as any).catch === 'function') {
|
|
370
|
+
(result as any).catch(() => {});
|
|
371
|
+
}
|
|
372
|
+
return result;
|
|
269
373
|
};
|
|
270
374
|
// Property may be non-writable on some builds; defineProperty
|
|
271
375
|
// gives us a clearer error than a plain assignment.
|
|
@@ -285,7 +389,21 @@ export class XmppClient {
|
|
|
285
389
|
this.attachEventListeners();
|
|
286
390
|
this.client.start().catch((error: any) => {
|
|
287
391
|
console.error('Error starting xmpp client:', error);
|
|
392
|
+
if (isNotAuthorizedError(error)) {
|
|
393
|
+
this.lastAuthError = 'not-authorized';
|
|
394
|
+
}
|
|
288
395
|
this.status = 'error';
|
|
396
|
+
// A connect-time failure lands in 'error', which — unlike a
|
|
397
|
+
// 'disconnect' after being online — has no other retry path
|
|
398
|
+
// (onError doesn't reschedule; the provider only reacts to
|
|
399
|
+
// 'offline'). Schedule a backoff retry so a failed initial or
|
|
400
|
+
// reconnect attempt recovers on its own. reconnect() refreshes
|
|
401
|
+
// creds first when the failure was a SASL not-authorized.
|
|
402
|
+
if (!this.suppressReconnect) {
|
|
403
|
+
try {
|
|
404
|
+
this.scheduleReconnect();
|
|
405
|
+
} catch {}
|
|
406
|
+
}
|
|
289
407
|
});
|
|
290
408
|
} catch (error) {
|
|
291
409
|
console.error('Error initializing client:', error);
|
|
@@ -293,8 +411,22 @@ export class XmppClient {
|
|
|
293
411
|
}
|
|
294
412
|
}
|
|
295
413
|
|
|
414
|
+
// Stored so detachEventListeners() can remove exactly the handlers we
|
|
415
|
+
// added (not xmpp.js's internal ones). Without this a re-login left the
|
|
416
|
+
// previous instance's `stanza` handler bound, double-dispatching every
|
|
417
|
+
// incoming stanza into the (new session's) redux store.
|
|
418
|
+
private onDisconnect?: () => void;
|
|
419
|
+
private onOnline?: () => void;
|
|
420
|
+
private onError?: (error: any) => void;
|
|
421
|
+
private onStanza?: (stanza: any) => void;
|
|
422
|
+
|
|
296
423
|
attachEventListeners() {
|
|
297
|
-
|
|
424
|
+
// Idempotent: drop any previously-attached handlers first so a
|
|
425
|
+
// reconnect that re-runs initializeClient on a fresh underlying
|
|
426
|
+
// client doesn't stack duplicates.
|
|
427
|
+
this.detachEventListeners();
|
|
428
|
+
|
|
429
|
+
this.onDisconnect = () => {
|
|
298
430
|
console.log('XMPP disconnected.');
|
|
299
431
|
this.status = 'offline';
|
|
300
432
|
try {
|
|
@@ -302,9 +434,20 @@ export class XmppClient {
|
|
|
302
434
|
// that don't reference it; tree-shaken via dead-code elim.
|
|
303
435
|
devPushLog('xmpp', 'disconnect');
|
|
304
436
|
} catch {}
|
|
305
|
-
|
|
437
|
+
// Self-heal at the socket level. The provider's reconnect effect
|
|
438
|
+
// keys on `client.status`, but that's a mutated class field React
|
|
439
|
+
// does NOT track — so a silent socket drop (e.g. JS paused by the
|
|
440
|
+
// debugger, iOS WebSocket suspend) never re-runs the effect and the
|
|
441
|
+
// client sits 'offline' forever (sends then queue and time out).
|
|
442
|
+
// Driving reconnect here makes recovery independent of React.
|
|
443
|
+
if (!this.suppressReconnect) {
|
|
444
|
+
try {
|
|
445
|
+
this.scheduleReconnect();
|
|
446
|
+
} catch {}
|
|
447
|
+
}
|
|
448
|
+
};
|
|
306
449
|
|
|
307
|
-
this.
|
|
450
|
+
this.onOnline = () => {
|
|
308
451
|
console.log('XMPP online.', new Date());
|
|
309
452
|
this.status = 'online';
|
|
310
453
|
this.presencesReady = true;
|
|
@@ -316,10 +459,35 @@ export class XmppClient {
|
|
|
316
459
|
this.username
|
|
317
460
|
);
|
|
318
461
|
} catch {}
|
|
319
|
-
|
|
462
|
+
// Notify the provider that the session is live again. On a RECONNECT
|
|
463
|
+
// (new stream) the client is no longer joined to any MUC, so the
|
|
464
|
+
// provider must re-send room presences — otherwise messages get a
|
|
465
|
+
// local double-tick but never reach the room (bug #21). Harmless on
|
|
466
|
+
// the first connect (room list not loaded yet → joins nothing; the
|
|
467
|
+
// bootstrap's own allRoomPresences handles that pass).
|
|
468
|
+
try {
|
|
469
|
+
this.onOnlineCallback?.();
|
|
470
|
+
} catch (err) {
|
|
471
|
+
console.warn('onOnline callback failed', err);
|
|
472
|
+
}
|
|
473
|
+
// Drain any sends that were buffered while the stream was down. The
|
|
474
|
+
// presence stanzas from onOnlineCallback above were written to the
|
|
475
|
+
// socket synchronously (allRoomPresences → presenceInRoom send the
|
|
476
|
+
// <presence> before yielding), so flushing here puts the message
|
|
477
|
+
// stanzas AFTER the joins on the same stream — XMPP's in-order
|
|
478
|
+
// per-stream processing guarantees the MUC join lands first.
|
|
479
|
+
try {
|
|
480
|
+
this.flushPendingSends();
|
|
481
|
+
} catch (err) {
|
|
482
|
+
console.warn('flushPendingSends failed', err);
|
|
483
|
+
}
|
|
484
|
+
};
|
|
320
485
|
|
|
321
|
-
this.
|
|
486
|
+
this.onError = (error: any) => {
|
|
322
487
|
console.error('XMPP client error:', error);
|
|
488
|
+
if (isNotAuthorizedError(error)) {
|
|
489
|
+
this.lastAuthError = 'not-authorized';
|
|
490
|
+
}
|
|
323
491
|
try {
|
|
324
492
|
devPushLog(
|
|
325
493
|
'xmpp',
|
|
@@ -327,9 +495,9 @@ export class XmppClient {
|
|
|
327
495
|
(error && error.message) || error
|
|
328
496
|
);
|
|
329
497
|
} catch {}
|
|
330
|
-
}
|
|
498
|
+
};
|
|
331
499
|
|
|
332
|
-
this.
|
|
500
|
+
this.onStanza = (stanza: any) => {
|
|
333
501
|
try {
|
|
334
502
|
const tag = stanza?.name || 'stanza';
|
|
335
503
|
const id = stanza?.attrs?.id || '';
|
|
@@ -342,7 +510,27 @@ export class XmppClient {
|
|
|
342
510
|
);
|
|
343
511
|
} catch {}
|
|
344
512
|
handleStanza.bind(this, stanza, this)();
|
|
345
|
-
}
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
this.client.on('disconnect', this.onDisconnect);
|
|
516
|
+
this.client.on('online', this.onOnline);
|
|
517
|
+
this.client.on('error', this.onError);
|
|
518
|
+
this.client.on('stanza', this.onStanza);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
detachEventListeners() {
|
|
522
|
+
const c = this.client as any;
|
|
523
|
+
if (!c?.removeListener) {return;}
|
|
524
|
+
try {
|
|
525
|
+
if (this.onDisconnect) {c.removeListener('disconnect', this.onDisconnect);}
|
|
526
|
+
if (this.onOnline) {c.removeListener('online', this.onOnline);}
|
|
527
|
+
if (this.onError) {c.removeListener('error', this.onError);}
|
|
528
|
+
if (this.onStanza) {c.removeListener('stanza', this.onStanza);}
|
|
529
|
+
} catch {}
|
|
530
|
+
this.onDisconnect = undefined;
|
|
531
|
+
this.onOnline = undefined;
|
|
532
|
+
this.onError = undefined;
|
|
533
|
+
this.onStanza = undefined;
|
|
346
534
|
}
|
|
347
535
|
|
|
348
536
|
/**
|
|
@@ -374,36 +562,150 @@ export class XmppClient {
|
|
|
374
562
|
|
|
375
563
|
scheduleReconnect() {
|
|
376
564
|
if (this.suppressReconnect) {return;}
|
|
377
|
-
if (this.
|
|
378
|
-
console.error('Max reconnect attempts reached.');
|
|
379
|
-
return;
|
|
380
|
-
}
|
|
565
|
+
if (this.reconnecting || this.reconnectTimer) {return;}
|
|
381
566
|
this.reconnectAttempts++;
|
|
382
|
-
const
|
|
383
|
-
this.
|
|
567
|
+
const exp = Math.min(
|
|
568
|
+
this.reconnectAttempts - 1,
|
|
569
|
+
Math.max(0, this.maxReconnectAttempts)
|
|
570
|
+
);
|
|
571
|
+
const delay = Math.min(
|
|
572
|
+
this.maxReconnectDelay,
|
|
573
|
+
this.reconnectDelay * Math.pow(2, exp)
|
|
574
|
+
);
|
|
384
575
|
console.log(`Reconnecting attempt ${this.reconnectAttempts} in ${delay}ms`);
|
|
385
|
-
|
|
576
|
+
// No clearTimeout needed — the guard above guarantees no pending timer.
|
|
577
|
+
this.reconnectTimer = setTimeout(() => {
|
|
578
|
+
this.reconnectTimer = null;
|
|
579
|
+
// The connection may have recovered on its own (xmpp.js auto-
|
|
580
|
+
// reconnect) between scheduling and firing — don't tear down a
|
|
581
|
+
// healthy stream. forceReconnect() bypasses this by calling
|
|
582
|
+
// reconnect() directly.
|
|
583
|
+
if (this.status === 'online') {
|
|
584
|
+
this.reconnectAttempts = 0;
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
this.reconnect();
|
|
588
|
+
}, delay);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Force an immediate reconnect, resetting the backoff counter. Called
|
|
593
|
+
* by the provider when NetInfo reports the network came back or the app
|
|
594
|
+
* returned to the foreground — we don't want to wait out the existing
|
|
595
|
+
* exponential delay (or a maxed-out attempt count) in those cases.
|
|
596
|
+
*/
|
|
597
|
+
forceReconnect() {
|
|
598
|
+
if (this.suppressReconnect) {return;}
|
|
599
|
+
// Debounce bursts: NetInfo restore + AppState 'active' + the provider
|
|
600
|
+
// watchdog can fire within the same instant. reconnect() dedups
|
|
601
|
+
// CONCURRENT calls, but sequential ones (one finishes, the next
|
|
602
|
+
// arrives) would still each tear down + re-create the client. Ignore
|
|
603
|
+
// forces within 2s of the last one.
|
|
604
|
+
const now = Date.now();
|
|
605
|
+
if (now - this.lastReconnectAt < 2000) {return;}
|
|
606
|
+
this.lastReconnectAt = now;
|
|
607
|
+
if (this.reconnectTimer) {
|
|
608
|
+
clearTimeout(this.reconnectTimer);
|
|
609
|
+
this.reconnectTimer = null;
|
|
610
|
+
}
|
|
611
|
+
this.reconnectAttempts = 0;
|
|
612
|
+
this.reconnect();
|
|
386
613
|
}
|
|
387
614
|
|
|
388
|
-
reconnect() {
|
|
615
|
+
async reconnect() {
|
|
389
616
|
if (this.suppressReconnect) {return;}
|
|
617
|
+
// Single-flight: ignore overlapping triggers (AppState + NetInfo +
|
|
618
|
+
// scheduled timer) so we never spawn two underlying clients and leak
|
|
619
|
+
// one with live listeners (duplicate stanza dispatch).
|
|
620
|
+
if (this.reconnecting) {
|
|
621
|
+
console.log('reconnect already in progress — skip');
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
this.reconnecting = true;
|
|
390
625
|
console.log('Attempting to reconnect xmpp client...');
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
626
|
+
|
|
627
|
+
try {
|
|
628
|
+
// If the last failure was a SASL `not-authorized` (typically a stale
|
|
629
|
+
// JWT-derived XMPP password after idle), try to fetch fresh creds
|
|
630
|
+
// before retrying. Without this, reconnect retries with the same
|
|
631
|
+
// stale password forever and the user has to kill the app.
|
|
632
|
+
if (this.lastAuthError === 'not-authorized' && this.credentialsProvider) {
|
|
633
|
+
try {
|
|
634
|
+
await this.refreshCredentialsOnce();
|
|
635
|
+
} catch (err) {
|
|
636
|
+
console.warn(
|
|
637
|
+
'XMPP credential refresh failed; reconnecting with cached creds',
|
|
638
|
+
err
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// Tear the OLD underlying client down FULLY before spinning up a new
|
|
644
|
+
// one: detach our handlers (so its `stanza` handler can't keep
|
|
645
|
+
// dispatching) and stop it. detachEventListeners() must run while
|
|
646
|
+
// `this.client` still points at the old client — i.e. before
|
|
647
|
+
// initializeClient() reassigns it.
|
|
648
|
+
const old = this.client;
|
|
649
|
+
if (old) {
|
|
650
|
+
this.detachEventListeners();
|
|
651
|
+
try {
|
|
652
|
+
await old.stop();
|
|
653
|
+
} catch {}
|
|
654
|
+
}
|
|
396
655
|
this.initializeClient();
|
|
656
|
+
} finally {
|
|
657
|
+
this.reconnecting = false;
|
|
397
658
|
}
|
|
398
659
|
}
|
|
399
660
|
|
|
661
|
+
/**
|
|
662
|
+
* Single-flight wrapper around credentialsProvider. Multiple concurrent
|
|
663
|
+
* reconnect() calls (e.g. from the provider's reconnect effect firing
|
|
664
|
+
* twice quickly) share one in-flight request and update `this.password`
|
|
665
|
+
* exactly once.
|
|
666
|
+
*/
|
|
667
|
+
private refreshCredentialsOnce(): Promise<void> {
|
|
668
|
+
if (this.credentialsRefreshInFlight) {return this.credentialsRefreshInFlight;}
|
|
669
|
+
if (!this.credentialsProvider) {return Promise.resolve();}
|
|
670
|
+
const provider = this.credentialsProvider;
|
|
671
|
+
this.credentialsRefreshInFlight = (async () => {
|
|
672
|
+
try {
|
|
673
|
+
const fresh = await provider();
|
|
674
|
+
if (fresh?.username && fresh?.password) {
|
|
675
|
+
// Apply whatever the provider returns and retry. We do NOT try to
|
|
676
|
+
// detect "auth expired" by comparing passwords — a stable
|
|
677
|
+
// xmppPassword (e.g. deterministic from the JWT/wallet) is the
|
|
678
|
+
// NORMAL case for a transient blip, and treating it as expiry
|
|
679
|
+
// falsely bricked recoverable sessions (sending stopped). If the
|
|
680
|
+
// creds really are stale we simply get not-authorized again and
|
|
681
|
+
// retry, which is the confirmed-good 26.5.7 behaviour.
|
|
682
|
+
this.updateCredentials(fresh.username, fresh.password);
|
|
683
|
+
this.lastAuthError = null;
|
|
684
|
+
}
|
|
685
|
+
} finally {
|
|
686
|
+
this.credentialsRefreshInFlight = null;
|
|
687
|
+
}
|
|
688
|
+
})();
|
|
689
|
+
return this.credentialsRefreshInFlight;
|
|
690
|
+
}
|
|
691
|
+
|
|
400
692
|
async disconnect(options?: { suppressReconnect?: boolean }): Promise<void> {
|
|
401
693
|
if (options?.suppressReconnect) {this.suppressReconnect = true;}
|
|
402
694
|
return this.close();
|
|
403
695
|
}
|
|
404
696
|
|
|
405
697
|
async close() {
|
|
698
|
+
// Cancel any pending reconnect so a stale timer can't resurrect this
|
|
699
|
+
// instance after teardown/logout.
|
|
700
|
+
if (this.reconnectTimer) {
|
|
701
|
+
clearTimeout(this.reconnectTimer);
|
|
702
|
+
this.reconnectTimer = null;
|
|
703
|
+
}
|
|
406
704
|
if (this.client) {
|
|
705
|
+
// Detach OUR handlers first so a late stanza during the close
|
|
706
|
+
// handshake can't re-enter handleStanza and dispatch into a store
|
|
707
|
+
// that's being wiped (or, on re-login, the new session's store).
|
|
708
|
+
this.detachEventListeners();
|
|
407
709
|
try {
|
|
408
710
|
await this.client.stop();
|
|
409
711
|
console.log('XMPP client connection closed.');
|
|
@@ -412,6 +714,11 @@ export class XmppClient {
|
|
|
412
714
|
}
|
|
413
715
|
}
|
|
414
716
|
this.status = 'offline';
|
|
717
|
+
this.presencesReady = false;
|
|
718
|
+
// Permanent teardown (logout / unmount) — drop buffered sends so they
|
|
719
|
+
// don't replay into the next session. Reconnect uses old.stop(), NOT
|
|
720
|
+
// close(), so the queue still survives a transient drop+reconnect.
|
|
721
|
+
clearOutboundSends();
|
|
415
722
|
}
|
|
416
723
|
|
|
417
724
|
getRoomsStanza = async () => {
|
|
@@ -429,11 +736,11 @@ export class XmppClient {
|
|
|
429
736
|
}
|
|
430
737
|
|
|
431
738
|
leaveTheRoomStanza = (roomJID: string) => {
|
|
432
|
-
leaveTheRoom(roomJID, this.client);
|
|
739
|
+
leaveTheRoom(normalizeRoomJid(roomJID, this.conference), this.client);
|
|
433
740
|
};
|
|
434
741
|
|
|
435
742
|
presenceInRoomStanza = (roomJID: string) => {
|
|
436
|
-
presenceInRoom(this.client, roomJID);
|
|
743
|
+
presenceInRoom(this.client, normalizeRoomJid(roomJID, this.conference));
|
|
437
744
|
};
|
|
438
745
|
|
|
439
746
|
getHistoryStanza = async (
|
|
@@ -456,7 +763,7 @@ export class XmppClient {
|
|
|
456
763
|
};
|
|
457
764
|
|
|
458
765
|
getLastMessageArchiveStanza(roomJID: string) {
|
|
459
|
-
getLastMessage(this.client, roomJID);
|
|
766
|
+
getLastMessage(this.client, normalizeRoomJid(roomJID, this.conference));
|
|
460
767
|
}
|
|
461
768
|
|
|
462
769
|
setRoomImageStanza = (
|
|
@@ -469,13 +776,28 @@ export class XmppClient {
|
|
|
469
776
|
};
|
|
470
777
|
|
|
471
778
|
getRoomInfoStanza = (roomJID: string) => {
|
|
472
|
-
getRoomInfo(roomJID, this.client);
|
|
779
|
+
getRoomInfo(normalizeRoomJid(roomJID, this.conference), this.client);
|
|
473
780
|
};
|
|
474
781
|
|
|
475
782
|
getRoomMembersStanza = (roomJID: string) => {
|
|
476
|
-
getRoomMembers(roomJID, this.client);
|
|
783
|
+
getRoomMembers(normalizeRoomJid(roomJID, this.conference), this.client);
|
|
477
784
|
};
|
|
478
785
|
|
|
786
|
+
/**
|
|
787
|
+
* True when the underlying stream is live enough to actually put a
|
|
788
|
+
* stanza on the wire. Used to gate sends: when false we buffer instead
|
|
789
|
+
* of firing a fire-and-forget stanza into a dead/half-open socket
|
|
790
|
+
* (which is silently dropped and only caught by the 30s watchdog).
|
|
791
|
+
*/
|
|
792
|
+
private isStreamReady(): boolean {
|
|
793
|
+
return !!this.client && this.status === 'online' && !this.reconnecting;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/** Replay buffered sends against this (now-online) client, in order. */
|
|
797
|
+
flushPendingSends() {
|
|
798
|
+
flushOutboundSends(this, Date.now());
|
|
799
|
+
}
|
|
800
|
+
|
|
479
801
|
//messages
|
|
480
802
|
sendMessage = (
|
|
481
803
|
roomJID: string,
|
|
@@ -489,7 +811,32 @@ export class XmppClient {
|
|
|
489
811
|
showInChannel?: boolean,
|
|
490
812
|
mainMessage?: string,
|
|
491
813
|
customId?: string
|
|
492
|
-
) => {
|
|
814
|
+
): boolean => {
|
|
815
|
+
// Stream down (start race / reconnect window): buffer and replay on
|
|
816
|
+
// the next 'online' instead of losing the stanza. Returns false so
|
|
817
|
+
// callers know the send was deferred, not delivered.
|
|
818
|
+
if (!this.isStreamReady()) {
|
|
819
|
+
enqueueOutboundSend({
|
|
820
|
+
optimisticId: customId || `send-text-message-${Date.now()}`,
|
|
821
|
+
roomJID,
|
|
822
|
+
enqueuedAt: Date.now(),
|
|
823
|
+
send: (c) =>
|
|
824
|
+
c.sendMessage(
|
|
825
|
+
roomJID,
|
|
826
|
+
firstName,
|
|
827
|
+
lastName,
|
|
828
|
+
photo,
|
|
829
|
+
walletAddress,
|
|
830
|
+
userMessage,
|
|
831
|
+
notDisplayedValue,
|
|
832
|
+
isReply,
|
|
833
|
+
showInChannel,
|
|
834
|
+
mainMessage,
|
|
835
|
+
customId
|
|
836
|
+
),
|
|
837
|
+
});
|
|
838
|
+
return false;
|
|
839
|
+
}
|
|
493
840
|
sendTextMessage(
|
|
494
841
|
this.client,
|
|
495
842
|
roomJID,
|
|
@@ -505,6 +852,7 @@ export class XmppClient {
|
|
|
505
852
|
this.devServer || DEFAULT_DEV_SERVER,
|
|
506
853
|
customId
|
|
507
854
|
);
|
|
855
|
+
return true;
|
|
508
856
|
};
|
|
509
857
|
|
|
510
858
|
deleteMessageStanza(room: string, msgId: string) {
|
|
@@ -550,7 +898,7 @@ export class XmppClient {
|
|
|
550
898
|
// src/networking/xmpp/flushLastViewedToPrivateStore.ts for details.
|
|
551
899
|
async flushLastViewedToPrivateStoreStanza(
|
|
552
900
|
rooms: Record<string, any> | null | undefined,
|
|
553
|
-
opts: {
|
|
901
|
+
opts: { visibleRoomJID?: string | null; onlyIfNoUnread?: boolean } = {}
|
|
554
902
|
) {
|
|
555
903
|
if (this.disableLastRead) {return false;}
|
|
556
904
|
try {
|
|
@@ -561,6 +909,19 @@ export class XmppClient {
|
|
|
561
909
|
}
|
|
562
910
|
|
|
563
911
|
sendMediaMessageStanza(roomJID: string, data: any, customId?: string) {
|
|
912
|
+
// Media: the file is already uploaded by the time we get here; only the
|
|
913
|
+
// stanza needs the stream. Buffer + replay on reconnect if it's down so
|
|
914
|
+
// the upload isn't wasted (otherwise the stanza is lost and the media
|
|
915
|
+
// bubble sits pending until the 30s watchdog fails it).
|
|
916
|
+
if (!this.isStreamReady()) {
|
|
917
|
+
enqueueOutboundSend({
|
|
918
|
+
optimisticId: customId || `send-media-message-${Date.now()}`,
|
|
919
|
+
roomJID,
|
|
920
|
+
enqueuedAt: Date.now(),
|
|
921
|
+
send: (c) => c.sendMediaMessageStanza(roomJID, data, customId),
|
|
922
|
+
});
|
|
923
|
+
return undefined;
|
|
924
|
+
}
|
|
564
925
|
return sendMediaMessage(
|
|
565
926
|
this.client,
|
|
566
927
|
roomJID,
|
|
@@ -611,9 +972,15 @@ export class XmppClient {
|
|
|
611
972
|
isReply?: boolean,
|
|
612
973
|
showInChannel?: boolean,
|
|
613
974
|
mainMessage?: string,
|
|
614
|
-
_langSource?: string
|
|
975
|
+
_langSource?: string,
|
|
976
|
+
customId?: string
|
|
615
977
|
) {
|
|
616
978
|
// No translate tag support yet — fall back to a regular text send.
|
|
979
|
+
// Forward the caller's customId so the optimistic message (heap/redux)
|
|
980
|
+
// reconciles with the server echo. Without it, translated sends went
|
|
981
|
+
// out with no client id → the echo carried a server-assigned id and
|
|
982
|
+
// surfaced as a stuck-pending message PLUS a duplicate (affected both
|
|
983
|
+
// the live translated-send path and resend).
|
|
617
984
|
this.sendMessage(
|
|
618
985
|
roomJID,
|
|
619
986
|
firstName,
|
|
@@ -624,7 +991,8 @@ export class XmppClient {
|
|
|
624
991
|
notDisplayedValue,
|
|
625
992
|
isReply,
|
|
626
993
|
showInChannel,
|
|
627
|
-
mainMessage
|
|
994
|
+
mainMessage,
|
|
995
|
+
customId
|
|
628
996
|
);
|
|
629
997
|
}
|
|
630
998
|
}
|
|
@@ -65,8 +65,15 @@ const TRIGGER_ACTIONS = new Set([
|
|
|
65
65
|
'roomMessages/editRoomMessage',
|
|
66
66
|
'roomMessages/setLastViewedTimestamp',
|
|
67
67
|
'roomMessages/setCurrentRoom',
|
|
68
|
+
'roomMessages/setVisibleRoom',
|
|
69
|
+
'roomMessages/clearVisibleRoom',
|
|
68
70
|
'roomMessages/addRoom',
|
|
69
71
|
'roomMessages/updateRoom',
|
|
72
|
+
// The history preload scheduler merges fetched pages via this action on
|
|
73
|
+
// re-entry; recompute so unread reflects messages that arrived while the
|
|
74
|
+
// app was away (the per-room fingerprint cache below skips no-op batches,
|
|
75
|
+
// e.g. the 'loading' marker dispatch that carries no messages).
|
|
76
|
+
'roomMessages/applyRoomsPreloadBatch',
|
|
70
77
|
]);
|
|
71
78
|
|
|
72
79
|
export const unreadMiddleware: Middleware =
|
|
@@ -95,7 +102,7 @@ export const unreadMiddleware: Middleware =
|
|
|
95
102
|
|
|
96
103
|
const state = storeAPI.getState();
|
|
97
104
|
const rooms = state.rooms.rooms;
|
|
98
|
-
const
|
|
105
|
+
const visibleRoomJID = state.rooms.visibleRoomJID;
|
|
99
106
|
const selfUser = state.chatSettingStore?.user;
|
|
100
107
|
const selfXmpp = selfUser?.xmppUsername || '';
|
|
101
108
|
const selfWallet = selfUser?.walletAddress || '';
|
|
@@ -104,13 +111,24 @@ export const unreadMiddleware: Middleware =
|
|
|
104
111
|
Object.keys(rooms).forEach((jid) => {
|
|
105
112
|
const room = rooms[jid];
|
|
106
113
|
if (!room) {return;}
|
|
107
|
-
// Skip rooms the user is currently viewing —
|
|
108
|
-
//
|
|
109
|
-
//
|
|
114
|
+
// Skip rooms the user is currently viewing — visibility clears the
|
|
115
|
+
// badge directly. Skip rooms with no reference point (covers
|
|
116
|
+
// undefined / null / 0) — after logout→login, hydrated rooms
|
|
110
117
|
// come back without lastViewedTimestamp, and treating that as 0
|
|
111
118
|
// made every history message satisfy `date > 0` and incorrectly
|
|
112
119
|
// bumped unread for already-seen content.
|
|
113
|
-
if (
|
|
120
|
+
if (jid === visibleRoomJID) {
|
|
121
|
+
if (room.unreadMessages !== 0) {
|
|
122
|
+
storeAPI.dispatch(
|
|
123
|
+
updateRoom({
|
|
124
|
+
jid,
|
|
125
|
+
updates: { unreadMessages: 0 },
|
|
126
|
+
})
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (!(room.lastViewedTimestamp > 0)) {return;}
|
|
114
132
|
|
|
115
133
|
// Fingerprint = "what would affect the answer". If neither the
|
|
116
134
|
// message count nor the lastViewedTimestamp changed since last
|