@ethora/chat-component-rn 26.5.7 → 26.5.11
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 +191 -0
- package/README.md +95 -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 +31 -16
- package/lib/commonjs/components/MainComponents/ChatHeader.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ChatRoom.js +78 -38
- package/lib/commonjs/components/MainComponents/ChatRoom.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ChatWrapper.js +74 -27
- package/lib/commonjs/components/MainComponents/ChatWrapper.js.map +1 -1
- package/lib/commonjs/components/MainComponents/MediaMessage.js +16 -11
- 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 +165 -28
- 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/InteractionsOverlay.js +119 -0
- package/lib/commonjs/components/MessageBubble/InteractionsOverlay.js.map +1 -0
- package/lib/commonjs/components/MessageBubble/Message.js +122 -41
- package/lib/commonjs/components/MessageBubble/Message.js.map +1 -1
- package/lib/commonjs/components/MessageBubble/MessageInteractions.js +132 -44
- package/lib/commonjs/components/MessageBubble/MessageInteractions.js.map +1 -1
- package/lib/commonjs/components/Modals/AttachSheet/AttachSheet.js +155 -52
- package/lib/commonjs/components/Modals/AttachSheet/AttachSheet.js.map +1 -1
- package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js +20 -5
- package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.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 +144 -24
- 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/ModalHeaderComponent.js +10 -0
- package/lib/commonjs/components/Modals/ModalHeaderComponent.js.map +1 -1
- package/lib/commonjs/components/Modals/ModalWrapper/ModalWrapper.js +77 -47
- 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 +17 -13
- package/lib/commonjs/components/Modals/styledModalComponents.js.map +1 -1
- package/lib/commonjs/components/styled/MediaFilePreview.js +47 -28
- package/lib/commonjs/components/styled/MediaFilePreview.js.map +1 -1
- package/lib/commonjs/components/styled/MessageImage.js +49 -9
- 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 +49 -27
- package/lib/commonjs/components/styled/SendInput.js.map +1 -1
- package/lib/commonjs/components/styled/StyledComponents.js +6 -3
- package/lib/commonjs/components/styled/StyledComponents.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 +81 -16
- 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 +244 -32
- 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 +56 -0
- package/lib/commonjs/helpers/mimeToExtension.js.map +1 -1
- package/lib/commonjs/helpers/normalizeRoomJid.js +20 -0
- package/lib/commonjs/helpers/normalizeRoomJid.js.map +1 -0
- package/lib/commonjs/hooks/useChatRoomFocus.js +87 -0
- package/lib/commonjs/hooks/useChatRoomFocus.js.map +1 -0
- package/lib/commonjs/hooks/useHeapSender.js +1 -1
- package/lib/commonjs/hooks/useHeapSender.js.map +1 -1
- package/lib/commonjs/hooks/useLogout.js +36 -11
- package/lib/commonjs/hooks/useLogout.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 +201 -10
- 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 +67 -0
- package/lib/commonjs/networking/api-requests/auth.api.js.map +1 -1
- package/lib/commonjs/networking/api-requests/rooms.api.js +29 -4
- package/lib/commonjs/networking/api-requests/rooms.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 +294 -35
- 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/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 +31 -16
- package/lib/module/components/MainComponents/ChatHeader.js.map +1 -1
- package/lib/module/components/MainComponents/ChatRoom.js +80 -40
- package/lib/module/components/MainComponents/ChatRoom.js.map +1 -1
- package/lib/module/components/MainComponents/ChatWrapper.js +74 -27
- package/lib/module/components/MainComponents/ChatWrapper.js.map +1 -1
- package/lib/module/components/MainComponents/MediaMessage.js +17 -12
- 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 +166 -29
- 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/InteractionsOverlay.js +112 -0
- package/lib/module/components/MessageBubble/InteractionsOverlay.js.map +1 -0
- package/lib/module/components/MessageBubble/Message.js +123 -42
- package/lib/module/components/MessageBubble/Message.js.map +1 -1
- package/lib/module/components/MessageBubble/MessageInteractions.js +134 -46
- package/lib/module/components/MessageBubble/MessageInteractions.js.map +1 -1
- package/lib/module/components/Modals/AttachSheet/AttachSheet.js +155 -52
- package/lib/module/components/Modals/AttachSheet/AttachSheet.js.map +1 -1
- package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js +20 -5
- package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.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 +148 -26
- 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/ModalHeaderComponent.js +10 -0
- package/lib/module/components/Modals/ModalHeaderComponent.js.map +1 -1
- package/lib/module/components/Modals/ModalWrapper/ModalWrapper.js +79 -49
- 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 +17 -13
- package/lib/module/components/Modals/styledModalComponents.js.map +1 -1
- package/lib/module/components/styled/MediaFilePreview.js +47 -28
- package/lib/module/components/styled/MediaFilePreview.js.map +1 -1
- package/lib/module/components/styled/MessageImage.js +51 -11
- 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 +50 -28
- package/lib/module/components/styled/SendInput.js.map +1 -1
- package/lib/module/components/styled/StyledComponents.js +6 -3
- package/lib/module/components/styled/StyledComponents.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 +82 -17
- 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 +246 -34
- 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 +55 -0
- package/lib/module/helpers/mimeToExtension.js.map +1 -1
- package/lib/module/helpers/normalizeRoomJid.js +15 -0
- package/lib/module/helpers/normalizeRoomJid.js.map +1 -0
- package/lib/module/hooks/useChatRoomFocus.js +82 -0
- package/lib/module/hooks/useChatRoomFocus.js.map +1 -0
- package/lib/module/hooks/useHeapSender.js +1 -1
- package/lib/module/hooks/useHeapSender.js.map +1 -1
- package/lib/module/hooks/useLogout.js +36 -11
- package/lib/module/hooks/useLogout.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 +203 -12
- 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 +67 -1
- package/lib/module/networking/api-requests/auth.api.js.map +1 -1
- package/lib/module/networking/api-requests/rooms.api.js +29 -4
- package/lib/module/networking/api-requests/rooms.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 +294 -35
- 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/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/InteractionsOverlay.d.ts +13 -0
- package/lib/typescript/components/MessageBubble/InteractionsOverlay.d.ts.map +1 -0
- 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/ChatProfileModal/ChatProfileModal.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/ModalHeaderComponent.d.ts.map +1 -1
- 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/StyledComponents.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 +8 -0
- package/lib/typescript/helpers/mimeToExtension.d.ts.map +1 -1
- package/lib/typescript/helpers/normalizeRoomJid.d.ts +2 -0
- package/lib/typescript/helpers/normalizeRoomJid.d.ts.map +1 -0
- package/lib/typescript/hooks/useChatRoomFocus.d.ts +42 -0
- package/lib/typescript/hooks/useChatRoomFocus.d.ts.map +1 -0
- package/lib/typescript/hooks/useHeapSender.d.ts.map +1 -1
- package/lib/typescript/hooks/useLogout.d.ts +23 -1
- package/lib/typescript/hooks/useLogout.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.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/api-requests/rooms.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 +33 -4
- 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/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 +20 -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 +23 -4
- package/src/assets/icons.tsx +23 -14
- package/src/components/ContextMenu/ContextMenuComponents.tsx +10 -5
- package/src/components/MainComponents/ChatHeader.tsx +46 -15
- package/src/components/MainComponents/ChatRoom.tsx +95 -66
- package/src/components/MainComponents/ChatWrapper.tsx +127 -42
- package/src/components/MainComponents/MediaMessage.tsx +14 -8
- package/src/components/MainComponents/MessageContainer.tsx +4 -2
- package/src/components/MainComponents/MessageList.tsx +175 -28
- package/src/components/MainComponents/ReduxWrapper.tsx +19 -9
- package/src/components/MainComponents/SystemMessage.tsx +38 -26
- package/src/components/MessageBubble/InteractionsOverlay.tsx +121 -0
- package/src/components/MessageBubble/Message.tsx +133 -63
- package/src/components/MessageBubble/MessageInteractions.tsx +146 -58
- package/src/components/Modals/AttachSheet/AttachSheet.tsx +146 -39
- package/src/components/Modals/ChatProfileModal/ChatProfileModal.tsx +25 -3
- package/src/components/Modals/FilePreviewModal/DocumentViewer.tsx +93 -0
- package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +161 -26
- package/src/components/Modals/FilePreviewModal/PdfView.tsx +32 -4
- package/src/components/Modals/Modal/Modal.tsx +4 -1
- package/src/components/Modals/ModalHeaderComponent.tsx +8 -1
- package/src/components/Modals/ModalWrapper/ModalWrapper.tsx +70 -58
- package/src/components/Modals/UserProfileModal/UserProfileModal.tsx +19 -15
- package/src/components/Modals/styledModalComponents.tsx +17 -13
- package/src/components/styled/MediaFilePreview.tsx +46 -26
- package/src/components/styled/MessageImage.tsx +58 -13
- package/src/components/styled/NewMessageLabel.tsx +8 -5
- package/src/components/styled/SendInput.tsx +64 -29
- package/src/components/styled/StyledComponents.ts +6 -3
- 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 +76 -12
- package/src/context/MessageNotificationContext.tsx +8 -8
- package/src/context/xmppProvider.tsx +247 -30
- 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 +64 -0
- package/src/helpers/normalizeRoomJid.ts +6 -0
- package/src/hooks/useChatRoomFocus.ts +95 -0
- package/src/hooks/useHeapSender.tsx +2 -1
- package/src/hooks/useLogout.tsx +36 -13
- package/src/hooks/usePushNotifications.ts +21 -8
- package/src/hooks/useRoomState.tsx +4 -0
- package/src/hooks/useSendMessage.tsx +258 -13
- package/src/hooks/useUnreadMessagesCounter.ts +13 -16
- package/src/main.ts +1 -0
- package/src/networking/api-requests/auth.api.ts +55 -1
- package/src/networking/api-requests/rooms.api.ts +31 -7
- 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 +317 -43
- package/src/roomStore/Middleware/unreadMidlleware.tsx +23 -5
- 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 +31 -0
- package/src/utils/installPromiseRejectionTracker.ts +91 -0
- package/src/utils/resendMessage.ts +2 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/** @format */
|
|
2
2
|
|
|
3
|
-
import React from 'react';
|
|
3
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
4
4
|
import {
|
|
5
|
+
Animated,
|
|
6
|
+
Easing,
|
|
5
7
|
Modal,
|
|
6
8
|
Platform,
|
|
7
9
|
StyleSheet,
|
|
@@ -20,6 +22,11 @@ interface AttachSheetProps {
|
|
|
20
22
|
primaryColor?: string;
|
|
21
23
|
}
|
|
22
24
|
|
|
25
|
+
// Starting translateY for the sheet. Larger than any realistic sheet
|
|
26
|
+
// height so it sits fully below the viewport before opening; the exact
|
|
27
|
+
// number doesn't matter visually because it animates to 0.
|
|
28
|
+
const SHEET_OFFSCREEN = 600;
|
|
29
|
+
|
|
23
30
|
const AttachSheet: React.FC<AttachSheetProps> = ({
|
|
24
31
|
visible,
|
|
25
32
|
onClose,
|
|
@@ -28,9 +35,94 @@ const AttachSheet: React.FC<AttachSheetProps> = ({
|
|
|
28
35
|
onDocument,
|
|
29
36
|
primaryColor = '#0052CD',
|
|
30
37
|
}) => {
|
|
38
|
+
// Pending handler captured on row tap — runs once the EXIT animation
|
|
39
|
+
// finishes so iOS doesn't try to present an image picker over a
|
|
40
|
+
// still-dismissing modal (was the original reason for the
|
|
41
|
+
// platform-split + Modal.onDismiss dance). The animation completion
|
|
42
|
+
// callback now drives this on both platforms.
|
|
43
|
+
const pendingRef = useRef<(() => void) | null>(null);
|
|
44
|
+
|
|
45
|
+
// Local "modal stays mounted while animating out" state. The parent
|
|
46
|
+
// controls `visible`; we mirror it but delay unmount until the exit
|
|
47
|
+
// animation has played so the slide-down is actually seen.
|
|
48
|
+
const [mounted, setMounted] = useState(visible);
|
|
49
|
+
|
|
50
|
+
// Two separate Animated values — the whole point of the rewrite:
|
|
51
|
+
// • backdropOpacity: fades the dim layer in/out (no slide).
|
|
52
|
+
// • sheetTranslateY: slides ONLY the sheet up from below.
|
|
53
|
+
// RN Modal's built-in `animationType="slide"` slides the entire
|
|
54
|
+
// contents (backdrop included) so the dim shade entered from the
|
|
55
|
+
// bottom — what looked "awful". Splitting these gives the standard
|
|
56
|
+
// bottom-sheet feel.
|
|
57
|
+
const backdropOpacity = useRef(new Animated.Value(0)).current;
|
|
58
|
+
const sheetTranslateY = useRef(
|
|
59
|
+
new Animated.Value(SHEET_OFFSCREEN)
|
|
60
|
+
).current;
|
|
61
|
+
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (visible) {
|
|
64
|
+
setMounted(true);
|
|
65
|
+
// Reset to starting positions BEFORE the Modal becomes visible so
|
|
66
|
+
// the first animated frame is below-the-fold (not a flash of the
|
|
67
|
+
// settled sheet).
|
|
68
|
+
backdropOpacity.setValue(0);
|
|
69
|
+
sheetTranslateY.setValue(SHEET_OFFSCREEN);
|
|
70
|
+
// requestAnimationFrame lets the Modal commit its mount before the
|
|
71
|
+
// animation starts; without this the first frame can be skipped.
|
|
72
|
+
const raf = requestAnimationFrame(() => {
|
|
73
|
+
Animated.parallel([
|
|
74
|
+
Animated.timing(backdropOpacity, {
|
|
75
|
+
toValue: 1,
|
|
76
|
+
duration: 220,
|
|
77
|
+
easing: Easing.out(Easing.cubic),
|
|
78
|
+
useNativeDriver: true,
|
|
79
|
+
}),
|
|
80
|
+
Animated.spring(sheetTranslateY, {
|
|
81
|
+
toValue: 0,
|
|
82
|
+
// Tuned for a quick-but-not-bouncy entry. tension is the
|
|
83
|
+
// "stiffness" knob, friction the "damping" knob in RN's
|
|
84
|
+
// spring config.
|
|
85
|
+
tension: 90,
|
|
86
|
+
friction: 14,
|
|
87
|
+
useNativeDriver: true,
|
|
88
|
+
}),
|
|
89
|
+
]).start();
|
|
90
|
+
});
|
|
91
|
+
return () => cancelAnimationFrame(raf);
|
|
92
|
+
}
|
|
93
|
+
if (!mounted) {return;}
|
|
94
|
+
// Exit: fade the backdrop, slide the sheet down, then unmount and
|
|
95
|
+
// fire any pending picker handler.
|
|
96
|
+
Animated.parallel([
|
|
97
|
+
Animated.timing(backdropOpacity, {
|
|
98
|
+
toValue: 0,
|
|
99
|
+
duration: 180,
|
|
100
|
+
easing: Easing.in(Easing.cubic),
|
|
101
|
+
useNativeDriver: true,
|
|
102
|
+
}),
|
|
103
|
+
Animated.timing(sheetTranslateY, {
|
|
104
|
+
toValue: SHEET_OFFSCREEN,
|
|
105
|
+
duration: 220,
|
|
106
|
+
easing: Easing.in(Easing.cubic),
|
|
107
|
+
useNativeDriver: true,
|
|
108
|
+
}),
|
|
109
|
+
]).start(({ finished }) => {
|
|
110
|
+
if (!finished) {return;} // interrupted (rapid re-open) → leave state alone
|
|
111
|
+
setMounted(false);
|
|
112
|
+
const fn = pendingRef.current;
|
|
113
|
+
pendingRef.current = null;
|
|
114
|
+
fn?.();
|
|
115
|
+
});
|
|
116
|
+
return undefined;
|
|
117
|
+
}, [visible]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
118
|
+
|
|
31
119
|
const trigger = (handler: () => void) => () => {
|
|
120
|
+
// Capture the handler and ask parent to close. The exit-animation
|
|
121
|
+
// completion above fires `handler` AFTER the sheet has slid away,
|
|
122
|
+
// so iOS no longer tries to present a picker over a dismissing
|
|
123
|
+
// modal — and Android no longer needs the ad-hoc 150ms setTimeout.
|
|
124
|
+
pendingRef.current = handler;
|
|
32
125
|
onClose();
|
|
33
|
-
setTimeout(handler, 150);
|
|
34
126
|
};
|
|
35
127
|
|
|
36
128
|
const rows: {
|
|
@@ -62,51 +154,66 @@ const AttachSheet: React.FC<AttachSheetProps> = ({
|
|
|
62
154
|
return (
|
|
63
155
|
<Modal
|
|
64
156
|
transparent
|
|
65
|
-
visible={
|
|
66
|
-
|
|
157
|
+
visible={mounted}
|
|
158
|
+
// We drive the animation ourselves — disable RN's built-in slide
|
|
159
|
+
// (which slid the backdrop too) so the fade + spring above are the
|
|
160
|
+
// only motion.
|
|
161
|
+
animationType="none"
|
|
67
162
|
onRequestClose={onClose}
|
|
163
|
+
// Hardware-back / iOS gesture also routes through onClose, which
|
|
164
|
+
// flips `visible` and triggers our exit animation above.
|
|
68
165
|
>
|
|
69
|
-
<
|
|
70
|
-
style={styles.backdrop}
|
|
71
|
-
activeOpacity={1}
|
|
72
|
-
onPress={onClose}
|
|
166
|
+
<Animated.View
|
|
167
|
+
style={[styles.backdrop, { opacity: backdropOpacity }]}
|
|
73
168
|
>
|
|
74
|
-
<TouchableOpacity
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
{
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
169
|
+
<TouchableOpacity
|
|
170
|
+
style={StyleSheet.absoluteFill}
|
|
171
|
+
activeOpacity={1}
|
|
172
|
+
onPress={onClose}
|
|
173
|
+
/>
|
|
174
|
+
<Animated.View
|
|
175
|
+
style={[
|
|
176
|
+
styles.sheet,
|
|
177
|
+
{ transform: [{ translateY: sheetTranslateY }] },
|
|
178
|
+
]}
|
|
179
|
+
>
|
|
180
|
+
<TouchableOpacity activeOpacity={1}>
|
|
181
|
+
<View style={styles.grabber} />
|
|
182
|
+
<Text style={styles.title}>Attach</Text>
|
|
183
|
+
{rows.map(({ label, hint, Icon, handler }, idx) => (
|
|
184
|
+
<TouchableOpacity
|
|
185
|
+
key={label}
|
|
186
|
+
activeOpacity={0.6}
|
|
88
187
|
style={[
|
|
89
|
-
styles.
|
|
90
|
-
|
|
188
|
+
styles.row,
|
|
189
|
+
idx < rows.length - 1 && styles.rowDivider,
|
|
91
190
|
]}
|
|
191
|
+
onPress={trigger(handler)}
|
|
92
192
|
>
|
|
93
|
-
<
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
193
|
+
<View
|
|
194
|
+
style={[
|
|
195
|
+
styles.iconBubble,
|
|
196
|
+
{ backgroundColor: primaryColor + '14' },
|
|
197
|
+
]}
|
|
198
|
+
>
|
|
199
|
+
<Icon color={primaryColor} width={20} height={20} />
|
|
200
|
+
</View>
|
|
201
|
+
<View style={styles.rowText}>
|
|
202
|
+
<Text style={styles.rowLabel}>{label}</Text>
|
|
203
|
+
<Text style={styles.rowHint}>{hint}</Text>
|
|
204
|
+
</View>
|
|
205
|
+
</TouchableOpacity>
|
|
206
|
+
))}
|
|
207
|
+
<TouchableOpacity
|
|
208
|
+
activeOpacity={0.6}
|
|
209
|
+
style={styles.cancelRow}
|
|
210
|
+
onPress={onClose}
|
|
211
|
+
>
|
|
212
|
+
<Text style={styles.cancelLabel}>Cancel</Text>
|
|
99
213
|
</TouchableOpacity>
|
|
100
|
-
))}
|
|
101
|
-
<TouchableOpacity
|
|
102
|
-
activeOpacity={0.6}
|
|
103
|
-
style={styles.cancelRow}
|
|
104
|
-
onPress={onClose}
|
|
105
|
-
>
|
|
106
|
-
<Text style={styles.cancelLabel}>Cancel</Text>
|
|
107
214
|
</TouchableOpacity>
|
|
108
|
-
</
|
|
109
|
-
</
|
|
215
|
+
</Animated.View>
|
|
216
|
+
</Animated.View>
|
|
110
217
|
</Modal>
|
|
111
218
|
);
|
|
112
219
|
};
|
|
@@ -293,11 +293,22 @@ const ChatProfileModal: React.FC<ChatProfileModalProps> = ({
|
|
|
293
293
|
<ProfileImagePlaceholder
|
|
294
294
|
name={activeRoom.title || activeRoom.name}
|
|
295
295
|
icon={activeRoom.icon}
|
|
296
|
+
// Two gates control whether the icon is editable:
|
|
297
|
+
// 1. role — only non-participants could edit before.
|
|
298
|
+
// 2. NEW: `disableChatInfo.disableIconEdit` — hard kill from
|
|
299
|
+
// config so a consumer can make the icon read-only for
|
|
300
|
+
// everyone (e.g. branded rooms, patient-facing apps).
|
|
301
|
+
// Both must allow it for the picker/remove affordances to show.
|
|
296
302
|
upload={{
|
|
297
303
|
onUpload,
|
|
298
|
-
active:
|
|
304
|
+
active:
|
|
305
|
+
!config?.disableChatInfo?.disableIconEdit &&
|
|
306
|
+
activeRoom?.role !== 'participant',
|
|
307
|
+
}}
|
|
308
|
+
remove={{
|
|
309
|
+
enabled: !config?.disableChatInfo?.disableIconEdit,
|
|
310
|
+
onRemoveClick,
|
|
299
311
|
}}
|
|
300
|
-
remove={{ enabled: true, onRemoveClick }}
|
|
301
312
|
role={activeRoom?.role}
|
|
302
313
|
size={128}
|
|
303
314
|
/>
|
|
@@ -377,7 +388,18 @@ const ChatProfileModal: React.FC<ChatProfileModalProps> = ({
|
|
|
377
388
|
>
|
|
378
389
|
<Pressable
|
|
379
390
|
style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-start', gap: 16 }}
|
|
380
|
-
|
|
391
|
+
// `disableChatInfo.disableMemberTap` blocks the
|
|
392
|
+
// tap entirely so the user-profile popup never
|
|
393
|
+
// opens. `disableMemberProfileActions` only hid
|
|
394
|
+
// the action buttons INSIDE that popup, leaving
|
|
395
|
+
// the popup itself to open on tap — this gate
|
|
396
|
+
// closes the door. Customer-reported #16.
|
|
397
|
+
onPress={
|
|
398
|
+
config?.disableChatInfo?.disableMemberTap
|
|
399
|
+
? undefined
|
|
400
|
+
: () => handleUserAvatarClick(user)
|
|
401
|
+
}
|
|
402
|
+
disabled={!!config?.disableChatInfo?.disableMemberTap}
|
|
381
403
|
>
|
|
382
404
|
<ProfileImagePlaceholder
|
|
383
405
|
name={`${user.firstName} ${user.lastName}`}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { ActivityIndicator, StyleSheet, Text, View } from 'react-native';
|
|
3
|
+
import { WebView } from 'react-native-webview';
|
|
4
|
+
|
|
5
|
+
interface DocumentViewerProps {
|
|
6
|
+
/** Public CDN URL of the doc. Must be reachable from Google's servers. */
|
|
7
|
+
url: string;
|
|
8
|
+
/** Optional display filename; used for the error fallback only. */
|
|
9
|
+
fileName?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Inline preview for office documents (DOC / DOCX / XLS / XLSX / PPT /
|
|
14
|
+
* PPTX / TXT / RTF / CSV) and other non-PDF formats supported by
|
|
15
|
+
* Google's free gview rendering service.
|
|
16
|
+
*
|
|
17
|
+
* Strategy: embed `https://docs.google.com/gview?url=<encoded>&embedded=true`
|
|
18
|
+
* inside a WebView. Google fetches the doc from its public URL and
|
|
19
|
+
* renders it to a viewable iframe — no client-side dependency on
|
|
20
|
+
* native office libraries, no extra peer dep. Works for any backend
|
|
21
|
+
* whose file URLs are publicly reachable (which Ethora's `/files/`
|
|
22
|
+
* already are — they're CDN URLs returned in `item.location`).
|
|
23
|
+
*
|
|
24
|
+
* Limitations (documented for the integrator):
|
|
25
|
+
* - Requires public URL — won't work for auth-gated downloads.
|
|
26
|
+
* - First load can take 2-5s on slow networks.
|
|
27
|
+
* - Read-only — no editing, no print/share buttons.
|
|
28
|
+
*
|
|
29
|
+
* For PDFs use the existing PdfViewer (downloads then renders locally).
|
|
30
|
+
*/
|
|
31
|
+
const DocumentViewer: React.FC<DocumentViewerProps> = ({ url, fileName }) => {
|
|
32
|
+
const [loaded, setLoaded] = useState(false);
|
|
33
|
+
const [errored, setErrored] = useState(false);
|
|
34
|
+
|
|
35
|
+
const viewerUrl = `https://docs.google.com/gview?url=${encodeURIComponent(
|
|
36
|
+
url
|
|
37
|
+
)}&embedded=true`;
|
|
38
|
+
|
|
39
|
+
if (errored) {
|
|
40
|
+
return (
|
|
41
|
+
<View style={styles.center}>
|
|
42
|
+
<Text style={styles.errorTitle}>
|
|
43
|
+
{fileName || 'Document'}
|
|
44
|
+
</Text>
|
|
45
|
+
<Text style={styles.errorBody}>
|
|
46
|
+
Couldn't render an inline preview. Tap the save icon above to
|
|
47
|
+
download the file.
|
|
48
|
+
</Text>
|
|
49
|
+
</View>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<View style={styles.container}>
|
|
55
|
+
<WebView
|
|
56
|
+
source={{ uri: viewerUrl }}
|
|
57
|
+
style={styles.webview}
|
|
58
|
+
originWhitelist={['*']}
|
|
59
|
+
startInLoadingState
|
|
60
|
+
onLoadEnd={() => setLoaded(true)}
|
|
61
|
+
onError={() => setErrored(true)}
|
|
62
|
+
onHttpError={() => setErrored(true)}
|
|
63
|
+
/>
|
|
64
|
+
{!loaded && (
|
|
65
|
+
<View style={styles.loadingOverlay} pointerEvents="none">
|
|
66
|
+
<ActivityIndicator size="large" />
|
|
67
|
+
</View>
|
|
68
|
+
)}
|
|
69
|
+
</View>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export default DocumentViewer;
|
|
74
|
+
|
|
75
|
+
const styles = StyleSheet.create({
|
|
76
|
+
container: { flex: 1, width: '100%', backgroundColor: '#fff' },
|
|
77
|
+
webview: { flex: 1, backgroundColor: '#fff' },
|
|
78
|
+
loadingOverlay: {
|
|
79
|
+
...StyleSheet.absoluteFillObject,
|
|
80
|
+
justifyContent: 'center',
|
|
81
|
+
alignItems: 'center',
|
|
82
|
+
backgroundColor: '#ffffff',
|
|
83
|
+
},
|
|
84
|
+
center: {
|
|
85
|
+
flex: 1,
|
|
86
|
+
alignItems: 'center',
|
|
87
|
+
justifyContent: 'center',
|
|
88
|
+
padding: 24,
|
|
89
|
+
gap: 8,
|
|
90
|
+
},
|
|
91
|
+
errorTitle: { fontSize: 16, fontWeight: '600' },
|
|
92
|
+
errorBody: { color: '#666', textAlign: 'center' },
|
|
93
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import styled from 'styled-components/native';
|
|
3
3
|
import {
|
|
4
4
|
CenterContainer,
|
|
@@ -11,19 +11,94 @@ import Button from '../../styled/Button';
|
|
|
11
11
|
import { RootState } from '../../../roomStore';
|
|
12
12
|
import { FullScreenImage } from '../../styled/StyledInputComponents/MediaComponents';
|
|
13
13
|
import { setActiveFile } from '../../../roomStore/chatSettingsSlice';
|
|
14
|
-
import {
|
|
15
|
-
|
|
14
|
+
import {
|
|
15
|
+
Alert,
|
|
16
|
+
Text,
|
|
17
|
+
View,
|
|
18
|
+
Platform,
|
|
19
|
+
TouchableOpacity,
|
|
20
|
+
StyleSheet,
|
|
21
|
+
} from 'react-native';
|
|
22
|
+
import { useVideoPlayer, VideoView } from 'expo-video';
|
|
23
|
+
import { PlayIcon } from '../../../assets/icons';
|
|
16
24
|
import * as FileSystem from 'expo-file-system/legacy';
|
|
17
25
|
import * as MediaLibrary from 'expo-media-library';
|
|
18
26
|
import { useToast } from '../../../context/ToastContext';
|
|
19
27
|
import PdfViewer from './PdfView';
|
|
20
|
-
import
|
|
28
|
+
import DocumentViewer from './DocumentViewer';
|
|
29
|
+
import AudioMessage from '../../styled/AudioMessage';
|
|
30
|
+
import { ensureFilenameHasExtension, isLikelyAudio } from '../../../helpers/mimeToExtension';
|
|
31
|
+
|
|
32
|
+
// MIME types Google's gview embed renders reliably. Everything else
|
|
33
|
+
// falls through to the info-card so the user can still download.
|
|
34
|
+
const GVIEW_PREVIEWABLE = new Set<string>([
|
|
35
|
+
'application/msword',
|
|
36
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
37
|
+
'application/vnd.ms-excel',
|
|
38
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
39
|
+
'application/vnd.ms-powerpoint',
|
|
40
|
+
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
41
|
+
'text/plain',
|
|
42
|
+
'text/csv',
|
|
43
|
+
'application/rtf',
|
|
44
|
+
]);
|
|
45
|
+
const isGviewPreviewable = (mime: string | undefined | null) => {
|
|
46
|
+
if (!mime) {return false;}
|
|
47
|
+
return GVIEW_PREVIEWABLE.has(mime.toLowerCase().split(';')[0]!.trim());
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Full-screen video preview on expo-video (expo-av <Video> is deprecated
|
|
51
|
+
// and rendered blank on Android). `contentFit: contain` fits the clip to
|
|
52
|
+
// the area with the surrounding letterbox painted as the view's own
|
|
53
|
+
// (transparent → theme) background, NOT black. `textureView` so the play
|
|
54
|
+
// overlay composites on top and nothing is clipped on Android.
|
|
55
|
+
const ModalVideo: React.FC<{ uri: string }> = ({ uri }) => {
|
|
56
|
+
const [showPlay, setShowPlay] = useState(true);
|
|
21
57
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
58
|
+
const player = useVideoPlayer(uri, (p) => {
|
|
59
|
+
p.muted = false;
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
const sub = player.addListener('playingChange', ({ isPlaying }) => {
|
|
64
|
+
if (isPlaying) {setShowPlay(false);}
|
|
65
|
+
});
|
|
66
|
+
return () => sub.remove();
|
|
67
|
+
}, [player]);
|
|
68
|
+
|
|
69
|
+
const handlePlay = () => {
|
|
70
|
+
setShowPlay(false);
|
|
71
|
+
player.play();
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<View style={{ flex: 1, width: '100%' }}>
|
|
76
|
+
<VideoView
|
|
77
|
+
player={player}
|
|
78
|
+
style={{ flex: 1, backgroundColor: 'transparent' }}
|
|
79
|
+
contentFit="contain"
|
|
80
|
+
nativeControls
|
|
81
|
+
surfaceType="textureView"
|
|
82
|
+
allowsFullscreen
|
|
83
|
+
/>
|
|
84
|
+
{/* Play affordance shown immediately on open; tapping starts
|
|
85
|
+
playback and the native controls take over. */}
|
|
86
|
+
{showPlay && (
|
|
87
|
+
<TouchableOpacity
|
|
88
|
+
activeOpacity={0.8}
|
|
89
|
+
onPress={handlePlay}
|
|
90
|
+
style={StyleSheet.absoluteFillObject}
|
|
91
|
+
>
|
|
92
|
+
<View style={styles.playOverlay}>
|
|
93
|
+
<View style={styles.playButton}>
|
|
94
|
+
<PlayIcon width={28} height={28} />
|
|
95
|
+
</View>
|
|
96
|
+
</View>
|
|
97
|
+
</TouchableOpacity>
|
|
98
|
+
)}
|
|
99
|
+
</View>
|
|
100
|
+
);
|
|
101
|
+
};
|
|
27
102
|
|
|
28
103
|
interface FilePreviewModalProps {
|
|
29
104
|
handleCloseModal: any;
|
|
@@ -39,7 +114,7 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
|
|
|
39
114
|
(state: RootState) => state.chatSettingStore
|
|
40
115
|
);
|
|
41
116
|
|
|
42
|
-
if (!activeFile) {return;}
|
|
117
|
+
if (!activeFile) {return null;}
|
|
43
118
|
|
|
44
119
|
const requestStoragePermission = async () => {
|
|
45
120
|
try {
|
|
@@ -143,30 +218,55 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
|
|
|
143
218
|
handleCloseModal?.();
|
|
144
219
|
};
|
|
145
220
|
|
|
146
|
-
|
|
221
|
+
// NOTE: plain computed value, NOT useMemo. It sits AFTER the
|
|
222
|
+
// `if (!activeFile) return` guard above, so as a hook it would run
|
|
223
|
+
// conditionally (skipped when activeFile is null) — changing the hook
|
|
224
|
+
// count between renders and throwing "rendered more hooks than during
|
|
225
|
+
// the previous render", which crashed the modal the instant you tapped
|
|
226
|
+
// a video/image to open it. Recomputing this on each render is cheap.
|
|
227
|
+
const getMediaComponent: React.ReactNode = (() => {
|
|
147
228
|
switch (true) {
|
|
148
229
|
case activeFile.mimetype.startsWith('image/'):
|
|
149
230
|
return (
|
|
150
231
|
<FullScreenImage
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
232
|
+
source={{
|
|
233
|
+
uri:
|
|
234
|
+
activeFile.fileURL ||
|
|
235
|
+
'https://as2.ftcdn.net/v2/jpg/02/51/95/53/1000_F_251955356_FAQH0U1y1TZw3ZcdPGybwUkH90a3VAhb.jpg',
|
|
236
|
+
}}
|
|
237
|
+
resizeMode="contain"
|
|
238
|
+
accessibilityLabel={activeFile.fileName}
|
|
156
239
|
/>
|
|
157
240
|
);
|
|
158
241
|
case activeFile.mimetype.startsWith('video/'):
|
|
242
|
+
return <ModalVideo uri={activeFile.fileURL} />;
|
|
243
|
+
// Mirrors the MediaMessage heuristic: treat octet-stream voicemails
|
|
244
|
+
// with audio-shaped filenames / URLs as audio so the preview shows
|
|
245
|
+
// the player instead of an "Unsupported" card. Customer-reported
|
|
246
|
+
// #9 voicemail fix — see isLikelyAudio in mimeToExtension.ts.
|
|
247
|
+
case isLikelyAudio(
|
|
248
|
+
activeFile.mimetype,
|
|
249
|
+
activeFile.fileName,
|
|
250
|
+
activeFile.fileURL
|
|
251
|
+
):
|
|
159
252
|
return (
|
|
160
|
-
<
|
|
253
|
+
<View
|
|
161
254
|
style={{
|
|
162
255
|
width: '100%',
|
|
163
|
-
|
|
256
|
+
padding: 20,
|
|
257
|
+
gap: 12,
|
|
258
|
+
backgroundColor: '#FFF8ED',
|
|
259
|
+
borderRadius: 16,
|
|
164
260
|
}}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
261
|
+
>
|
|
262
|
+
<Text style={{ fontSize: 16, fontWeight: '600' }}>
|
|
263
|
+
{ensureFilenameHasExtension(
|
|
264
|
+
activeFile.fileName,
|
|
265
|
+
activeFile.mimetype
|
|
266
|
+
)}
|
|
267
|
+
</Text>
|
|
268
|
+
<AudioMessage src={activeFile.fileURL} />
|
|
269
|
+
</View>
|
|
170
270
|
);
|
|
171
271
|
case activeFile.mimetype === 'application/pdf':
|
|
172
272
|
return <PdfViewer pdfUrl={activeFile.fileURL} />;
|
|
@@ -175,6 +275,20 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
|
|
|
175
275
|
activeFile.fileName,
|
|
176
276
|
activeFile.mimetype
|
|
177
277
|
);
|
|
278
|
+
// Office docs (.docx / .xlsx / .pptx / .doc / .xls / .ppt /
|
|
279
|
+
// .txt / .csv / .rtf) → render inline via Google's gview embed
|
|
280
|
+
// — fixes the "blank preview" complaint for docs (bug #9).
|
|
281
|
+
if (isGviewPreviewable(activeFile.mimetype)) {
|
|
282
|
+
return (
|
|
283
|
+
<DocumentViewer
|
|
284
|
+
url={activeFile.fileURL}
|
|
285
|
+
fileName={displayName}
|
|
286
|
+
/>
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
// True last-resort fallback for genuinely unrenderable types
|
|
290
|
+
// (binary blobs, exotic MIMEs). Friendly info card with the
|
|
291
|
+
// filename so the user can still download.
|
|
178
292
|
return (
|
|
179
293
|
<View
|
|
180
294
|
style={{
|
|
@@ -198,7 +312,7 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
|
|
|
198
312
|
);
|
|
199
313
|
}
|
|
200
314
|
}
|
|
201
|
-
}
|
|
315
|
+
})();
|
|
202
316
|
|
|
203
317
|
return (
|
|
204
318
|
<ModalContainerFullScreen>
|
|
@@ -217,10 +331,15 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
|
|
|
217
331
|
<CenterContainer
|
|
218
332
|
style={{
|
|
219
333
|
display: 'flex',
|
|
220
|
-
|
|
334
|
+
flex: 1,
|
|
221
335
|
justifyContent: 'center',
|
|
222
|
-
overflow:
|
|
336
|
+
// NOTE: do NOT set overflow:'hidden' here — on Android it clips
|
|
337
|
+
// hardware-accelerated children (expo-av Video TextureView and
|
|
338
|
+
// react-native-webview) to nothing, so video/PDF/doc previews
|
|
339
|
+
// render blank while plain <Image> survives.
|
|
340
|
+
overflow: 'visible',
|
|
223
341
|
padding: 16,
|
|
342
|
+
paddingBottom: 48,
|
|
224
343
|
}}
|
|
225
344
|
>
|
|
226
345
|
{getMediaComponent}
|
|
@@ -230,4 +349,20 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
|
|
|
230
349
|
);
|
|
231
350
|
};
|
|
232
351
|
|
|
352
|
+
const styles = StyleSheet.create({
|
|
353
|
+
playOverlay: {
|
|
354
|
+
...StyleSheet.absoluteFillObject,
|
|
355
|
+
justifyContent: 'center',
|
|
356
|
+
alignItems: 'center',
|
|
357
|
+
},
|
|
358
|
+
playButton: {
|
|
359
|
+
width: 64,
|
|
360
|
+
height: 64,
|
|
361
|
+
borderRadius: 32,
|
|
362
|
+
backgroundColor: 'rgba(0, 0, 0, 0.55)',
|
|
363
|
+
justifyContent: 'center',
|
|
364
|
+
alignItems: 'center',
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
|
|
233
368
|
export default FilePreviewModal;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useEffect, useState } from 'react';
|
|
2
|
-
import { ActivityIndicator, StyleSheet, Text, View } from 'react-native';
|
|
2
|
+
import { ActivityIndicator, Platform, StyleSheet, Text, View } from 'react-native';
|
|
3
3
|
import { WebView } from 'react-native-webview';
|
|
4
4
|
import * as FileSystem from 'expo-file-system/legacy';
|
|
5
5
|
|
|
@@ -10,13 +10,20 @@ interface PdfViewerProps {
|
|
|
10
10
|
const PdfViewer: React.FC<PdfViewerProps> = ({ pdfUrl }) => {
|
|
11
11
|
const [localUri, setLocalUri] = useState<string | null>(null);
|
|
12
12
|
const [error, setError] = useState<string | null>(null);
|
|
13
|
+
// Android's WebView has NO native PDF renderer, so a file:// (or direct)
|
|
14
|
+
// PDF loads blank. Route it through Google's gview embed using the
|
|
15
|
+
// (public) remote URL — same approach as DocumentViewer for office docs.
|
|
16
|
+
// iOS WKWebView renders PDFs natively, so we download + show the file.
|
|
17
|
+
const isAndroid = Platform.OS === 'android';
|
|
13
18
|
|
|
14
19
|
useEffect(() => {
|
|
20
|
+
if (isAndroid) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
15
23
|
let cancelled = false;
|
|
16
24
|
const run = async () => {
|
|
17
25
|
try {
|
|
18
|
-
const target =
|
|
19
|
-
FileSystem.cacheDirectory + `pdf-${Date.now()}.pdf`;
|
|
26
|
+
const target = FileSystem.cacheDirectory + `pdf-${Date.now()}.pdf`;
|
|
20
27
|
const result = await FileSystem.downloadAsync(pdfUrl, target);
|
|
21
28
|
if (cancelled) {return;}
|
|
22
29
|
setLocalUri(result.uri);
|
|
@@ -30,7 +37,28 @@ const PdfViewer: React.FC<PdfViewerProps> = ({ pdfUrl }) => {
|
|
|
30
37
|
return () => {
|
|
31
38
|
cancelled = true;
|
|
32
39
|
};
|
|
33
|
-
}, [pdfUrl]);
|
|
40
|
+
}, [pdfUrl, isAndroid]);
|
|
41
|
+
|
|
42
|
+
if (isAndroid) {
|
|
43
|
+
const gviewUrl = `https://docs.google.com/gview?embedded=true&url=${encodeURIComponent(
|
|
44
|
+
pdfUrl
|
|
45
|
+
)}`;
|
|
46
|
+
return (
|
|
47
|
+
<View style={styles.container}>
|
|
48
|
+
<WebView
|
|
49
|
+
source={{ uri: gviewUrl }}
|
|
50
|
+
style={styles.webview}
|
|
51
|
+
originWhitelist={['*']}
|
|
52
|
+
startInLoadingState
|
|
53
|
+
renderLoading={() => (
|
|
54
|
+
<View style={styles.center}>
|
|
55
|
+
<ActivityIndicator size="large" />
|
|
56
|
+
</View>
|
|
57
|
+
)}
|
|
58
|
+
/>
|
|
59
|
+
</View>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
34
62
|
|
|
35
63
|
if (error) {
|
|
36
64
|
return (
|
|
@@ -39,7 +39,10 @@ const Modal: React.FC<ModalProps> = ({ children, modal, setOpenModal }) => {
|
|
|
39
39
|
};
|
|
40
40
|
return (
|
|
41
41
|
modal && (
|
|
42
|
-
<ModalBackground
|
|
42
|
+
<ModalBackground
|
|
43
|
+
id="modal-background"
|
|
44
|
+
style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }}
|
|
45
|
+
>
|
|
43
46
|
{renderModalContent()}
|
|
44
47
|
{children}
|
|
45
48
|
</ModalBackground>
|