@ethora/chat-component-rn 26.8.5 → 26.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -0
- package/lib/commonjs/assets/emojiData.js +40 -0
- package/lib/commonjs/assets/emojiData.js.map +1 -0
- package/lib/commonjs/assets/icons.js +1 -1
- package/lib/commonjs/assets/icons.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ChatHeader.js +7 -9
- package/lib/commonjs/components/MainComponents/ChatHeader.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ChatRoom.js +24 -23
- package/lib/commonjs/components/MainComponents/ChatRoom.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ChatWrapper.js +22 -16
- package/lib/commonjs/components/MainComponents/ChatWrapper.js.map +1 -1
- package/lib/commonjs/components/MainComponents/MessageContainer.js +4 -12
- package/lib/commonjs/components/MainComponents/MessageContainer.js.map +1 -1
- package/lib/commonjs/components/MainComponents/MessageList.js +110 -35
- package/lib/commonjs/components/MainComponents/MessageList.js.map +1 -1
- package/lib/commonjs/components/MainComponents/RoomList.js +32 -29
- package/lib/commonjs/components/MainComponents/RoomList.js.map +1 -1
- package/lib/commonjs/components/MainComponents/RoomStack.js +133 -65
- package/lib/commonjs/components/MainComponents/RoomStack.js.map +1 -1
- package/lib/commonjs/components/MainComponents/SystemMessage.js +6 -2
- package/lib/commonjs/components/MainComponents/SystemMessage.js.map +1 -1
- package/lib/commonjs/components/MessageBubble/Avatar.js +5 -1
- package/lib/commonjs/components/MessageBubble/Avatar.js.map +1 -1
- package/lib/commonjs/components/MessageBubble/BottomReplyContainer.js +17 -21
- package/lib/commonjs/components/MessageBubble/BottomReplyContainer.js.map +1 -1
- package/lib/commonjs/components/MessageBubble/EmojiPickerSheet.js +447 -0
- package/lib/commonjs/components/MessageBubble/EmojiPickerSheet.js.map +1 -0
- package/lib/commonjs/components/MessageBubble/Message.js +200 -132
- package/lib/commonjs/components/MessageBubble/Message.js.map +1 -1
- package/lib/commonjs/components/MessageBubble/MessageInteractions.js +213 -12
- package/lib/commonjs/components/MessageBubble/MessageInteractions.js.map +1 -1
- package/lib/commonjs/components/MessageBubble/MessageReaction.js +113 -94
- package/lib/commonjs/components/MessageBubble/MessageReaction.js.map +1 -1
- package/lib/commonjs/components/MessageBubble/MessageReply.js +66 -24
- package/lib/commonjs/components/MessageBubble/MessageReply.js.map +1 -1
- package/lib/commonjs/components/Modals/Modal/SwipeBackLayer.js +76 -44
- package/lib/commonjs/components/Modals/Modal/SwipeBackLayer.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/ChatRoomItem.js +5 -4
- package/lib/commonjs/components/RoomComponents/ChatRoomItem.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/styled/LastMessageEmoji.js +8 -9
- package/lib/commonjs/components/RoomComponents/styled/LastMessageEmoji.js.map +1 -1
- package/lib/commonjs/components/Thread/ThreadWrapper.js +265 -152
- package/lib/commonjs/components/Thread/ThreadWrapper.js.map +1 -1
- package/lib/commonjs/components/styled/DateLabel.js +6 -3
- package/lib/commonjs/components/styled/DateLabel.js.map +1 -1
- package/lib/commonjs/context/MessageNotificationContext.js +188 -9
- package/lib/commonjs/context/MessageNotificationContext.js.map +1 -1
- package/lib/commonjs/context/xmppProvider.js +22 -7
- package/lib/commonjs/context/xmppProvider.js.map +1 -1
- package/lib/commonjs/helpers/createRoomFromApi.js +36 -2
- package/lib/commonjs/helpers/createRoomFromApi.js.map +1 -1
- package/lib/commonjs/helpers/emoji.js +51 -0
- package/lib/commonjs/helpers/emoji.js.map +1 -0
- package/lib/commonjs/helpers/haptics.js +29 -0
- package/lib/commonjs/helpers/haptics.js.map +1 -0
- package/lib/commonjs/helpers/historyPreloadScheduler.js +32 -66
- package/lib/commonjs/helpers/historyPreloadScheduler.js.map +1 -1
- package/lib/commonjs/helpers/insertMessageWithDelimiter.js +21 -12
- package/lib/commonjs/helpers/insertMessageWithDelimiter.js.map +1 -1
- package/lib/commonjs/helpers/mamReactions.js +57 -0
- package/lib/commonjs/helpers/mamReactions.js.map +1 -0
- package/lib/commonjs/helpers/parseMessageReference.js +42 -0
- package/lib/commonjs/helpers/parseMessageReference.js.map +1 -0
- package/lib/commonjs/helpers/reactionsConfig.js +17 -0
- package/lib/commonjs/helpers/reactionsConfig.js.map +1 -0
- package/lib/commonjs/helpers/updateMessagesTillLast.js +17 -2
- package/lib/commonjs/helpers/updateMessagesTillLast.js.map +1 -1
- package/lib/commonjs/hooks/useHeapSender.js +3 -1
- package/lib/commonjs/hooks/useHeapSender.js.map +1 -1
- package/lib/commonjs/hooks/useMessageHeapState.js +3 -2
- package/lib/commonjs/hooks/useMessageHeapState.js.map +1 -1
- package/lib/commonjs/hooks/useRoomInitialization.js +16 -7
- package/lib/commonjs/hooks/useRoomInitialization.js.map +1 -1
- package/lib/commonjs/hooks/useSendMessage.js +14 -8
- package/lib/commonjs/hooks/useSendMessage.js.map +1 -1
- package/lib/commonjs/hooks/useUnreadMessagesCounter.js +1 -1
- package/lib/commonjs/hooks/useUnreadMessagesCounter.js.map +1 -1
- package/lib/commonjs/networking/stanzaHandlers.js +106 -50
- package/lib/commonjs/networking/stanzaHandlers.js.map +1 -1
- package/lib/commonjs/networking/xmpp/getHistory.xmpp.js +29 -74
- package/lib/commonjs/networking/xmpp/getHistory.xmpp.js.map +1 -1
- package/lib/commonjs/networking/xmpp/handleStanzas.xmpp.js +4 -0
- package/lib/commonjs/networking/xmpp/handleStanzas.xmpp.js.map +1 -1
- package/lib/commonjs/networking/xmpp/mamRouter.js +186 -0
- package/lib/commonjs/networking/xmpp/mamRouter.js.map +1 -0
- package/lib/commonjs/networking/xmpp/presenceInRoom.xmpp.js +41 -4
- package/lib/commonjs/networking/xmpp/presenceInRoom.xmpp.js.map +1 -1
- package/lib/commonjs/networking/xmppClient.js +98 -23
- package/lib/commonjs/networking/xmppClient.js.map +1 -1
- package/lib/commonjs/roomStore/Middleware/newMessageMidlleware.js +29 -34
- package/lib/commonjs/roomStore/Middleware/newMessageMidlleware.js.map +1 -1
- package/lib/commonjs/roomStore/Middleware/perfMiddleware.js +51 -0
- package/lib/commonjs/roomStore/Middleware/perfMiddleware.js.map +1 -0
- package/lib/commonjs/roomStore/Middleware/reactionsMiddleware.js +66 -43
- package/lib/commonjs/roomStore/Middleware/reactionsMiddleware.js.map +1 -1
- package/lib/commonjs/roomStore/Middleware/unreadMidlleware.js +88 -68
- package/lib/commonjs/roomStore/Middleware/unreadMidlleware.js.map +1 -1
- package/lib/commonjs/roomStore/index.js +18 -14
- package/lib/commonjs/roomStore/index.js.map +1 -1
- package/lib/commonjs/roomStore/persistBackend.js +96 -0
- package/lib/commonjs/roomStore/persistBackend.js.map +1 -0
- package/lib/commonjs/roomStore/persistence.js +245 -50
- package/lib/commonjs/roomStore/persistence.js.map +1 -1
- package/lib/commonjs/roomStore/roomsSlice.js +282 -136
- package/lib/commonjs/roomStore/roomsSlice.js.map +1 -1
- package/lib/commonjs/theme/theme.js +3 -0
- package/lib/commonjs/theme/theme.js.map +1 -1
- package/lib/commonjs/utils/devLogger.js +14 -0
- package/lib/commonjs/utils/devLogger.js.map +1 -1
- package/lib/commonjs/version.js +1 -1
- package/lib/module/assets/emojiData.js +36 -0
- package/lib/module/assets/emojiData.js.map +1 -0
- package/lib/module/assets/icons.js +1 -1
- package/lib/module/assets/icons.js.map +1 -1
- package/lib/module/components/MainComponents/ChatHeader.js +8 -10
- package/lib/module/components/MainComponents/ChatHeader.js.map +1 -1
- package/lib/module/components/MainComponents/ChatRoom.js +25 -24
- package/lib/module/components/MainComponents/ChatRoom.js.map +1 -1
- package/lib/module/components/MainComponents/ChatWrapper.js +23 -17
- package/lib/module/components/MainComponents/ChatWrapper.js.map +1 -1
- package/lib/module/components/MainComponents/MessageContainer.js +7 -14
- package/lib/module/components/MainComponents/MessageContainer.js.map +1 -1
- package/lib/module/components/MainComponents/MessageList.js +110 -35
- package/lib/module/components/MainComponents/MessageList.js.map +1 -1
- package/lib/module/components/MainComponents/RoomList.js +32 -29
- package/lib/module/components/MainComponents/RoomList.js.map +1 -1
- package/lib/module/components/MainComponents/RoomStack.js +131 -63
- package/lib/module/components/MainComponents/RoomStack.js.map +1 -1
- package/lib/module/components/MainComponents/SystemMessage.js +6 -2
- package/lib/module/components/MainComponents/SystemMessage.js.map +1 -1
- package/lib/module/components/MessageBubble/Avatar.js +5 -1
- package/lib/module/components/MessageBubble/Avatar.js.map +1 -1
- package/lib/module/components/MessageBubble/BottomReplyContainer.js +18 -22
- package/lib/module/components/MessageBubble/BottomReplyContainer.js.map +1 -1
- package/lib/module/components/MessageBubble/EmojiPickerSheet.js +438 -0
- package/lib/module/components/MessageBubble/EmojiPickerSheet.js.map +1 -0
- package/lib/module/components/MessageBubble/Message.js +203 -134
- package/lib/module/components/MessageBubble/Message.js.map +1 -1
- package/lib/module/components/MessageBubble/MessageInteractions.js +215 -14
- package/lib/module/components/MessageBubble/MessageInteractions.js.map +1 -1
- package/lib/module/components/MessageBubble/MessageReaction.js +115 -94
- package/lib/module/components/MessageBubble/MessageReaction.js.map +1 -1
- package/lib/module/components/MessageBubble/MessageReply.js +65 -24
- package/lib/module/components/MessageBubble/MessageReply.js.map +1 -1
- package/lib/module/components/Modals/Modal/SwipeBackLayer.js +76 -44
- package/lib/module/components/Modals/Modal/SwipeBackLayer.js.map +1 -1
- package/lib/module/components/RoomComponents/ChatRoomItem.js +5 -4
- package/lib/module/components/RoomComponents/ChatRoomItem.js.map +1 -1
- package/lib/module/components/RoomComponents/styled/LastMessageEmoji.js +8 -9
- package/lib/module/components/RoomComponents/styled/LastMessageEmoji.js.map +1 -1
- package/lib/module/components/Thread/ThreadWrapper.js +264 -152
- package/lib/module/components/Thread/ThreadWrapper.js.map +1 -1
- package/lib/module/components/styled/DateLabel.js +6 -3
- package/lib/module/components/styled/DateLabel.js.map +1 -1
- package/lib/module/context/MessageNotificationContext.js +190 -11
- package/lib/module/context/MessageNotificationContext.js.map +1 -1
- package/lib/module/context/xmppProvider.js +22 -7
- package/lib/module/context/xmppProvider.js.map +1 -1
- package/lib/module/helpers/createRoomFromApi.js +36 -2
- package/lib/module/helpers/createRoomFromApi.js.map +1 -1
- package/lib/module/helpers/emoji.js +44 -0
- package/lib/module/helpers/emoji.js.map +1 -0
- package/lib/module/helpers/haptics.js +24 -0
- package/lib/module/helpers/haptics.js.map +1 -0
- package/lib/module/helpers/historyPreloadScheduler.js +32 -66
- package/lib/module/helpers/historyPreloadScheduler.js.map +1 -1
- package/lib/module/helpers/insertMessageWithDelimiter.js +21 -12
- package/lib/module/helpers/insertMessageWithDelimiter.js.map +1 -1
- package/lib/module/helpers/mamReactions.js +51 -0
- package/lib/module/helpers/mamReactions.js.map +1 -0
- package/lib/module/helpers/parseMessageReference.js +37 -0
- package/lib/module/helpers/parseMessageReference.js.map +1 -0
- package/lib/module/helpers/reactionsConfig.js +10 -0
- package/lib/module/helpers/reactionsConfig.js.map +1 -0
- package/lib/module/helpers/updateMessagesTillLast.js +17 -2
- package/lib/module/helpers/updateMessagesTillLast.js.map +1 -1
- package/lib/module/hooks/useHeapSender.js +3 -1
- package/lib/module/hooks/useHeapSender.js.map +1 -1
- package/lib/module/hooks/useMessageHeapState.js +3 -2
- package/lib/module/hooks/useMessageHeapState.js.map +1 -1
- package/lib/module/hooks/useRoomInitialization.js +17 -8
- package/lib/module/hooks/useRoomInitialization.js.map +1 -1
- package/lib/module/hooks/useSendMessage.js +14 -8
- package/lib/module/hooks/useSendMessage.js.map +1 -1
- package/lib/module/hooks/useUnreadMessagesCounter.js +1 -1
- package/lib/module/hooks/useUnreadMessagesCounter.js.map +1 -1
- package/lib/module/networking/stanzaHandlers.js +107 -51
- package/lib/module/networking/stanzaHandlers.js.map +1 -1
- package/lib/module/networking/xmpp/getHistory.xmpp.js +29 -74
- package/lib/module/networking/xmpp/getHistory.xmpp.js.map +1 -1
- package/lib/module/networking/xmpp/handleStanzas.xmpp.js +4 -0
- package/lib/module/networking/xmpp/handleStanzas.xmpp.js.map +1 -1
- package/lib/module/networking/xmpp/mamRouter.js +175 -0
- package/lib/module/networking/xmpp/mamRouter.js.map +1 -0
- package/lib/module/networking/xmpp/presenceInRoom.xmpp.js +38 -2
- package/lib/module/networking/xmpp/presenceInRoom.xmpp.js.map +1 -1
- package/lib/module/networking/xmppClient.js +99 -24
- package/lib/module/networking/xmppClient.js.map +1 -1
- package/lib/module/roomStore/Middleware/newMessageMidlleware.js +29 -34
- package/lib/module/roomStore/Middleware/newMessageMidlleware.js.map +1 -1
- package/lib/module/roomStore/Middleware/perfMiddleware.js +45 -0
- package/lib/module/roomStore/Middleware/perfMiddleware.js.map +1 -0
- package/lib/module/roomStore/Middleware/reactionsMiddleware.js +66 -43
- package/lib/module/roomStore/Middleware/reactionsMiddleware.js.map +1 -1
- package/lib/module/roomStore/Middleware/unreadMidlleware.js +89 -69
- package/lib/module/roomStore/Middleware/unreadMidlleware.js.map +1 -1
- package/lib/module/roomStore/index.js +19 -15
- package/lib/module/roomStore/index.js.map +1 -1
- package/lib/module/roomStore/persistBackend.js +89 -0
- package/lib/module/roomStore/persistBackend.js.map +1 -0
- package/lib/module/roomStore/persistence.js +245 -48
- package/lib/module/roomStore/persistence.js.map +1 -1
- package/lib/module/roomStore/roomsSlice.js +275 -134
- package/lib/module/roomStore/roomsSlice.js.map +1 -1
- package/lib/module/theme/theme.js +3 -0
- package/lib/module/theme/theme.js.map +1 -1
- package/lib/module/utils/devLogger.js +13 -0
- package/lib/module/utils/devLogger.js.map +1 -1
- package/lib/module/version.js +1 -1
- package/lib/typescript/assets/emojiData.d.ts +8 -0
- package/lib/typescript/assets/emojiData.d.ts.map +1 -0
- 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/MessageContainer.d.ts +2 -2
- 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/RoomList.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/RoomStack.d.ts +7 -2
- package/lib/typescript/components/MainComponents/RoomStack.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/SystemMessage.d.ts.map +1 -1
- package/lib/typescript/components/MessageBubble/Avatar.d.ts +2 -0
- package/lib/typescript/components/MessageBubble/Avatar.d.ts.map +1 -1
- package/lib/typescript/components/MessageBubble/BottomReplyContainer.d.ts.map +1 -1
- package/lib/typescript/components/MessageBubble/EmojiPickerSheet.d.ts +11 -0
- package/lib/typescript/components/MessageBubble/EmojiPickerSheet.d.ts.map +1 -0
- package/lib/typescript/components/MessageBubble/Message.d.ts +2 -2
- package/lib/typescript/components/MessageBubble/Message.d.ts.map +1 -1
- package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts +2 -0
- package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts.map +1 -1
- package/lib/typescript/components/MessageBubble/MessageReaction.d.ts +1 -0
- package/lib/typescript/components/MessageBubble/MessageReaction.d.ts.map +1 -1
- package/lib/typescript/components/MessageBubble/MessageReply.d.ts +7 -7
- package/lib/typescript/components/MessageBubble/MessageReply.d.ts.map +1 -1
- package/lib/typescript/components/Modals/Modal/SwipeBackLayer.d.ts +2 -1
- package/lib/typescript/components/Modals/Modal/SwipeBackLayer.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/ChatRoomItem.d.ts +2 -2
- package/lib/typescript/components/RoomComponents/ChatRoomItem.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/styled/LastMessageEmoji.d.ts +3 -6
- package/lib/typescript/components/RoomComponents/styled/LastMessageEmoji.d.ts.map +1 -1
- package/lib/typescript/components/Thread/ThreadWrapper.d.ts +7 -1
- package/lib/typescript/components/Thread/ThreadWrapper.d.ts.map +1 -1
- package/lib/typescript/components/styled/DateLabel.d.ts +1 -0
- package/lib/typescript/components/styled/DateLabel.d.ts.map +1 -1
- package/lib/typescript/context/MessageNotificationContext.d.ts.map +1 -1
- package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
- package/lib/typescript/helpers/createRoomFromApi.d.ts.map +1 -1
- package/lib/typescript/helpers/emoji.d.ts +6 -0
- package/lib/typescript/helpers/emoji.d.ts.map +1 -0
- package/lib/typescript/helpers/haptics.d.ts +3 -0
- package/lib/typescript/helpers/haptics.d.ts.map +1 -0
- package/lib/typescript/helpers/historyPreloadScheduler.d.ts +1 -0
- package/lib/typescript/helpers/historyPreloadScheduler.d.ts.map +1 -1
- package/lib/typescript/helpers/insertMessageWithDelimiter.d.ts.map +1 -1
- package/lib/typescript/helpers/mamReactions.d.ts +13 -0
- package/lib/typescript/helpers/mamReactions.d.ts.map +1 -0
- package/lib/typescript/helpers/parseMessageReference.d.ts +16 -0
- package/lib/typescript/helpers/parseMessageReference.d.ts.map +1 -0
- package/lib/typescript/helpers/reactionsConfig.d.ts +5 -0
- package/lib/typescript/helpers/reactionsConfig.d.ts.map +1 -0
- package/lib/typescript/helpers/updateMessagesTillLast.d.ts.map +1 -1
- package/lib/typescript/hooks/useHeapSender.d.ts.map +1 -1
- package/lib/typescript/hooks/useMessageHeapState.d.ts.map +1 -1
- package/lib/typescript/hooks/useRoomInitialization.d.ts +2 -2
- package/lib/typescript/hooks/useRoomInitialization.d.ts.map +1 -1
- package/lib/typescript/hooks/useSendMessage.d.ts.map +1 -1
- package/lib/typescript/networking/stanzaHandlers.d.ts +4 -2
- package/lib/typescript/networking/stanzaHandlers.d.ts.map +1 -1
- package/lib/typescript/networking/xmpp/getHistory.xmpp.d.ts +3 -1
- package/lib/typescript/networking/xmpp/getHistory.xmpp.d.ts.map +1 -1
- package/lib/typescript/networking/xmpp/handleStanzas.xmpp.d.ts.map +1 -1
- package/lib/typescript/networking/xmpp/mamRouter.d.ts +23 -0
- package/lib/typescript/networking/xmpp/mamRouter.d.ts.map +1 -0
- package/lib/typescript/networking/xmpp/presenceInRoom.xmpp.d.ts +1 -0
- package/lib/typescript/networking/xmpp/presenceInRoom.xmpp.d.ts.map +1 -1
- package/lib/typescript/networking/xmppClient.d.ts +22 -1
- package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
- package/lib/typescript/roomStore/Middleware/newMessageMidlleware.d.ts.map +1 -1
- package/lib/typescript/roomStore/Middleware/perfMiddleware.d.ts +4 -0
- package/lib/typescript/roomStore/Middleware/perfMiddleware.d.ts.map +1 -0
- package/lib/typescript/roomStore/Middleware/reactionsMiddleware.d.ts +9 -0
- package/lib/typescript/roomStore/Middleware/reactionsMiddleware.d.ts.map +1 -1
- package/lib/typescript/roomStore/Middleware/unreadMidlleware.d.ts.map +1 -1
- package/lib/typescript/roomStore/index.d.ts.map +1 -1
- package/lib/typescript/roomStore/persistBackend.d.ts +11 -0
- package/lib/typescript/roomStore/persistBackend.d.ts.map +1 -0
- package/lib/typescript/roomStore/persistence.d.ts +5 -6
- package/lib/typescript/roomStore/persistence.d.ts.map +1 -1
- package/lib/typescript/roomStore/roomsSlice.d.ts +41 -1
- package/lib/typescript/roomStore/roomsSlice.d.ts.map +1 -1
- package/lib/typescript/theme/theme.d.ts +4 -1
- package/lib/typescript/theme/theme.d.ts.map +1 -1
- package/lib/typescript/types/models/message.model.d.ts +4 -0
- package/lib/typescript/types/models/message.model.d.ts.map +1 -1
- package/lib/typescript/types/models/room.model.d.ts +16 -0
- package/lib/typescript/types/models/room.model.d.ts.map +1 -1
- package/lib/typescript/types/models/xmpp.model.d.ts +6 -1
- package/lib/typescript/types/models/xmpp.model.d.ts.map +1 -1
- package/lib/typescript/types/types.d.ts +15 -3
- package/lib/typescript/types/types.d.ts.map +1 -1
- package/lib/typescript/utils/devLogger.d.ts +8 -0
- package/lib/typescript/utils/devLogger.d.ts.map +1 -1
- package/lib/typescript/version.d.ts +1 -1
- package/package.json +10 -1
- package/src/assets/emojiData.ts +2146 -0
- package/src/assets/icons.tsx +5 -5
- package/src/components/MainComponents/ChatHeader.tsx +16 -7
- package/src/components/MainComponents/ChatRoom.tsx +35 -29
- package/src/components/MainComponents/ChatWrapper.tsx +28 -10
- package/src/components/MainComponents/MessageContainer.tsx +7 -15
- package/src/components/MainComponents/MessageList.tsx +96 -43
- package/src/components/MainComponents/RoomList.tsx +28 -29
- package/src/components/MainComponents/RoomStack.tsx +156 -83
- package/src/components/MainComponents/SystemMessage.tsx +6 -2
- package/src/components/MessageBubble/Avatar.tsx +7 -1
- package/src/components/MessageBubble/BottomReplyContainer.tsx +19 -21
- package/src/components/MessageBubble/EmojiPickerSheet.tsx +468 -0
- package/src/components/MessageBubble/Message.tsx +146 -87
- package/src/components/MessageBubble/MessageInteractions.tsx +213 -12
- package/src/components/MessageBubble/MessageReaction.tsx +120 -96
- package/src/components/MessageBubble/MessageReply.tsx +65 -32
- package/src/components/Modals/Modal/SwipeBackLayer.tsx +114 -63
- package/src/components/RoomComponents/ChatRoomItem.tsx +11 -4
- package/src/components/RoomComponents/styled/LastMessageEmoji.tsx +7 -8
- package/src/components/Thread/ThreadWrapper.tsx +274 -198
- package/src/components/styled/DateLabel.tsx +7 -3
- package/src/context/MessageNotificationContext.tsx +205 -7
- package/src/context/xmppProvider.tsx +29 -9
- package/src/helpers/createRoomFromApi.tsx +37 -1
- package/src/helpers/emoji.ts +39 -0
- package/src/helpers/haptics.ts +19 -0
- package/src/helpers/historyPreloadScheduler.ts +37 -69
- package/src/helpers/insertMessageWithDelimiter.ts +23 -16
- package/src/helpers/mamReactions.ts +59 -0
- package/src/helpers/parseMessageReference.ts +38 -0
- package/src/helpers/reactionsConfig.ts +13 -0
- package/src/helpers/updateMessagesTillLast.tsx +14 -2
- package/src/hooks/useHeapSender.tsx +3 -2
- package/src/hooks/useMessageHeapState.tsx +6 -2
- package/src/hooks/useRoomInitialization.tsx +32 -15
- package/src/hooks/useSendMessage.tsx +18 -11
- package/src/hooks/useUnreadMessagesCounter.ts +1 -1
- package/src/networking/stanzaHandlers.ts +116 -60
- package/src/networking/xmpp/getHistory.xmpp.ts +44 -106
- package/src/networking/xmpp/handleStanzas.xmpp.ts +2 -0
- package/src/networking/xmpp/mamRouter.ts +176 -0
- package/src/networking/xmpp/presenceInRoom.xmpp.ts +46 -3
- package/src/networking/xmppClient.ts +105 -20
- package/src/roomStore/Middleware/newMessageMidlleware.tsx +35 -38
- package/src/roomStore/Middleware/perfMiddleware.ts +50 -0
- package/src/roomStore/Middleware/reactionsMiddleware.tsx +53 -52
- package/src/roomStore/Middleware/unreadMidlleware.tsx +92 -80
- package/src/roomStore/index.ts +28 -27
- package/src/roomStore/persistBackend.ts +96 -0
- package/src/roomStore/persistence.ts +280 -73
- package/src/roomStore/roomsSlice.ts +304 -155
- package/src/theme/theme.ts +10 -1
- package/src/types/models/message.model.ts +4 -0
- package/src/types/models/room.model.ts +17 -0
- package/src/types/models/xmpp.model.ts +4 -2
- package/src/types/types.ts +15 -3
- package/src/utils/devLogger.ts +16 -0
- package/src/version.ts +1 -1
|
@@ -8,6 +8,7 @@ import React, {
|
|
|
8
8
|
useCallback,
|
|
9
9
|
useContext,
|
|
10
10
|
useEffect,
|
|
11
|
+
useMemo,
|
|
11
12
|
useRef,
|
|
12
13
|
useState,
|
|
13
14
|
} from 'react';
|
|
@@ -15,17 +16,23 @@ import {
|
|
|
15
16
|
Animated,
|
|
16
17
|
AppState,
|
|
17
18
|
AppStateStatus,
|
|
19
|
+
Easing,
|
|
20
|
+
PanResponder,
|
|
18
21
|
Pressable,
|
|
19
22
|
StyleSheet,
|
|
20
23
|
Text,
|
|
21
24
|
View,
|
|
22
25
|
} from 'react-native';
|
|
26
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
23
27
|
import { useDispatch, useSelector } from 'react-redux';
|
|
24
28
|
import { messageNotificationManager } from '../utils/messageNotificationManager';
|
|
25
29
|
import { setCurrentRoom } from '../roomStore/roomsSlice';
|
|
26
30
|
import { IConfig, IMessage } from '../types/types';
|
|
27
31
|
import { RootState } from '../roomStore';
|
|
28
32
|
import { useTheme } from '../hooks/useTheme';
|
|
33
|
+
import { ProfileImagePlaceholder } from '../components/MainComponents/ProfileImagePlaceholder';
|
|
34
|
+
import { useFileToken } from '../hooks/useFileToken';
|
|
35
|
+
import { appendFileToken } from '../helpers/secureFileUrl';
|
|
29
36
|
|
|
30
37
|
interface ToastItem {
|
|
31
38
|
id: string;
|
|
@@ -49,7 +56,18 @@ const MessageNotificationContext =
|
|
|
49
56
|
createContext<MessageNotificationContextValue | null>(null);
|
|
50
57
|
|
|
51
58
|
const DEFAULT_MAX = 3;
|
|
59
|
+
|
|
60
|
+
const pruneExpired = (prev: ToastItem[], now: number, ttl: number) => {
|
|
61
|
+
if (prev.length === 0) {return prev;}
|
|
62
|
+
const next = prev.filter((t) => now - t.timestamp < ttl);
|
|
63
|
+
return next.length === prev.length ? prev : next;
|
|
64
|
+
};
|
|
52
65
|
const DEFAULT_DURATION_MS = 30000;
|
|
66
|
+
// The top banner is a glance, not a stack to work through: it leaves on its
|
|
67
|
+
// own after a few seconds unless the host sets `duration`.
|
|
68
|
+
const BANNER_DURATION_MS = 4000;
|
|
69
|
+
const BANNER_IN_MS = 280;
|
|
70
|
+
const BANNER_OUT_MS = 220;
|
|
53
71
|
|
|
54
72
|
interface ProviderProps {
|
|
55
73
|
children: ReactNode;
|
|
@@ -72,7 +90,17 @@ export const MessageNotificationProvider: React.FC<ProviderProps> = ({
|
|
|
72
90
|
const notificationConfig = config?.inAppNotifications;
|
|
73
91
|
const isEnabled = notificationConfig?.enabled === true;
|
|
74
92
|
const maxNotifications = notificationConfig?.maxNotifications ?? DEFAULT_MAX;
|
|
75
|
-
|
|
93
|
+
// Default presentation: one full-width banner sliding in from the top
|
|
94
|
+
// (the messenger convention). An explicit `position` or a
|
|
95
|
+
// `customComponent` keeps the stacked corner toasts.
|
|
96
|
+
const bannerMode =
|
|
97
|
+
!notificationConfig?.position && !notificationConfig?.customComponent;
|
|
98
|
+
const duration =
|
|
99
|
+
notificationConfig?.duration ??
|
|
100
|
+
(bannerMode ? BANNER_DURATION_MS : DEFAULT_DURATION_MS);
|
|
101
|
+
// The banner times its own exit (so it can animate out); the list prune
|
|
102
|
+
// below is only its safety net.
|
|
103
|
+
const pruneAfter = bannerMode ? duration + 2000 : duration;
|
|
76
104
|
|
|
77
105
|
const [toasts, setToasts] = useState<ToastItem[]>([]);
|
|
78
106
|
const appActiveRef = useRef(AppState.currentState === 'active');
|
|
@@ -86,22 +114,22 @@ export const MessageNotificationProvider: React.FC<ProviderProps> = ({
|
|
|
86
114
|
appActiveRef.current = next === 'active';
|
|
87
115
|
if (next === 'active') {
|
|
88
116
|
const now = Date.now();
|
|
89
|
-
setToasts((prev) => prev
|
|
117
|
+
setToasts((prev) => pruneExpired(prev, now, pruneAfter));
|
|
90
118
|
}
|
|
91
119
|
}
|
|
92
120
|
);
|
|
93
121
|
return () => sub.remove();
|
|
94
|
-
}, [
|
|
122
|
+
}, [pruneAfter]);
|
|
95
123
|
|
|
96
124
|
// Periodic prune when active.
|
|
97
125
|
useEffect(() => {
|
|
98
126
|
const interval = setInterval(() => {
|
|
99
127
|
if (!appActiveRef.current) {return;}
|
|
100
128
|
const now = Date.now();
|
|
101
|
-
setToasts((prev) => prev
|
|
129
|
+
setToasts((prev) => pruneExpired(prev, now, pruneAfter));
|
|
102
130
|
}, 1000);
|
|
103
131
|
return () => clearInterval(interval);
|
|
104
|
-
}, [
|
|
132
|
+
}, [pruneAfter]);
|
|
105
133
|
|
|
106
134
|
// Clear toasts when their room becomes active.
|
|
107
135
|
useEffect(() => {
|
|
@@ -172,10 +200,33 @@ export const MessageNotificationProvider: React.FC<ProviderProps> = ({
|
|
|
172
200
|
return unsubscribe;
|
|
173
201
|
}, [isEnabled, showMessageNotification]);
|
|
174
202
|
|
|
203
|
+
const contextValue = useMemo(
|
|
204
|
+
() => ({ showMessageNotification }),
|
|
205
|
+
[showMessageNotification]
|
|
206
|
+
);
|
|
207
|
+
|
|
175
208
|
return (
|
|
176
|
-
<MessageNotificationContext.Provider value={
|
|
209
|
+
<MessageNotificationContext.Provider value={contextValue}>
|
|
177
210
|
{children}
|
|
178
|
-
{isEnabled && toasts.length > 0 && (
|
|
211
|
+
{isEnabled && bannerMode && toasts.length > 0 && (
|
|
212
|
+
<ToastBanner
|
|
213
|
+
item={toasts[toasts.length - 1]}
|
|
214
|
+
duration={duration}
|
|
215
|
+
safeAreaTop={!!config?.headerLayout?.safeAreaTop}
|
|
216
|
+
onPress={(t) =>
|
|
217
|
+
navigateToMessage(
|
|
218
|
+
t.roomJID,
|
|
219
|
+
t.message.id,
|
|
220
|
+
t.message,
|
|
221
|
+
t.roomName,
|
|
222
|
+
t.senderName
|
|
223
|
+
)
|
|
224
|
+
}
|
|
225
|
+
// One banner stands for everything queued behind it.
|
|
226
|
+
onDismiss={() => setToasts([])}
|
|
227
|
+
/>
|
|
228
|
+
)}
|
|
229
|
+
{isEnabled && !bannerMode && toasts.length > 0 && (
|
|
179
230
|
<View
|
|
180
231
|
pointerEvents="box-none"
|
|
181
232
|
style={[
|
|
@@ -224,6 +275,122 @@ export const MessageNotificationProvider: React.FC<ProviderProps> = ({
|
|
|
224
275
|
);
|
|
225
276
|
};
|
|
226
277
|
|
|
278
|
+
/**
|
|
279
|
+
* Top banner: slides down from under the status bar, stays for `duration`,
|
|
280
|
+
* slides back up. Tap opens the room; a flick upwards dismisses it. A newer
|
|
281
|
+
* message replaces the content in place and restarts the timer.
|
|
282
|
+
*/
|
|
283
|
+
const ToastBanner: React.FC<{
|
|
284
|
+
item: ToastItem;
|
|
285
|
+
duration: number;
|
|
286
|
+
safeAreaTop: boolean;
|
|
287
|
+
onPress: (item: ToastItem) => void;
|
|
288
|
+
onDismiss: () => void;
|
|
289
|
+
}> = ({ item, duration, safeAreaTop, onPress, onDismiss }) => {
|
|
290
|
+
const theme = useTheme();
|
|
291
|
+
const insets = useSafeAreaInsets();
|
|
292
|
+
const top = (safeAreaTop ? insets.top : 0) + 8;
|
|
293
|
+
const roomIcon = useSelector(
|
|
294
|
+
(state: RootState) => state.rooms.rooms?.[item.roomJID]?.icon
|
|
295
|
+
);
|
|
296
|
+
const fileToken = useFileToken();
|
|
297
|
+
// 0 = hidden above the screen, 1 = in place.
|
|
298
|
+
const shown = useRef(new Animated.Value(0)).current;
|
|
299
|
+
const drag = useRef(new Animated.Value(0)).current;
|
|
300
|
+
const leaving = useRef(false);
|
|
301
|
+
const onDismissRef = useRef(onDismiss);
|
|
302
|
+
onDismissRef.current = onDismiss;
|
|
303
|
+
|
|
304
|
+
const leave = useCallback(() => {
|
|
305
|
+
if (leaving.current) {return;}
|
|
306
|
+
leaving.current = true;
|
|
307
|
+
Animated.timing(shown, {
|
|
308
|
+
toValue: 0,
|
|
309
|
+
duration: BANNER_OUT_MS,
|
|
310
|
+
easing: Easing.in(Easing.cubic),
|
|
311
|
+
useNativeDriver: true,
|
|
312
|
+
}).start(() => onDismissRef.current());
|
|
313
|
+
}, [shown]);
|
|
314
|
+
|
|
315
|
+
useEffect(() => {
|
|
316
|
+
Animated.timing(shown, {
|
|
317
|
+
toValue: 1,
|
|
318
|
+
duration: BANNER_IN_MS,
|
|
319
|
+
easing: Easing.out(Easing.cubic),
|
|
320
|
+
useNativeDriver: true,
|
|
321
|
+
}).start();
|
|
322
|
+
}, [shown]);
|
|
323
|
+
|
|
324
|
+
// Restarts with every new message shown in the banner.
|
|
325
|
+
useEffect(() => {
|
|
326
|
+
const timer = setTimeout(leave, duration);
|
|
327
|
+
return () => clearTimeout(timer);
|
|
328
|
+
}, [item.id, duration, leave]);
|
|
329
|
+
|
|
330
|
+
const pan = useRef(
|
|
331
|
+
PanResponder.create({
|
|
332
|
+
onMoveShouldSetPanResponder: (_e, g) =>
|
|
333
|
+
g.dy < -6 && Math.abs(g.dy) > Math.abs(g.dx),
|
|
334
|
+
onPanResponderMove: (_e, g) => drag.setValue(Math.min(g.dy, 0)),
|
|
335
|
+
onPanResponderRelease: (_e, g) => {
|
|
336
|
+
if (g.dy < -24 || g.vy < -0.5) {
|
|
337
|
+
leave();
|
|
338
|
+
} else {
|
|
339
|
+
Animated.spring(drag, { toValue: 0, useNativeDriver: true }).start();
|
|
340
|
+
}
|
|
341
|
+
},
|
|
342
|
+
onPanResponderTerminate: () => {
|
|
343
|
+
Animated.spring(drag, { toValue: 0, useNativeDriver: true }).start();
|
|
344
|
+
},
|
|
345
|
+
})
|
|
346
|
+
).current;
|
|
347
|
+
|
|
348
|
+
const slide = shown.interpolate({
|
|
349
|
+
inputRange: [0, 1],
|
|
350
|
+
outputRange: [-(top + 96), 0],
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
return (
|
|
354
|
+
<Animated.View
|
|
355
|
+
testID="message-notification-banner"
|
|
356
|
+
{...pan.panHandlers}
|
|
357
|
+
style={[
|
|
358
|
+
styles.banner,
|
|
359
|
+
{
|
|
360
|
+
top,
|
|
361
|
+
backgroundColor: theme.surface,
|
|
362
|
+
borderColor: theme.border,
|
|
363
|
+
shadowColor: theme.shadow,
|
|
364
|
+
opacity: shown,
|
|
365
|
+
transform: [{ translateY: Animated.add(slide, drag) }],
|
|
366
|
+
},
|
|
367
|
+
]}
|
|
368
|
+
>
|
|
369
|
+
<Pressable style={styles.bannerBody} onPress={() => onPress(item)}>
|
|
370
|
+
<ProfileImagePlaceholder
|
|
371
|
+
name={item.roomName}
|
|
372
|
+
icon={appendFileToken(roomIcon, fileToken)}
|
|
373
|
+
size={40}
|
|
374
|
+
/>
|
|
375
|
+
<View style={styles.bannerText}>
|
|
376
|
+
<Text
|
|
377
|
+
style={[styles.bannerTitle, { color: theme.text }]}
|
|
378
|
+
numberOfLines={1}
|
|
379
|
+
>
|
|
380
|
+
{item.roomName}
|
|
381
|
+
</Text>
|
|
382
|
+
<Text
|
|
383
|
+
style={[styles.bannerSubtitle, { color: theme.textSecondary }]}
|
|
384
|
+
numberOfLines={2}
|
|
385
|
+
>
|
|
386
|
+
{item.senderName}: {item.message?.body || ''}
|
|
387
|
+
</Text>
|
|
388
|
+
</View>
|
|
389
|
+
</Pressable>
|
|
390
|
+
</Animated.View>
|
|
391
|
+
);
|
|
392
|
+
};
|
|
393
|
+
|
|
227
394
|
const ToastRow: React.FC<{
|
|
228
395
|
item: ToastItem;
|
|
229
396
|
onPress: () => void;
|
|
@@ -300,6 +467,37 @@ const styles = StyleSheet.create({
|
|
|
300
467
|
zIndex: 10000,
|
|
301
468
|
elevation: 10000,
|
|
302
469
|
},
|
|
470
|
+
banner: {
|
|
471
|
+
position: 'absolute',
|
|
472
|
+
left: 8,
|
|
473
|
+
right: 8,
|
|
474
|
+
zIndex: 10000,
|
|
475
|
+
elevation: 12,
|
|
476
|
+
borderRadius: 16,
|
|
477
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
478
|
+
shadowOpacity: 0.25,
|
|
479
|
+
shadowOffset: { width: 0, height: 6 },
|
|
480
|
+
shadowRadius: 16,
|
|
481
|
+
},
|
|
482
|
+
bannerBody: {
|
|
483
|
+
flexDirection: 'row',
|
|
484
|
+
alignItems: 'center',
|
|
485
|
+
paddingVertical: 10,
|
|
486
|
+
paddingHorizontal: 12,
|
|
487
|
+
},
|
|
488
|
+
bannerText: {
|
|
489
|
+
flex: 1,
|
|
490
|
+
marginLeft: 12,
|
|
491
|
+
},
|
|
492
|
+
bannerTitle: {
|
|
493
|
+
fontWeight: '600',
|
|
494
|
+
fontSize: 15,
|
|
495
|
+
marginBottom: 2,
|
|
496
|
+
},
|
|
497
|
+
bannerSubtitle: {
|
|
498
|
+
fontSize: 13,
|
|
499
|
+
lineHeight: 18,
|
|
500
|
+
},
|
|
303
501
|
toast: {
|
|
304
502
|
flexDirection: 'row',
|
|
305
503
|
alignItems: 'center',
|
|
@@ -40,6 +40,7 @@ import { allRoomPresences } from '../networking/xmpp/allRoomPresences.xmpp';
|
|
|
40
40
|
import { pushSubscriptionService } from '../services/pushSubscriptionService';
|
|
41
41
|
import { store } from '../roomStore';
|
|
42
42
|
import { logout, setStoreClient, setConfig } from '../roomStore/chatSettingsSlice';
|
|
43
|
+
import { logoutService } from '../hooks/useLogout';
|
|
43
44
|
import {
|
|
44
45
|
setLogoutState,
|
|
45
46
|
setVisibleRoom,
|
|
@@ -233,6 +234,22 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config, is
|
|
|
233
234
|
// Reused clients (steps 1+2 above) also need the provider — config
|
|
234
235
|
// may have changed since the singleton was created.
|
|
235
236
|
created.setCredentialsProvider(credentialsProvider);
|
|
237
|
+
// The XMPP password is gone for good (rejected, and the refresh could
|
|
238
|
+
// not produce a working one): end the session the same way the
|
|
239
|
+
// "Sign out" item does, then let the host route to its login.
|
|
240
|
+
created.setAuthLostHandler?.(() => {
|
|
241
|
+
devPushLog('error', 'XMPP password lost — logging out');
|
|
242
|
+
logoutService
|
|
243
|
+
.performLogout()
|
|
244
|
+
.catch(() => {})
|
|
245
|
+
.finally(() => {
|
|
246
|
+
try {
|
|
247
|
+
Promise.resolve(config?.logout?.onAfterLogout?.()).catch(() => {});
|
|
248
|
+
} catch {
|
|
249
|
+
/* host callback */
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
});
|
|
236
253
|
|
|
237
254
|
// Re-join MUC rooms on every 'online'. After a reconnect the new
|
|
238
255
|
// XMPP stream isn't a member of any room, so a sent message gets a
|
|
@@ -925,16 +942,19 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config, is
|
|
|
925
942
|
return () => sub.remove();
|
|
926
943
|
}, [client]);
|
|
927
944
|
|
|
945
|
+
const contextValue = useMemo(
|
|
946
|
+
() => ({
|
|
947
|
+
client,
|
|
948
|
+
providerBootstrapStatus,
|
|
949
|
+
initMode,
|
|
950
|
+
initializeClient,
|
|
951
|
+
setClient,
|
|
952
|
+
}),
|
|
953
|
+
[client, providerBootstrapStatus, initMode, initializeClient, setClient]
|
|
954
|
+
);
|
|
955
|
+
|
|
928
956
|
return (
|
|
929
|
-
<XmppContext.Provider
|
|
930
|
-
value={{
|
|
931
|
-
client,
|
|
932
|
-
providerBootstrapStatus,
|
|
933
|
-
initMode,
|
|
934
|
-
initializeClient,
|
|
935
|
-
setClient,
|
|
936
|
-
}}
|
|
937
|
-
>
|
|
957
|
+
<XmppContext.Provider value={contextValue}>
|
|
938
958
|
{children}
|
|
939
959
|
{/* Lives here, not inside <Chat>: an incoming call-token can arrive
|
|
940
960
|
(and must still ring) while the user is on a different screen of
|
|
@@ -1,4 +1,36 @@
|
|
|
1
1
|
import { ApiRoom, IRoom } from '../types/types';
|
|
2
|
+
import type { LastMessage } from '../types/models/message.model';
|
|
3
|
+
|
|
4
|
+
const mapApiLastMessage = (
|
|
5
|
+
apiLastMessage: ApiRoom['lastMessage'],
|
|
6
|
+
roomJid: string
|
|
7
|
+
): LastMessage | undefined => {
|
|
8
|
+
const body = String(apiLastMessage?.body || '').trim();
|
|
9
|
+
if (!apiLastMessage || !body) {return undefined;}
|
|
10
|
+
const senderName = `${apiLastMessage.senderFirstName || ''} ${
|
|
11
|
+
apiLastMessage.senderLastName || ''
|
|
12
|
+
}`.trim();
|
|
13
|
+
return {
|
|
14
|
+
id: apiLastMessage.messageId || apiLastMessage.stanzaId || '',
|
|
15
|
+
xmppId: apiLastMessage.stanzaId,
|
|
16
|
+
roomJid,
|
|
17
|
+
body,
|
|
18
|
+
date: apiLastMessage.createdAt,
|
|
19
|
+
isDeleted: false,
|
|
20
|
+
user: {
|
|
21
|
+
id: apiLastMessage.fromUserId || apiLastMessage.from || '',
|
|
22
|
+
name: senderName,
|
|
23
|
+
},
|
|
24
|
+
} as LastMessage;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const seedTimestamp = (seed: LastMessage | undefined): number | undefined => {
|
|
28
|
+
if (!seed) {return undefined;}
|
|
29
|
+
const id = Number(seed.id);
|
|
30
|
+
if (Number.isFinite(id) && id > 0) {return id;}
|
|
31
|
+
const ms = seed.date ? new Date(seed.date).getTime() : NaN;
|
|
32
|
+
return Number.isFinite(ms) && ms > 0 ? ms * 1000 : undefined;
|
|
33
|
+
};
|
|
2
34
|
|
|
3
35
|
export const createRoomFromApi = (
|
|
4
36
|
room: ApiRoom,
|
|
@@ -6,10 +38,12 @@ export const createRoomFromApi = (
|
|
|
6
38
|
usersArrayLength: number = 0
|
|
7
39
|
): IRoom | null => {
|
|
8
40
|
try {
|
|
41
|
+
const jid = `${room?.name}@${service}` || '';
|
|
42
|
+
const lastMessage = mapApiLastMessage(room?.lastMessage, jid);
|
|
9
43
|
const roomData: IRoom = {
|
|
10
44
|
...room,
|
|
11
45
|
id: (room as any)?._id || '',
|
|
12
|
-
jid
|
|
46
|
+
jid,
|
|
13
47
|
name: room?.title || '',
|
|
14
48
|
title: room?.title || '',
|
|
15
49
|
usersCnt: Number(room?.members?.length || usersArrayLength + 1),
|
|
@@ -19,6 +53,8 @@ export const createRoomFromApi = (
|
|
|
19
53
|
icon: room?.picture !== 'none' ? room?.picture : null,
|
|
20
54
|
unreadMessages: 0,
|
|
21
55
|
lastViewedTimestamp: 0,
|
|
56
|
+
lastMessage,
|
|
57
|
+
lastMessageTimestamp: seedTimestamp(lastMessage),
|
|
22
58
|
};
|
|
23
59
|
return roomData;
|
|
24
60
|
} catch (error) {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { EMOJI_CATEGORIES, type EmojiEntry } from '../assets/emojiData';
|
|
2
|
+
|
|
3
|
+
let byId: Map<string, EmojiEntry> | null = null;
|
|
4
|
+
const index = (): Map<string, EmojiEntry> => {
|
|
5
|
+
if (!byId) {
|
|
6
|
+
byId = new Map();
|
|
7
|
+
for (const category of EMOJI_CATEGORIES) {
|
|
8
|
+
for (const entry of category.emojis) {byId.set(entry[0], entry);}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return byId;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const getEmojiNativeById = (id: string): string =>
|
|
15
|
+
index().get(id)?.[1] ?? id;
|
|
16
|
+
|
|
17
|
+
export const getEmojiIdByNative = (native: string): string | undefined => {
|
|
18
|
+
for (const [id, entry] of index()) {
|
|
19
|
+
if (entry[1] === native) {return id;}
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const DEFAULT_QUICK_REACTIONS = ['+1', 'heart', 'joy', 'open_mouth', 'cry', 'pray'];
|
|
25
|
+
|
|
26
|
+
export const searchEmojis = (query: string, limit = 60): EmojiEntry[] => {
|
|
27
|
+
const q = query.trim().toLowerCase();
|
|
28
|
+
if (!q) {return [];}
|
|
29
|
+
const out: EmojiEntry[] = [];
|
|
30
|
+
for (const category of EMOJI_CATEGORIES) {
|
|
31
|
+
for (const entry of category.emojis) {
|
|
32
|
+
if (entry[0].includes(q) || entry[2].includes(q)) {
|
|
33
|
+
out.push(entry);
|
|
34
|
+
if (out.length >= limit) {return out;}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// expo-haptics is an OPTIONAL peer, probed the same way as expo-blur: the JS
|
|
2
|
+
// package alone is not enough, the native module has to be in the binary,
|
|
3
|
+
// otherwise the call throws. Without it the gesture simply has no feedback.
|
|
4
|
+
let Haptics: any = null;
|
|
5
|
+
try {
|
|
6
|
+
const { requireNativeModule } = require('expo-modules-core');
|
|
7
|
+
requireNativeModule('ExpoHaptics');
|
|
8
|
+
Haptics = require('expo-haptics');
|
|
9
|
+
} catch {
|
|
10
|
+
Haptics = null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Short tap, the messenger "I got your long-press" feedback. Never throws. */
|
|
14
|
+
export const hapticTap = () => {
|
|
15
|
+
if (!Haptics) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
Haptics.impactAsync?.(Haptics.ImpactFeedbackStyle?.Medium).catch?.(() => {});
|
|
19
|
+
};
|
|
@@ -15,6 +15,7 @@ interface HistoryPreloadSchedulerOptions {
|
|
|
15
15
|
pageSize?: number;
|
|
16
16
|
retryLimit?: number;
|
|
17
17
|
roomLimit?: number;
|
|
18
|
+
firstWave?: number;
|
|
18
19
|
selectedRoomJid?: string | null;
|
|
19
20
|
defaultRoomJids?: string[];
|
|
20
21
|
forceReload?: boolean;
|
|
@@ -29,21 +30,15 @@ interface QueueItem {
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
const DEFAULT_CONCURRENCY = 3;
|
|
32
|
-
|
|
33
|
-
// also means more id-overlap with cache, so the merge in
|
|
34
|
-
// `mergeHistoryIntoCache` rarely has to fall back to clear-and-replace.
|
|
35
|
-
const DEFAULT_PAGE_SIZE = 20;
|
|
33
|
+
const DEFAULT_PAGE_SIZE = 10;
|
|
36
34
|
const DEFAULT_RETRY_LIMIT = 2;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
// than `pageSize` — `useUnread()` only ever sees what's been loaded. Page
|
|
40
|
-
// further back, up to this many extra fetches, until we either find the
|
|
41
|
-
// boundary or give up (`unreadCapped` then stays true so callers at least
|
|
42
|
-
// know the count is a floor, not exact). Customer-reported #34.
|
|
43
|
-
const MAX_UNREAD_CATCHUP_PAGES = 8;
|
|
35
|
+
const DEFAULT_FIRST_WAVE = 30;
|
|
36
|
+
const BACKGROUND_BATCH_PAUSE_MS = 400;
|
|
44
37
|
|
|
45
38
|
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
46
39
|
|
|
40
|
+
declare const __DEV__: boolean | undefined;
|
|
41
|
+
|
|
47
42
|
const messageTimestamp = (m: IMessage): number => {
|
|
48
43
|
if (!m) {return 0;}
|
|
49
44
|
const t = (m as any).timestamp;
|
|
@@ -54,8 +49,12 @@ const messageTimestamp = (m: IMessage): number => {
|
|
|
54
49
|
};
|
|
55
50
|
|
|
56
51
|
const getRoomLastActivityScore = (room: IRoom): number => {
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
const fromMessages = room?.messages?.length
|
|
53
|
+
? room.messages.reduce((max, m) => Math.max(max, messageTimestamp(m)), 0)
|
|
54
|
+
: 0;
|
|
55
|
+
const fromStamp = Number(room?.lastMessageTimestamp) || 0;
|
|
56
|
+
const fromSeed = Number(room?.lastMessage?.id) || 0;
|
|
57
|
+
return Math.max(fromMessages, fromStamp, fromSeed);
|
|
59
58
|
};
|
|
60
59
|
|
|
61
60
|
const computeUnreadCapped = (
|
|
@@ -73,7 +72,10 @@ const computeUnreadCapped = (
|
|
|
73
72
|
if (countable.length < pageSize) {return false;}
|
|
74
73
|
|
|
75
74
|
const lastViewed = Number(room.lastViewedTimestamp) || 0;
|
|
76
|
-
|
|
75
|
+
// No read marker yet (the private-store markers often land after the
|
|
76
|
+
// first preload page): the count is unknown, not "more than a page".
|
|
77
|
+
// Claiming capped here showed "10+" on rooms with a single unread.
|
|
78
|
+
if (lastViewed <= 0) {return false;}
|
|
77
79
|
|
|
78
80
|
const oldestTs = countable.reduce<number>((minTs, m) => {
|
|
79
81
|
const ts = messageTimestamp(m);
|
|
@@ -116,6 +118,7 @@ export const runHistoryPreloadScheduler = async (
|
|
|
116
118
|
pageSize = DEFAULT_PAGE_SIZE,
|
|
117
119
|
retryLimit = DEFAULT_RETRY_LIMIT,
|
|
118
120
|
roomLimit,
|
|
121
|
+
firstWave = DEFAULT_FIRST_WAVE,
|
|
119
122
|
selectedRoomJid = null,
|
|
120
123
|
defaultRoomJids = [],
|
|
121
124
|
forceReload = false,
|
|
@@ -123,6 +126,12 @@ export const runHistoryPreloadScheduler = async (
|
|
|
123
126
|
|
|
124
127
|
if (signal?.aborted) {return;}
|
|
125
128
|
|
|
129
|
+
const startedAt = Date.now();
|
|
130
|
+
let firstWaveLogged = false;
|
|
131
|
+
const devLog = (msg: string) => {
|
|
132
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {console.log(msg);}
|
|
133
|
+
};
|
|
134
|
+
|
|
126
135
|
store.dispatch(setUnreadSyncing(true));
|
|
127
136
|
try {
|
|
128
137
|
|
|
@@ -153,6 +162,10 @@ export const runHistoryPreloadScheduler = async (
|
|
|
153
162
|
|
|
154
163
|
const inFlightByRoom = new Map<string, Promise<void>>();
|
|
155
164
|
let consecutiveErrorCount = 0;
|
|
165
|
+
let started = 0;
|
|
166
|
+
devLog(
|
|
167
|
+
`[preload] ${queue.length} rooms queued, first wave ${Math.min(firstWave, queue.length)}, page ${pageSize}`
|
|
168
|
+
);
|
|
156
169
|
|
|
157
170
|
while (queue.length > 0) {
|
|
158
171
|
if (signal?.aborted) {return;}
|
|
@@ -176,6 +189,12 @@ export const runHistoryPreloadScheduler = async (
|
|
|
176
189
|
}
|
|
177
190
|
|
|
178
191
|
const batch = readyItems.slice(0, Math.max(1, concurrency));
|
|
192
|
+
const inBackgroundWave = started >= firstWave;
|
|
193
|
+
if (inBackgroundWave && !firstWaveLogged) {
|
|
194
|
+
firstWaveLogged = true;
|
|
195
|
+
devLog(`[preload] first wave done in ${Date.now() - startedAt}ms`);
|
|
196
|
+
}
|
|
197
|
+
started += batch.length;
|
|
179
198
|
|
|
180
199
|
// Snapshot each room's historyPreloadState BEFORE marking the
|
|
181
200
|
// batch as 'loading'. Without this snapshot the per-task
|
|
@@ -229,6 +248,7 @@ export const runHistoryPreloadScheduler = async (
|
|
|
229
248
|
coalesceRoom: true,
|
|
230
249
|
skipIfPreloaded: !forceReload,
|
|
231
250
|
source: 'background',
|
|
251
|
+
selfApplied: true,
|
|
232
252
|
}
|
|
233
253
|
);
|
|
234
254
|
if (signal?.aborted) {return;}
|
|
@@ -237,59 +257,7 @@ export const runHistoryPreloadScheduler = async (
|
|
|
237
257
|
}
|
|
238
258
|
|
|
239
259
|
const nextRoom = store.getState().rooms.rooms[item.jid];
|
|
240
|
-
|
|
241
|
-
const lastViewed = Number(nextRoom?.lastViewedTimestamp) || 0;
|
|
242
|
-
|
|
243
|
-
// The first page was entirely unread — keep paging older until
|
|
244
|
-
// we find a message at/before `lastViewedTimestamp` (the true
|
|
245
|
-
// boundary) or run out of catch-up budget, so the count isn't
|
|
246
|
-
// silently truncated at `pageSize`.
|
|
247
|
-
if (lastViewed > 0 && computeUnreadCapped(nextRoom, combined, pageSize)) {
|
|
248
|
-
let lastPageLen = combined.length;
|
|
249
|
-
let extraPages = 0;
|
|
250
|
-
while (
|
|
251
|
-
extraPages < MAX_UNREAD_CATCHUP_PAGES &&
|
|
252
|
-
lastPageLen >= pageSize &&
|
|
253
|
-
!signal?.aborted
|
|
254
|
-
) {
|
|
255
|
-
const oldestId = combined.reduce<number | null>((min, m) => {
|
|
256
|
-
const idNum = Number((m as any)?.id);
|
|
257
|
-
if (!Number.isFinite(idNum)) {return min;}
|
|
258
|
-
return min === null || idNum < min ? idNum : min;
|
|
259
|
-
}, null);
|
|
260
|
-
if (oldestId === null) {break;}
|
|
261
|
-
|
|
262
|
-
let older: IMessage[] | undefined;
|
|
263
|
-
try {
|
|
264
|
-
older = await client.getHistoryStanza(
|
|
265
|
-
item.jid,
|
|
266
|
-
pageSize,
|
|
267
|
-
oldestId,
|
|
268
|
-
undefined,
|
|
269
|
-
{
|
|
270
|
-
coalesceRoom: true,
|
|
271
|
-
skipIfPreloaded: !forceReload,
|
|
272
|
-
source: 'background',
|
|
273
|
-
}
|
|
274
|
-
);
|
|
275
|
-
} catch {
|
|
276
|
-
break;
|
|
277
|
-
}
|
|
278
|
-
if (!older || !older.length) {break;}
|
|
279
|
-
|
|
280
|
-
combined = [...older, ...combined];
|
|
281
|
-
lastPageLen = older.length;
|
|
282
|
-
extraPages++;
|
|
283
|
-
|
|
284
|
-
const oldestTs = combined.reduce<number>((minTs, m) => {
|
|
285
|
-
const ts = messageTimestamp(m);
|
|
286
|
-
return ts > 0 ? Math.min(minTs, ts) : minTs;
|
|
287
|
-
}, Number.MAX_SAFE_INTEGER);
|
|
288
|
-
if (oldestTs !== Number.MAX_SAFE_INTEGER && oldestTs <= lastViewed) {
|
|
289
|
-
break;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
260
|
+
const combined: IMessage[] = fetchedMessages || [];
|
|
293
261
|
|
|
294
262
|
// `mergeHistoryIntoCache` (roomsSlice) caps whatever we dispatch
|
|
295
263
|
// here to the newest RUNTIME_MESSAGE_LIMIT messages before it
|
|
@@ -353,10 +321,10 @@ export const runHistoryPreloadScheduler = async (
|
|
|
353
321
|
})
|
|
354
322
|
);
|
|
355
323
|
|
|
356
|
-
|
|
357
|
-
await new Promise((r) => setTimeout(r, 0));
|
|
324
|
+
await sleep(inBackgroundWave ? BACKGROUND_BATCH_PAUSE_MS : 0);
|
|
358
325
|
}
|
|
359
326
|
} finally {
|
|
327
|
+
devLog(`[preload] finished in ${Date.now() - startedAt}ms`);
|
|
360
328
|
store.dispatch(setUnreadSyncing(false));
|
|
361
329
|
}
|
|
362
330
|
};
|