@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// RN port of the web MessageNotificationProvider. Renders Telegram-style
|
|
2
2
|
// in-app toasts when a new message arrives in a room that is NOT the
|
|
3
|
-
// currently
|
|
3
|
+
// currently visible room. Backed by the global messageNotificationManager,
|
|
4
4
|
// which means non-React code (stanza handlers) can push notifications.
|
|
5
5
|
import React, {
|
|
6
6
|
createContext,
|
|
@@ -63,8 +63,8 @@ export const MessageNotificationProvider: React.FC<ProviderProps> = ({
|
|
|
63
63
|
const contextConfig = useSelector(
|
|
64
64
|
(state: RootState) => state.chatSettingStore?.config
|
|
65
65
|
);
|
|
66
|
-
const
|
|
67
|
-
(state: RootState) => state.rooms.
|
|
66
|
+
const visibleRoomJID = useSelector(
|
|
67
|
+
(state: RootState) => state.rooms.visibleRoomJID
|
|
68
68
|
);
|
|
69
69
|
|
|
70
70
|
const config = propConfig || contextConfig;
|
|
@@ -104,9 +104,9 @@ export const MessageNotificationProvider: React.FC<ProviderProps> = ({
|
|
|
104
104
|
|
|
105
105
|
// Clear toasts when their room becomes active.
|
|
106
106
|
useEffect(() => {
|
|
107
|
-
if (!
|
|
108
|
-
setToasts((prev) => prev.filter((t) => t.roomJID !==
|
|
109
|
-
}, [
|
|
107
|
+
if (!visibleRoomJID) {return;}
|
|
108
|
+
setToasts((prev) => prev.filter((t) => t.roomJID !== visibleRoomJID));
|
|
109
|
+
}, [visibleRoomJID]);
|
|
110
110
|
|
|
111
111
|
const dismiss = useCallback((id: string) => {
|
|
112
112
|
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
@@ -142,7 +142,7 @@ export const MessageNotificationProvider: React.FC<ProviderProps> = ({
|
|
|
142
142
|
) => {
|
|
143
143
|
if (!isEnabled) {return;}
|
|
144
144
|
// Don't toast for the currently active room.
|
|
145
|
-
if (
|
|
145
|
+
if (visibleRoomJID && visibleRoomJID === roomJID) {return;}
|
|
146
146
|
const id = `msg-notification-${message.id}-${Date.now()}`;
|
|
147
147
|
const item: ToastItem = {
|
|
148
148
|
id,
|
|
@@ -159,7 +159,7 @@ export const MessageNotificationProvider: React.FC<ProviderProps> = ({
|
|
|
159
159
|
: next;
|
|
160
160
|
});
|
|
161
161
|
},
|
|
162
|
-
[isEnabled,
|
|
162
|
+
[isEnabled, visibleRoomJID, maxNotifications]
|
|
163
163
|
);
|
|
164
164
|
|
|
165
165
|
// Register with the global manager.
|
|
@@ -4,11 +4,16 @@ import React, {
|
|
|
4
4
|
useCallback,
|
|
5
5
|
useContext,
|
|
6
6
|
useEffect,
|
|
7
|
+
useMemo,
|
|
7
8
|
useRef,
|
|
8
9
|
useState,
|
|
9
10
|
} from 'react';
|
|
10
11
|
import { AppState, DeviceEventEmitter } from 'react-native';
|
|
11
|
-
import
|
|
12
|
+
import NetInfo from '@react-native-community/netinfo';
|
|
13
|
+
import XmppClient, {
|
|
14
|
+
XmppCredentialsProvider,
|
|
15
|
+
} from '../networking/xmppClient';
|
|
16
|
+
import { refreshTokens } from '../roomStore/chatSettingsSlice';
|
|
12
17
|
import {
|
|
13
18
|
IConfig,
|
|
14
19
|
ProviderBootstrapStatus,
|
|
@@ -17,6 +22,7 @@ import {
|
|
|
17
22
|
} from '../types/types';
|
|
18
23
|
import {
|
|
19
24
|
buildXmppClientKey,
|
|
25
|
+
getGlobalXmppClient,
|
|
20
26
|
getReusableXmppClientByKey,
|
|
21
27
|
isXmppClientReusable,
|
|
22
28
|
setGlobalXmppClient,
|
|
@@ -24,6 +30,7 @@ import {
|
|
|
24
30
|
} from '../utils/clientRegistry';
|
|
25
31
|
import {
|
|
26
32
|
applyResolvedUserToStore,
|
|
33
|
+
refreshUserCredentialsForXmpp,
|
|
27
34
|
resolveInitBeforeLoadUser,
|
|
28
35
|
} from '../helpers/resolveInitBeforeLoadUser';
|
|
29
36
|
import { ensureScopedChatCache } from '../helpers/ensureScopedChatCache';
|
|
@@ -31,7 +38,13 @@ import { getRooms as prefetchRoomsViaRest } from '../networking/api-requests/roo
|
|
|
31
38
|
import { allRoomPresences } from '../networking/xmpp/allRoomPresences.xmpp';
|
|
32
39
|
import { store } from '../roomStore';
|
|
33
40
|
import { logout, setStoreClient } from '../roomStore/chatSettingsSlice';
|
|
34
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
setLogoutState,
|
|
43
|
+
setVisibleRoom,
|
|
44
|
+
clearVisibleRoom,
|
|
45
|
+
setLastViewedTimestamp,
|
|
46
|
+
deleteRoomMessage,
|
|
47
|
+
} from '../roomStore/roomsSlice';
|
|
35
48
|
import { runHistoryPreloadScheduler } from '../helpers/historyPreloadScheduler';
|
|
36
49
|
import { asyncLocalStorage } from '../hooks/useLocalStorage';
|
|
37
50
|
import { localStorageConstants } from '../helpers/constants/LOCAL_STORAGE';
|
|
@@ -59,15 +72,18 @@ const XmppContext = createContext<XmppContextType | null>(null);
|
|
|
59
72
|
interface XmppProviderProps {
|
|
60
73
|
children: ReactNode;
|
|
61
74
|
config?: IConfig;
|
|
75
|
+
// Consumer-supplied "is the chat surface shown" signal for hosts that
|
|
76
|
+
// keep <Chat> mounted while hidden (tab/route navigators). See the
|
|
77
|
+
// visibility effect below. Omit when you unmount <Chat> on hide.
|
|
78
|
+
isVisible?: boolean;
|
|
62
79
|
}
|
|
63
80
|
|
|
64
81
|
const LOGOUT_EVENT = 'ethora-xmpp-logout';
|
|
65
82
|
|
|
66
|
-
export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config }) => {
|
|
83
|
+
export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config, isVisible }) => {
|
|
67
84
|
const [client, setClient] = useState<XmppClient | null>(null);
|
|
68
85
|
const [providerBootstrapStatus, setProviderBootstrapStatus] =
|
|
69
86
|
useState<ProviderBootstrapStatus>('idle');
|
|
70
|
-
const [reconnectAttempts, setReconnectAttempts] = useState(0);
|
|
71
87
|
|
|
72
88
|
// Track which "init mode" this provider runs in. When config.initBeforeLoad
|
|
73
89
|
// is true, the provider owns bootstrap and ChatWrapper just waits.
|
|
@@ -77,6 +93,79 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config })
|
|
|
77
93
|
const completedBootstrapKeyRef = useRef<string>('');
|
|
78
94
|
const inflightBootstrapKeyRef = useRef<string>('');
|
|
79
95
|
|
|
96
|
+
// Callback XmppClient uses to fetch fresh credentials after a SASL
|
|
97
|
+
// `not-authorized` (typically JWT expiry during idle, bug #17).
|
|
98
|
+
// Always installed — the underlying `refreshUserCredentialsForXmpp`
|
|
99
|
+
// picks the right path based on config:
|
|
100
|
+
// - `jwtLogin` → re-exchange JWT via `/users/client`
|
|
101
|
+
// - `userLogin` / `customLogin` / store / persisted → `/users/my`
|
|
102
|
+
// with REST refresh chain (handles 401 → refresh → retry)
|
|
103
|
+
// Returns last-known cached creds if nothing usable is available so
|
|
104
|
+
// the XmppClient at least doesn't crash on `undefined.password`.
|
|
105
|
+
const credentialsProvider = useMemo<XmppCredentialsProvider>(() => {
|
|
106
|
+
return async () => {
|
|
107
|
+
// 1. Consumer-supplied REST refresh (when wired). This runs
|
|
108
|
+
// before the SDK's own refresh chain so non-Ethora backends
|
|
109
|
+
// can keep auth state in sync with their own token endpoint.
|
|
110
|
+
const customRefresh = config?.refreshTokens?.refreshFunction;
|
|
111
|
+
if (customRefresh) {
|
|
112
|
+
try {
|
|
113
|
+
const result = await customRefresh();
|
|
114
|
+
if (result?.accessToken) {
|
|
115
|
+
store.dispatch(
|
|
116
|
+
refreshTokens({
|
|
117
|
+
token: result.accessToken,
|
|
118
|
+
refreshToken: result.refreshToken || '',
|
|
119
|
+
})
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
} catch (err) {
|
|
123
|
+
devPushLog('warn', 'XMPP creds refresh: customRefresh failed', err);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// 2. Re-mint XMPP creds via the right priority chain for the
|
|
128
|
+
// current auth mode. This call ALWAYS hydrates (unlike
|
|
129
|
+
// `resolveInitBeforeLoadUser` which short-circuits when a
|
|
130
|
+
// cached user already has xmppCredentials).
|
|
131
|
+
const fresh = await refreshUserCredentialsForXmpp(config).catch(
|
|
132
|
+
(err) => {
|
|
133
|
+
devPushLog('warn', 'XMPP creds refresh: full chain failed', err);
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
if (fresh) {
|
|
138
|
+
applyResolvedUserToStore(fresh);
|
|
139
|
+
return {
|
|
140
|
+
username:
|
|
141
|
+
fresh.xmppUsername ||
|
|
142
|
+
fresh.defaultWallet?.walletAddress ||
|
|
143
|
+
'',
|
|
144
|
+
password: fresh.xmppPassword || '',
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// 3. Last-resort: return the cached creds so reconnect() can
|
|
149
|
+
// still attempt a connection. If the original failure was
|
|
150
|
+
// a stale JWT this will fail again and the user has to
|
|
151
|
+
// re-mount the chat — but at least we don't break the
|
|
152
|
+
// transient-network-blip case where the cached creds are
|
|
153
|
+
// still valid.
|
|
154
|
+
const u = store.getState().chatSettingStore.user;
|
|
155
|
+
return {
|
|
156
|
+
username: u?.xmppUsername || u?.defaultWallet?.walletAddress || '',
|
|
157
|
+
password: u?.xmppPassword || '',
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
}, [
|
|
161
|
+
config?.jwtLogin?.enabled,
|
|
162
|
+
config?.jwtLogin?.token,
|
|
163
|
+
config?.refreshTokens?.refreshFunction,
|
|
164
|
+
config?.userLogin?.enabled,
|
|
165
|
+
config?.customLogin?.enabled,
|
|
166
|
+
config?.initBeforeLoadAuth?.myEndpoint,
|
|
167
|
+
]);
|
|
168
|
+
|
|
80
169
|
const initializeClient = useCallback(
|
|
81
170
|
async (
|
|
82
171
|
username: string,
|
|
@@ -103,7 +192,17 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config })
|
|
|
103
192
|
// 3. Otherwise create a new client under the init lock to dedup
|
|
104
193
|
// concurrent calls for the same key.
|
|
105
194
|
const created = await withXmppClientInitLock(key, async () => {
|
|
195
|
+
const prevGlobal = getGlobalXmppClient();
|
|
196
|
+
if (prevGlobal) {
|
|
197
|
+
try { await prevGlobal.close(); } catch {}
|
|
198
|
+
}
|
|
106
199
|
const newClient = new XmppClient(username, password, settings);
|
|
200
|
+
// Wire up the credentialsProvider so SASL `not-authorized`
|
|
201
|
+
// after idle triggers a refresh chain (jwtLogin →
|
|
202
|
+
// /users/client; userLogin/customLogin/store → /users/my +
|
|
203
|
+
// REST refresh) instead of looping forever with stale creds.
|
|
204
|
+
// Bug #17 — full coverage in 26.5.6.
|
|
205
|
+
newClient.setCredentialsProvider(credentialsProvider);
|
|
107
206
|
setGlobalXmppClient(newClient, key);
|
|
108
207
|
await newClient.waitForOnline().catch((err) => {
|
|
109
208
|
console.error('Error waiting for xmpp online:', err);
|
|
@@ -112,12 +211,37 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config })
|
|
|
112
211
|
return newClient;
|
|
113
212
|
});
|
|
114
213
|
|
|
214
|
+
// Reused clients (steps 1+2 above) also need the provider — config
|
|
215
|
+
// may have changed since the singleton was created.
|
|
216
|
+
created.setCredentialsProvider(credentialsProvider);
|
|
217
|
+
|
|
218
|
+
// Re-join MUC rooms on every 'online'. After a reconnect the new
|
|
219
|
+
// XMPP stream isn't a member of any room, so a sent message gets a
|
|
220
|
+
// local double-tick but never reaches the room (bug #21). Re-sending
|
|
221
|
+
// presence on reconnect fixes delivery. On the very first connect
|
|
222
|
+
// the room list isn't loaded yet so this joins nothing — the
|
|
223
|
+
// bootstrap's own allRoomPresences handles that pass.
|
|
224
|
+
created.setOnOnline(() => {
|
|
225
|
+
const underlying = (created as any).client;
|
|
226
|
+
if (!underlying) {return;}
|
|
227
|
+
allRoomPresences(underlying).catch((e) =>
|
|
228
|
+
devPushLog('warn', 'reconnect: allRoomPresences re-join failed', e)
|
|
229
|
+
);
|
|
230
|
+
// Also refresh the private store so unread / lastViewed markers
|
|
231
|
+
// are accurate after a long reconnect — the MUC re-join above only
|
|
232
|
+
// restores delivery, not unread state. Idempotent on first connect.
|
|
233
|
+
created
|
|
234
|
+
.getChatsPrivateStoreRequestStanza()
|
|
235
|
+
.catch((e: unknown) =>
|
|
236
|
+
devPushLog('warn', 'reconnect: privateStore refresh failed', e)
|
|
237
|
+
);
|
|
238
|
+
});
|
|
239
|
+
|
|
115
240
|
setClient(created);
|
|
116
241
|
lastCredsRef.current = { username, password, settings };
|
|
117
|
-
setReconnectAttempts(0);
|
|
118
242
|
return created;
|
|
119
243
|
},
|
|
120
|
-
[client, config?.xmppSettings]
|
|
244
|
+
[client, config?.xmppSettings, credentialsProvider]
|
|
121
245
|
);
|
|
122
246
|
|
|
123
247
|
// -----------------------------------------------------------
|
|
@@ -131,6 +255,30 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config })
|
|
|
131
255
|
|
|
132
256
|
const abortController = new AbortController();
|
|
133
257
|
let cancelled = false;
|
|
258
|
+
// Auto-retry a TRANSIENT bootstrap failure a few times with backoff
|
|
259
|
+
// before surfacing the error modal — a flaky network at launch should
|
|
260
|
+
// self-heal, not dead-end the user on a Retry button. (Bad-creds
|
|
261
|
+
// failures resolve to null below and fail fast — they never reach the
|
|
262
|
+
// catch, so they aren't retried.)
|
|
263
|
+
const MAX_BOOTSTRAP_ATTEMPTS = 3;
|
|
264
|
+
let bootstrapAttempt = 0;
|
|
265
|
+
// Overall time budget: if we never reach 'ready' (a step hangs), abort
|
|
266
|
+
// the in-flight work and fail so the loader can't spin forever.
|
|
267
|
+
const BOOTSTRAP_BUDGET_MS = 45000;
|
|
268
|
+
let reachedReady = false;
|
|
269
|
+
let budgetTimer: ReturnType<typeof setTimeout> | null = setTimeout(() => {
|
|
270
|
+
if (cancelled || reachedReady) {return;}
|
|
271
|
+
devPushLog('warn', 'initBeforeLoad: time budget exceeded → failed');
|
|
272
|
+
abortController.abort();
|
|
273
|
+
inflightBootstrapKeyRef.current = '';
|
|
274
|
+
setProviderBootstrapStatus('failed');
|
|
275
|
+
}, BOOTSTRAP_BUDGET_MS);
|
|
276
|
+
const clearBudget = () => {
|
|
277
|
+
if (budgetTimer) {
|
|
278
|
+
clearTimeout(budgetTimer);
|
|
279
|
+
budgetTimer = null;
|
|
280
|
+
}
|
|
281
|
+
};
|
|
134
282
|
|
|
135
283
|
const run = async () => {
|
|
136
284
|
// Stable bootstrap key — re-run only when these change.
|
|
@@ -244,6 +392,8 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config })
|
|
|
244
392
|
store.dispatch(setStoreClient(c));
|
|
245
393
|
completedBootstrapKeyRef.current = key;
|
|
246
394
|
inflightBootstrapKeyRef.current = '';
|
|
395
|
+
reachedReady = true;
|
|
396
|
+
clearBudget();
|
|
247
397
|
setProviderBootstrapStatus('ready');
|
|
248
398
|
devPushLog('rn', 'initBeforeLoad: READY');
|
|
249
399
|
|
|
@@ -264,13 +414,34 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config })
|
|
|
264
414
|
roomLimit: qos?.preloadTopKRooms,
|
|
265
415
|
}).catch((err) => console.warn('History preload scheduler failed', err));
|
|
266
416
|
} catch (error: any) {
|
|
267
|
-
if (cancelled) {return;}
|
|
268
|
-
|
|
417
|
+
if (cancelled || abortController.signal.aborted) {return;}
|
|
418
|
+
bootstrapAttempt += 1;
|
|
419
|
+
// Throws here are transient (network / XMPP connect). A bad-creds
|
|
420
|
+
// failure resolves to null above and fails fast without throwing,
|
|
421
|
+
// so it never reaches this catch. Retry with backoff before the
|
|
422
|
+
// modal; release the in-flight key so the recursive run() isn't
|
|
423
|
+
// short-circuited by its own guard.
|
|
424
|
+
if (bootstrapAttempt < MAX_BOOTSTRAP_ATTEMPTS) {
|
|
425
|
+
const backoff = Math.min(8000, 1000 * 2 ** (bootstrapAttempt - 1));
|
|
426
|
+
devPushLog(
|
|
427
|
+
'rn',
|
|
428
|
+
`initBeforeLoad: attempt ${bootstrapAttempt} failed (${error?.message}); retrying in ${backoff}ms`
|
|
429
|
+
);
|
|
430
|
+
inflightBootstrapKeyRef.current = '';
|
|
431
|
+
setTimeout(() => {
|
|
432
|
+
if (!cancelled && !abortController.signal.aborted) {
|
|
433
|
+
run();
|
|
434
|
+
}
|
|
435
|
+
}, backoff);
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
devPushLog('error', 'initBeforeLoad: bootstrap threw (giving up)', {
|
|
269
439
|
message: error?.message,
|
|
270
440
|
status: error?.response?.status,
|
|
271
441
|
data: error?.response?.data,
|
|
272
442
|
});
|
|
273
443
|
inflightBootstrapKeyRef.current = '';
|
|
444
|
+
clearBudget();
|
|
274
445
|
setProviderBootstrapStatus('failed');
|
|
275
446
|
}
|
|
276
447
|
};
|
|
@@ -279,6 +450,7 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config })
|
|
|
279
450
|
|
|
280
451
|
return () => {
|
|
281
452
|
cancelled = true;
|
|
453
|
+
clearBudget();
|
|
282
454
|
abortController.abort();
|
|
283
455
|
// Re-running this effect (deps changed or unmount) must release the
|
|
284
456
|
// in-flight bootstrap key so the next render isn't permanently
|
|
@@ -308,29 +480,30 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config })
|
|
|
308
480
|
]);
|
|
309
481
|
|
|
310
482
|
// -----------------------------------------------------------
|
|
311
|
-
// Reconnect
|
|
483
|
+
// Reconnect watchdog. The client now owns reconnection (onDisconnect →
|
|
484
|
+
// scheduleReconnect with uncapped, clamped backoff; a connect 'error'
|
|
485
|
+
// reschedules too). This is the single provider-side safety net for the
|
|
486
|
+
// states the event-driven triggers miss: a stuck 'connecting', a connect
|
|
487
|
+
// 'error', or a server-side outage where NetInfo never reported the
|
|
488
|
+
// device offline. While foregrounded, if we're not provably online and
|
|
489
|
+
// not suppressed, force a reconnect (debounced inside forceReconnect).
|
|
490
|
+
//
|
|
491
|
+
// Replaces the old status-keyed reconnect effect, which keyed on
|
|
492
|
+
// `client.status` — a mutated class field React does NOT track — so on a
|
|
493
|
+
// silent socket drop it frequently never re-ran (and its 3-attempts-then-
|
|
494
|
+
// full-reinit path was effectively dead).
|
|
312
495
|
// -----------------------------------------------------------
|
|
313
496
|
useEffect(() => {
|
|
314
497
|
if (!client) {return;}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
// After 3 failed reconnects, drop the client and re-init from scratch.
|
|
325
|
-
if (lastCredsRef.current) {
|
|
326
|
-
const { username, password, settings } = lastCredsRef.current;
|
|
327
|
-
setGlobalXmppClient(null);
|
|
328
|
-
setClient(null);
|
|
329
|
-
initializeClient(username, password, settings).catch((err) =>
|
|
330
|
-
console.error('Full reinit failed:', err)
|
|
331
|
-
);
|
|
332
|
-
}
|
|
333
|
-
}, [client, client?.status, reconnectAttempts, initializeClient]);
|
|
498
|
+
const id = setInterval(() => {
|
|
499
|
+
if (AppState.currentState !== 'active') {return;}
|
|
500
|
+
if (client.status !== 'online' && !client.suppressReconnect) {
|
|
501
|
+
devPushLog('rn', `watchdog: client ${client.status} → forceReconnect`);
|
|
502
|
+
client.forceReconnect();
|
|
503
|
+
}
|
|
504
|
+
}, 30000);
|
|
505
|
+
return () => clearInterval(id);
|
|
506
|
+
}, [client]);
|
|
334
507
|
|
|
335
508
|
// -----------------------------------------------------------
|
|
336
509
|
// AppState → background: flush lastViewedTimestamp into the
|
|
@@ -345,22 +518,143 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config })
|
|
|
345
518
|
// entry, or when this build has `disableLastRead`.
|
|
346
519
|
// -----------------------------------------------------------
|
|
347
520
|
useEffect(() => {
|
|
521
|
+
// Remembers the room that was visible when we backgrounded so it can
|
|
522
|
+
// be re-marked visible on return. Lives in the effect closure (one per
|
|
523
|
+
// client) — persists across the listener's many invocations.
|
|
524
|
+
let visibleBeforeBackground: string | null = null;
|
|
348
525
|
const sub = AppState.addEventListener('change', (next) => {
|
|
349
|
-
if (next === 'active') {return;}
|
|
350
526
|
const c = client;
|
|
527
|
+
if (next === 'active') {
|
|
528
|
+
// Returned to foreground. If the socket died while backgrounded
|
|
529
|
+
// (iOS suspends the WebSocket; long background kills it), the
|
|
530
|
+
// status may still read 'online' until xmpp.js notices the dead
|
|
531
|
+
// TCP — which can take a full timeout. Force an immediate
|
|
532
|
+
// reconnect when we're not provably online so cached UI gets
|
|
533
|
+
// live again fast instead of "100 years".
|
|
534
|
+
// Only force a reconnect from a genuinely dead state. Don't
|
|
535
|
+
// interrupt an in-progress 'connecting' (iOS delivers spurious
|
|
536
|
+
// inactive→active flaps for control-center / notifications /
|
|
537
|
+
// biometric prompts that would otherwise restart a healthy
|
|
538
|
+
// connect).
|
|
539
|
+
if (c && (c.status === 'offline' || c.status === 'error')) {
|
|
540
|
+
devPushLog('rn', `AppState active: client ${c.status} → forceReconnect`);
|
|
541
|
+
c.forceReconnect();
|
|
542
|
+
}
|
|
543
|
+
// Re-mark the previously-open room visible so its unread clears
|
|
544
|
+
// now that the user is looking again. We restore ONLY what was
|
|
545
|
+
// visible at background time, so a chat the consumer had already
|
|
546
|
+
// blurred (e.g. on a non-chat tab/route) correctly stays cleared.
|
|
547
|
+
if (visibleBeforeBackground) {
|
|
548
|
+
store.dispatch(setVisibleRoom({ roomJID: visibleBeforeBackground }));
|
|
549
|
+
visibleBeforeBackground = null;
|
|
550
|
+
}
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
// Going to background/inactive (sleep / app switcher / tray):
|
|
351
554
|
if (!c) {return;}
|
|
352
555
|
const state = store.getState();
|
|
353
556
|
const rooms = state.rooms?.rooms;
|
|
354
|
-
const
|
|
557
|
+
const visibleRoomJID = state.rooms?.visibleRoomJID || null;
|
|
558
|
+
// Mark the open room "not visible" while backgrounded so messages
|
|
559
|
+
// that arrive (or MAM-replay on reconnect) count as unread instead
|
|
560
|
+
// of being silently treated as read — the "mounted == visible ==
|
|
561
|
+
// read" gap. Stamp lastViewed=now as the read baseline, clear
|
|
562
|
+
// visibility, then flush that marker to the server private store.
|
|
563
|
+
// Restored on the next 'active' transition above. Handled here in
|
|
564
|
+
// the provider so consumers using the component as a package get
|
|
565
|
+
// correct unread without reaching into the chat store themselves.
|
|
566
|
+
visibleBeforeBackground = visibleRoomJID;
|
|
567
|
+
if (visibleRoomJID) {
|
|
568
|
+
store.dispatch(
|
|
569
|
+
setLastViewedTimestamp({ chatJID: visibleRoomJID, timestamp: Date.now() })
|
|
570
|
+
);
|
|
571
|
+
// Drop the "New messages" divider for the room we're leaving so it
|
|
572
|
+
// doesn't linger when the user comes back (mirrors ChatRoom's
|
|
573
|
+
// unmount cleanup, which never runs while the pane stays mounted).
|
|
574
|
+
store.dispatch(
|
|
575
|
+
deleteRoomMessage({ roomJID: visibleRoomJID, messageId: 'delimiter-new' })
|
|
576
|
+
);
|
|
577
|
+
store.dispatch(clearVisibleRoom());
|
|
578
|
+
}
|
|
355
579
|
// Fire-and-forget — we're going to the background and don't
|
|
356
580
|
// care about the resolution path.
|
|
357
|
-
c.flushLastViewedToPrivateStoreStanza(rooms, {
|
|
581
|
+
c.flushLastViewedToPrivateStoreStanza(rooms, { visibleRoomJID }).catch(
|
|
358
582
|
() => {}
|
|
359
583
|
);
|
|
360
584
|
});
|
|
361
585
|
return () => sub.remove();
|
|
362
586
|
}, [client]);
|
|
363
587
|
|
|
588
|
+
// -----------------------------------------------------------
|
|
589
|
+
// Consumer visibility signal (`isVisible` prop). For hosts that keep
|
|
590
|
+
// <Chat> MOUNTED while hidden (tab/route navigators — so the XMPP
|
|
591
|
+
// connection survives tab switches), ChatRoom's mount/unmount can't tell
|
|
592
|
+
// "shown" from "hidden", so the unread middleware would keep the open
|
|
593
|
+
// room's badge at 0 and messages arriving while you're elsewhere look
|
|
594
|
+
// already-read. When the host passes `isVisible`, mirror the AppState
|
|
595
|
+
// background/foreground handling: hidden → stamp lastViewed=now + clear
|
|
596
|
+
// room visibility (so later messages count) + flush; shown → restore
|
|
597
|
+
// visibility for the open room. Doing it here means consumers never
|
|
598
|
+
// touch the chat store themselves. (Undefined = host unmounts on hide,
|
|
599
|
+
// nothing to do.)
|
|
600
|
+
// -----------------------------------------------------------
|
|
601
|
+
useEffect(() => {
|
|
602
|
+
if (typeof isVisible !== 'boolean') {return;}
|
|
603
|
+
const state = store.getState();
|
|
604
|
+
const rooms = state.rooms?.rooms;
|
|
605
|
+
const activeRoomJID = state.rooms?.activeRoomJID || null;
|
|
606
|
+
if (isVisible) {
|
|
607
|
+
if (activeRoomJID) {
|
|
608
|
+
store.dispatch(setVisibleRoom({ roomJID: activeRoomJID }));
|
|
609
|
+
}
|
|
610
|
+
} else {
|
|
611
|
+
if (activeRoomJID) {
|
|
612
|
+
store.dispatch(
|
|
613
|
+
setLastViewedTimestamp({ chatJID: activeRoomJID, timestamp: Date.now() })
|
|
614
|
+
);
|
|
615
|
+
// Drop the "New messages" divider so it's gone when the user
|
|
616
|
+
// returns to the chat (mirrors ChatRoom's unmount cleanup).
|
|
617
|
+
store.dispatch(
|
|
618
|
+
deleteRoomMessage({ roomJID: activeRoomJID, messageId: 'delimiter-new' })
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
store.dispatch(clearVisibleRoom());
|
|
622
|
+
if (client?.flushLastViewedToPrivateStoreStanza) {
|
|
623
|
+
client
|
|
624
|
+
.flushLastViewedToPrivateStoreStanza(rooms, { visibleRoomJID: activeRoomJID })
|
|
625
|
+
.catch(() => {});
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}, [isVisible, client]);
|
|
629
|
+
|
|
630
|
+
// -----------------------------------------------------------
|
|
631
|
+
// NetInfo → proactive reconnect on network restore. Without this the
|
|
632
|
+
// only reconnect trigger is xmpp.js noticing the socket is dead, which
|
|
633
|
+
// on a Wi-Fi/cellular drop can hang on the TCP timeout for tens of
|
|
634
|
+
// seconds. NetInfo tells us the instant connectivity returns so we can
|
|
635
|
+
// force a reconnect immediately (and reset the backoff budget).
|
|
636
|
+
// -----------------------------------------------------------
|
|
637
|
+
useEffect(() => {
|
|
638
|
+
if (!client) {return;}
|
|
639
|
+
const unsub = NetInfo.addEventListener((state) => {
|
|
640
|
+
// Guard against undefined/partial state (jest mock, early emit).
|
|
641
|
+
const reachable =
|
|
642
|
+
!!state?.isConnected && state?.isInternetReachable !== false;
|
|
643
|
+
if (!reachable) {return;}
|
|
644
|
+
// Reachable. If we're in a dead state, kick a reconnect — do NOT
|
|
645
|
+
// require a prior offline→online transition (a missed/coalesced
|
|
646
|
+
// offline event otherwise left us stuck offline forever). Skip
|
|
647
|
+
// 'connecting' so we don't interrupt an in-progress connect; the
|
|
648
|
+
// watchdog covers a stuck 'connecting'. forceReconnect() debounces
|
|
649
|
+
// bursts and resets the backoff itself.
|
|
650
|
+
if (client.status === 'offline' || client.status === 'error') {
|
|
651
|
+
devPushLog('rn', `NetInfo: reachable & client ${client.status} → forceReconnect`);
|
|
652
|
+
client.forceReconnect();
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
return () => unsub();
|
|
656
|
+
}, [client]);
|
|
657
|
+
|
|
364
658
|
// -----------------------------------------------------------
|
|
365
659
|
// Retry-bootstrap listener — fired by ChatWrapper's error modal so
|
|
366
660
|
// a transient failure (bad endpoint, server hiccup, etc.) can be
|
|
@@ -391,7 +685,6 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config })
|
|
|
391
685
|
setClient(null);
|
|
392
686
|
completedBootstrapKeyRef.current = '';
|
|
393
687
|
inflightBootstrapKeyRef.current = '';
|
|
394
|
-
setReconnectAttempts(0);
|
|
395
688
|
setProviderBootstrapStatus('idle');
|
|
396
689
|
await clearPersistedState();
|
|
397
690
|
});
|
|
@@ -418,6 +711,18 @@ export const useXmppClient = () => {
|
|
|
418
711
|
if (!context) {
|
|
419
712
|
throw new Error('useXmppClient must be used within an XmppProvider');
|
|
420
713
|
}
|
|
714
|
+
if (!context.client) {
|
|
715
|
+
const candidates: Array<XmppClient | null | undefined> = [
|
|
716
|
+
getGlobalXmppClient(),
|
|
717
|
+
store.getState()?.chatSettingStore?.client as XmppClient | undefined,
|
|
718
|
+
];
|
|
719
|
+
const live = candidates.find(
|
|
720
|
+
(c) => c && (c as any).status === 'online'
|
|
721
|
+
);
|
|
722
|
+
if (live) {
|
|
723
|
+
return { ...context, client: live };
|
|
724
|
+
}
|
|
725
|
+
}
|
|
421
726
|
return context;
|
|
422
727
|
};
|
|
423
728
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { AppDispatch } from '../roomStore';
|
|
4
4
|
import { setCurrentRoom } from '../roomStore/roomsSlice';
|
|
5
|
+
import { normalizeRoomJid } from './normalizeRoomJid';
|
|
5
6
|
|
|
6
7
|
interface XmppConfig {
|
|
7
8
|
xmppSettings?: {
|
|
@@ -25,15 +26,8 @@ export const chatAutoEnterer = ({
|
|
|
25
26
|
console.log('🟡 chatAutoEnterer: Called', { roomJID, wasAutoSelected });
|
|
26
27
|
|
|
27
28
|
if (roomJID) {
|
|
28
|
-
// If roomJID is already a full JID, use it directly
|
|
29
|
-
// Otherwise, construct it from chatId if needed
|
|
30
29
|
const conferenceDomain = config.xmppSettings?.conference ?? '';
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (!roomJID.includes('@') && conferenceDomain) {
|
|
34
|
-
finalRoomJID = `${roomJID}@${conferenceDomain}`;
|
|
35
|
-
console.log('🟡 chatAutoEnterer: Constructed roomJID', finalRoomJID);
|
|
36
|
-
}
|
|
30
|
+
const finalRoomJID = normalizeRoomJid(roomJID, conferenceDomain);
|
|
37
31
|
|
|
38
32
|
console.log('🟡 chatAutoEnterer: Setting current room', finalRoomJID);
|
|
39
33
|
dispatch(setCurrentRoom({ roomJID: finalRoomJID }));
|
|
@@ -25,7 +25,10 @@ interface QueueItem {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const DEFAULT_CONCURRENCY = 3;
|
|
28
|
-
|
|
28
|
+
// Fetch the most recent ~20 on re-entry (mirrors web). A larger first page
|
|
29
|
+
// also means more id-overlap with cache, so the merge in
|
|
30
|
+
// `mergeHistoryIntoCache` rarely has to fall back to clear-and-replace.
|
|
31
|
+
const DEFAULT_PAGE_SIZE = 20;
|
|
29
32
|
const DEFAULT_RETRY_LIMIT = 2;
|
|
30
33
|
|
|
31
34
|
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -29,10 +29,21 @@ export function insertMessageWithDelimiter(
|
|
|
29
29
|
);
|
|
30
30
|
|
|
31
31
|
if (existingIndex !== -1) {
|
|
32
|
-
roomMessages[existingIndex]
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
)
|
|
32
|
+
const existing = roomMessages[existingIndex];
|
|
33
|
+
// Preserve the sender identity captured on first insert. The MUC echo
|
|
34
|
+
// parses user.id from the stanza's `from`/senderJID (the room nick or
|
|
35
|
+
// bare-jid local part), which often differs from the optimistic
|
|
36
|
+
// sender id (xmppUsername / walletAddress) set when WE sent it.
|
|
37
|
+
// deepMerge would overwrite it, flipping an own message to the "other"
|
|
38
|
+
// side (rendered on the left, "as if someone else wrote it"). Keep the
|
|
39
|
+
// original id so ownership (user.id === self) stays correct.
|
|
40
|
+
const preservedUserId =
|
|
41
|
+
(existing as any)?.user?.id || (message as any)?.user?.id;
|
|
42
|
+
const merged = deepMerge({ ...existing }, { ...message, pending: false });
|
|
43
|
+
if (merged.user && preservedUserId) {
|
|
44
|
+
merged.user.id = preservedUserId;
|
|
45
|
+
}
|
|
46
|
+
roomMessages[existingIndex] = merged;
|
|
36
47
|
return;
|
|
37
48
|
}
|
|
38
49
|
|
|
@@ -60,25 +71,21 @@ export function insertMessageWithDelimiter(
|
|
|
60
71
|
isDateAfter(msg.date?.toString() ?? '', lastViewedTimestamp.toString())
|
|
61
72
|
);
|
|
62
73
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
74
|
+
if (delimiterIndex !== -1) {
|
|
75
|
+
roomMessages.splice(delimiterIndex, 0, {
|
|
76
|
+
id: 'delimiter-new',
|
|
77
|
+
user: {
|
|
67
78
|
id: 'system',
|
|
68
79
|
name: undefined,
|
|
69
80
|
token: '',
|
|
70
81
|
refreshToken: '',
|
|
71
82
|
},
|
|
72
83
|
date: new Date().toString(),
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (lastViewedTimestamp.toString() === '0') {
|
|
78
|
-
roomMessages.splice(delimiterIndex, 1);
|
|
84
|
+
body: 'New Messages',
|
|
85
|
+
roomJid: '',
|
|
86
|
+
});
|
|
79
87
|
}
|
|
80
88
|
}
|
|
81
|
-
}
|
|
82
89
|
} else if (
|
|
83
90
|
isDateBefore(newMessageDate.toString(), firstMessage?.date?.toString() ?? '')
|
|
84
91
|
) {
|