@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
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import React, { useMemo, useRef, useState } from 'react';
|
|
1
|
+
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import {
|
|
3
|
+
Animated,
|
|
3
4
|
View,
|
|
4
5
|
Pressable,
|
|
5
6
|
StyleSheet,
|
|
@@ -14,6 +15,7 @@ import { Avatar } from './Avatar';
|
|
|
14
15
|
import MessageInteractions from './MessageInteractions';
|
|
15
16
|
import { BottomReplyContainer } from './BottomReplyContainer';
|
|
16
17
|
import { MessageReply } from './MessageReply';
|
|
18
|
+
import { parseMessageReference } from '../../helpers/parseMessageReference';
|
|
17
19
|
import { DeletedMessage } from './DeletedMessage';
|
|
18
20
|
import {
|
|
19
21
|
setActiveModal,
|
|
@@ -38,7 +40,10 @@ import { DoubleTick } from '../../assets/icons';
|
|
|
38
40
|
import { useXmppClient } from '../../context/xmppProvider';
|
|
39
41
|
import { useSendMessage } from '../../hooks/useSendMessage';
|
|
40
42
|
import { MessageReaction } from './MessageReaction';
|
|
41
|
-
import {
|
|
43
|
+
import { EmojiPickerSheet } from './EmojiPickerSheet';
|
|
44
|
+
import { reactionsEnabled } from '../../helpers/reactionsConfig';
|
|
45
|
+
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
46
|
+
import { hapticTap } from '../../helpers/haptics';
|
|
42
47
|
import { useTheme } from '../../hooks/useTheme';
|
|
43
48
|
|
|
44
49
|
const CustomMessageContainer = styled.View<{ isUser: boolean; reply?: number }>`
|
|
@@ -141,6 +146,20 @@ const CustomTimestampRow = styled.View<{media: boolean}>`
|
|
|
141
146
|
padding-right: ${({media}) => media ? '10px': 0};
|
|
142
147
|
`;
|
|
143
148
|
|
|
149
|
+
const clockCache = new Map<string, string>();
|
|
150
|
+
const formatClock = (date: string | Date): string => {
|
|
151
|
+
const key = String(date);
|
|
152
|
+
const cached = clockCache.get(key);
|
|
153
|
+
if (cached !== undefined) {return cached;}
|
|
154
|
+
const d = new Date(date);
|
|
155
|
+
const label = Number.isNaN(d.getTime())
|
|
156
|
+
? ''
|
|
157
|
+
: `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
|
|
158
|
+
if (clockCache.size > 5000) {clockCache.clear();}
|
|
159
|
+
clockCache.set(key, label);
|
|
160
|
+
return label;
|
|
161
|
+
};
|
|
162
|
+
|
|
144
163
|
const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
|
|
145
164
|
const dispatch = useDispatch();
|
|
146
165
|
const { client } = useXmppClient();
|
|
@@ -222,10 +241,7 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
|
|
|
222
241
|
const { retryMessage } = useSendMessage();
|
|
223
242
|
|
|
224
243
|
const [isPressed, setIsPressed] = useState(false);
|
|
225
|
-
|
|
226
|
-
if (__DEV__ && message.id && !(globalThis as any).__loggedMsg?.[message.id]) {
|
|
227
|
-
((globalThis as any).__loggedMsg ||= {})[message.id] = true;
|
|
228
|
-
}
|
|
244
|
+
const [emojiPickerOpen, setEmojiPickerOpen] = useState(false);
|
|
229
245
|
|
|
230
246
|
const [contextMenuPosition, setContextMenuPosition] = useState<{
|
|
231
247
|
left: number;
|
|
@@ -243,15 +259,20 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
|
|
|
243
259
|
dispatch(setSelectedUser(user));
|
|
244
260
|
};
|
|
245
261
|
|
|
262
|
+
const replyRef = parseMessageReference(message);
|
|
263
|
+
|
|
246
264
|
const handleReplyMessage = () => {
|
|
247
265
|
dispatch(setEditAction({ isEdit: false }));
|
|
248
266
|
|
|
249
|
-
|
|
250
|
-
|
|
267
|
+
// An in-channel reply opens its PARENT's thread.
|
|
268
|
+
const parent = !isReply ? parseMessageReference(message) : null;
|
|
269
|
+
if (parent) {
|
|
251
270
|
dispatch(
|
|
252
|
-
setActiveMessage({
|
|
271
|
+
setActiveMessage({
|
|
272
|
+
id: parent.id,
|
|
273
|
+
chatJID: parent.roomJid || message.roomJid,
|
|
274
|
+
})
|
|
253
275
|
);
|
|
254
|
-
|
|
255
276
|
return setIsPressed(false);
|
|
256
277
|
}
|
|
257
278
|
|
|
@@ -290,37 +311,13 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
|
|
|
290
311
|
// };
|
|
291
312
|
|
|
292
313
|
const handleReactionMessage = (emoji: string) => {
|
|
293
|
-
if (!
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
if (
|
|
302
|
-
message.reaction &&
|
|
303
|
-
message.reaction[user.xmppUsername || ''] &&
|
|
304
|
-
message.reaction[user.xmppUsername || '']?.emoji.includes(emoji)
|
|
305
|
-
) {
|
|
306
|
-
const filterEmoji = message.reaction[user.xmppUsername || '']?.emoji.filter(
|
|
307
|
-
(reaction: any) => reaction !== emoji
|
|
308
|
-
);
|
|
309
|
-
|
|
310
|
-
return client?.sendMessageReactionStanza(
|
|
311
|
-
message.id,
|
|
312
|
-
message.roomJid,
|
|
313
|
-
filterEmoji,
|
|
314
|
-
`${user.firstName} ${user.lastName}` as any
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
client?.sendMessageReactionStanza(
|
|
319
|
-
message.id,
|
|
320
|
-
message.roomJid,
|
|
321
|
-
[...(message.reaction[user.xmppUsername || '']?.emoji || []), emoji],
|
|
322
|
-
`${user.firstName} ${user.lastName}` as any
|
|
323
|
-
);
|
|
314
|
+
if (!reactionsEnabled(config) || config?.disableInteractions) {return;}
|
|
315
|
+
const sender = { firstName: user.firstName, lastName: user.lastName };
|
|
316
|
+
const own = message.reaction?.[user.xmppUsername || '']?.emoji || [];
|
|
317
|
+
const next = own.includes(emoji)
|
|
318
|
+
? own.filter((reaction) => reaction !== emoji)
|
|
319
|
+
: [...own, emoji];
|
|
320
|
+
client?.sendMessageReactionStanza(message.id, message.roomJid, next, sender);
|
|
324
321
|
};
|
|
325
322
|
|
|
326
323
|
// Long-press → capture the bubble's on-screen bounding box. The actual
|
|
@@ -363,6 +360,38 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
|
|
|
363
360
|
setIsPressed(true);
|
|
364
361
|
};
|
|
365
362
|
|
|
363
|
+
// Long-press is a NATIVE recognizer over the whole bubble column, not the
|
|
364
|
+
// Pressable's JS timer: the timer was lost whenever an inner responder
|
|
365
|
+
// (link text, media, reply quote, reactions) took the touch first, or the
|
|
366
|
+
// list stole it on the slightest drift — "works every other time". The
|
|
367
|
+
// recognizer fires wherever the finger lands, and once it does the touch
|
|
368
|
+
// is cancelled for everything underneath, so no tap handler fires on top.
|
|
369
|
+
const longPress = useMemo(
|
|
370
|
+
() =>
|
|
371
|
+
Gesture.LongPress()
|
|
372
|
+
.runOnJS(true)
|
|
373
|
+
.minDuration(350)
|
|
374
|
+
.maxDistance(12)
|
|
375
|
+
.enabled(!config?.disableInteractions)
|
|
376
|
+
.onStart(() => {
|
|
377
|
+
hapticTap();
|
|
378
|
+
handleLongPress();
|
|
379
|
+
}),
|
|
380
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
381
|
+
[config?.disableInteractions]
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
// The bubble lifts a touch while its menu is open, and settles back.
|
|
385
|
+
const lift = useRef(new Animated.Value(1)).current;
|
|
386
|
+
useEffect(() => {
|
|
387
|
+
Animated.spring(lift, {
|
|
388
|
+
toValue: isPressed ? 1.03 : 1,
|
|
389
|
+
useNativeDriver: true,
|
|
390
|
+
speed: 30,
|
|
391
|
+
bounciness: 6,
|
|
392
|
+
}).start();
|
|
393
|
+
}, [isPressed, lift]);
|
|
394
|
+
|
|
366
395
|
// Body text size/weight is set on the parser's leaf <Text>s — the markdown
|
|
367
396
|
// wraps content in <View>s which break Text-style inheritance, so the bubble
|
|
368
397
|
// wrapper's fontSize alone never reached the actual text.
|
|
@@ -383,13 +412,19 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
|
|
|
383
412
|
const bodyToRender = showInlineTranslation
|
|
384
413
|
? translationDisplay.displayText
|
|
385
414
|
: message.body;
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
415
|
+
const textFilter = config?.messageTextFilter;
|
|
416
|
+
const messageText = useMemo(
|
|
417
|
+
() =>
|
|
418
|
+
textFilter?.enabled
|
|
419
|
+
? parseMessageBody(
|
|
420
|
+
textFilter.filterFunction(bodyToRender),
|
|
421
|
+
bodyTextStyle,
|
|
422
|
+
theme
|
|
423
|
+
)
|
|
424
|
+
: parseMessageBody(bodyToRender, bodyTextStyle, theme),
|
|
425
|
+
[textFilter, bodyToRender, bodyTextStyle, theme]
|
|
426
|
+
);
|
|
427
|
+
const timeLabel = useMemo(() => formatClock(message.date), [message.date]);
|
|
393
428
|
|
|
394
429
|
const isFailed = failedIdSet.has(message.id);
|
|
395
430
|
const isPending =
|
|
@@ -408,12 +443,8 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
|
|
|
408
443
|
styles.customMessageContainer,
|
|
409
444
|
{
|
|
410
445
|
justifyContent: isUser ? 'flex-end' : 'flex-start',
|
|
411
|
-
marginBottom:
|
|
412
|
-
? 20 : 0,
|
|
446
|
+
marginBottom: 0,
|
|
413
447
|
},
|
|
414
|
-
isPressed
|
|
415
|
-
? { transform: [{ scale: 1.05 }], paddingRight: 16 }
|
|
416
|
-
: undefined,
|
|
417
448
|
// justify-content: ${({ isUser }) => (isUser ? "flex-end" : "flex-start")},
|
|
418
449
|
// margin-bottom: ${(props) => !!props.reply && "20px"},
|
|
419
450
|
]}
|
|
@@ -441,6 +472,7 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
|
|
|
441
472
|
)}
|
|
442
473
|
</CustomMessagePhotoContainer>
|
|
443
474
|
)}
|
|
475
|
+
<GestureDetector gesture={longPress}>
|
|
444
476
|
<Pressable
|
|
445
477
|
// The Pressable fills the row's content area, so the bubble
|
|
446
478
|
// inside must be aligned to the sender's side — otherwise it
|
|
@@ -462,13 +494,8 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
|
|
|
462
494
|
// fire when onLongPress fires, so opening MessageInteractions via
|
|
463
495
|
// long-press never triggers this dismiss.
|
|
464
496
|
onPress={() => Keyboard.dismiss()}
|
|
465
|
-
// disableInteractions hides the long-press → context menu
|
|
466
|
-
// (delete / edit / reply / react). Mirrors web's config gate.
|
|
467
|
-
onLongPress={
|
|
468
|
-
config?.disableInteractions ? undefined : handleLongPress
|
|
469
|
-
}
|
|
470
|
-
delayLongPress={500}
|
|
471
497
|
>
|
|
498
|
+
<Animated.View style={{ transform: [{ scale: lift }] }}>
|
|
472
499
|
<CustomMessageBubble
|
|
473
500
|
{...({ ref: bubbleRef, collapsable: false } as any)}
|
|
474
501
|
isUser={isUser}
|
|
@@ -488,11 +515,12 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
|
|
|
488
515
|
{senderDisplayName}
|
|
489
516
|
</CustomUserName>
|
|
490
517
|
)}
|
|
491
|
-
{!isReply &&
|
|
518
|
+
{!isReply && !!replyRef?.text && (
|
|
492
519
|
<MessageReply
|
|
493
520
|
handleReplyMessage={handleReplyMessage}
|
|
494
521
|
isUser={isUser}
|
|
495
|
-
text={
|
|
522
|
+
text={replyRef.text}
|
|
523
|
+
userName={replyRef.userName}
|
|
496
524
|
color={theme.primary}
|
|
497
525
|
/>
|
|
498
526
|
)}
|
|
@@ -567,39 +595,45 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
|
|
|
567
595
|
<Text style={[styles.editedText, themedStyles.muted]}>edited</Text>
|
|
568
596
|
)}
|
|
569
597
|
<Text style={[styles.timestampText, themedStyles.muted]}>
|
|
570
|
-
{
|
|
571
|
-
hour: '2-digit',
|
|
572
|
-
minute: '2-digit',
|
|
573
|
-
})}
|
|
598
|
+
{timeLabel}
|
|
574
599
|
</Text>
|
|
575
600
|
{!config?.disableSentLogic && isUser && !isPending && !isFailed && (
|
|
576
601
|
<DoubleTick />
|
|
577
602
|
)}
|
|
578
603
|
</CustomTimestampRow>
|
|
579
604
|
|
|
580
|
-
{message?.reply?.length && message?.reply?.length > 0 ? (
|
|
581
|
-
<BottomReplyContainer
|
|
582
|
-
isUser={isUser}
|
|
583
|
-
onClick={handleReplyMessage}
|
|
584
|
-
reply={message?.reply}
|
|
585
|
-
/>
|
|
586
|
-
) : (
|
|
587
|
-
<View />
|
|
588
|
-
)}
|
|
589
|
-
|
|
590
|
-
<MessageFooter isUser={isUser}>
|
|
591
|
-
|
|
592
|
-
{message.reaction && !config?.disableReactions && (
|
|
593
|
-
<MessageReaction
|
|
594
|
-
reaction={message.reaction}
|
|
595
|
-
changeReaction={handleReactionMessage}
|
|
596
|
-
color={theme.primary}
|
|
597
|
-
userName={`${user.firstName} ${user.lastName}`}
|
|
598
|
-
/>
|
|
599
|
-
)}
|
|
600
|
-
</MessageFooter>
|
|
601
605
|
</CustomMessageBubble>
|
|
606
|
+
{/* One row on the bubble's bottom edge: the thread pill (as on
|
|
607
|
+
web) and the reaction chips side by side. */}
|
|
608
|
+
{((!isReply && (message?.reply?.length || 0) > 0) ||
|
|
609
|
+
(!!message.reaction && reactionsEnabled(config))) && (
|
|
610
|
+
<View
|
|
611
|
+
style={[
|
|
612
|
+
styles.reactionRow,
|
|
613
|
+
isUser ? styles.reactionRowUser : styles.reactionRowOther,
|
|
614
|
+
]}
|
|
615
|
+
>
|
|
616
|
+
{!isReply && (message?.reply?.length || 0) > 0 && (
|
|
617
|
+
<BottomReplyContainer
|
|
618
|
+
isUser={isUser}
|
|
619
|
+
onClick={handleReplyMessage}
|
|
620
|
+
reply={message.reply!}
|
|
621
|
+
/>
|
|
622
|
+
)}
|
|
623
|
+
{message.reaction && reactionsEnabled(config) && (
|
|
624
|
+
<MessageReaction
|
|
625
|
+
reaction={message.reaction}
|
|
626
|
+
changeReaction={handleReactionMessage}
|
|
627
|
+
color={theme.primary}
|
|
628
|
+
userName={`${user.firstName || ''} ${user.lastName || ''}`.trim()}
|
|
629
|
+
interactive={!config?.disableInteractions}
|
|
630
|
+
/>
|
|
631
|
+
)}
|
|
632
|
+
</View>
|
|
633
|
+
)}
|
|
634
|
+
</Animated.View>
|
|
602
635
|
</Pressable>
|
|
636
|
+
</GestureDetector>
|
|
603
637
|
</View>
|
|
604
638
|
{!config?.disableInteractions && isPressed && (
|
|
605
639
|
<MessageInteractions
|
|
@@ -612,13 +646,22 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
|
|
|
612
646
|
handleDeleteMessage={handleDeleteMessage}
|
|
613
647
|
handleEditMessage={handleEditMessage}
|
|
614
648
|
handleReactionMessage={handleReactionMessage}
|
|
649
|
+
onOpenEmojiPicker={() => setEmojiPickerOpen(true)}
|
|
650
|
+
/>
|
|
651
|
+
)}
|
|
652
|
+
{emojiPickerOpen && (
|
|
653
|
+
<EmojiPickerSheet
|
|
654
|
+
visible
|
|
655
|
+
onClose={() => setEmojiPickerOpen(false)}
|
|
656
|
+
onPick={handleReactionMessage}
|
|
615
657
|
/>
|
|
616
658
|
)}
|
|
617
659
|
</View>
|
|
618
660
|
);
|
|
619
661
|
};
|
|
620
662
|
|
|
621
|
-
|
|
663
|
+
const MemoMessage = React.memo(Message);
|
|
664
|
+
export { MemoMessage as Message };
|
|
622
665
|
|
|
623
666
|
const styles = StyleSheet.create({
|
|
624
667
|
customMessageContainer: {
|
|
@@ -627,6 +670,22 @@ const styles = StyleSheet.create({
|
|
|
627
670
|
alignItems: 'flex-end',
|
|
628
671
|
position: 'relative',
|
|
629
672
|
},
|
|
673
|
+
reactionRow: {
|
|
674
|
+
marginTop: -10,
|
|
675
|
+
zIndex: 1,
|
|
676
|
+
flexDirection: 'row',
|
|
677
|
+
flexWrap: 'wrap',
|
|
678
|
+
alignItems: 'center',
|
|
679
|
+
gap: 6,
|
|
680
|
+
},
|
|
681
|
+
reactionRowUser: {
|
|
682
|
+
alignSelf: 'flex-end',
|
|
683
|
+
marginRight: 8,
|
|
684
|
+
},
|
|
685
|
+
reactionRowOther: {
|
|
686
|
+
alignSelf: 'flex-start',
|
|
687
|
+
marginLeft: 8,
|
|
688
|
+
},
|
|
630
689
|
overlay: {
|
|
631
690
|
...StyleSheet.absoluteFill,
|
|
632
691
|
position: 'absolute',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect, useId, useMemo, useState } from 'react';
|
|
1
|
+
import React, { useEffect, useId, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import { Delimeter, MenuItem } from '../ContextMenu/ContextMenuComponents';
|
|
3
3
|
import { useSelector } from 'react-redux';
|
|
4
4
|
import { RootState } from '../../roomStore';
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
} from '../../helpers/constants/MESSAGE_INTERACTIONS';
|
|
9
9
|
import { IMessage } from '../../types/types';
|
|
10
10
|
import {
|
|
11
|
+
Animated,
|
|
11
12
|
Text,
|
|
12
13
|
StyleSheet,
|
|
13
14
|
View,
|
|
@@ -20,6 +21,12 @@ import * as Clipboard from 'expo-clipboard';
|
|
|
20
21
|
import { useToast } from '../../context/ToastContext';
|
|
21
22
|
import { useInteractionsOverlay } from './InteractionsOverlay';
|
|
22
23
|
import { useTheme } from '../../hooks/useTheme';
|
|
24
|
+
import { getEmojiNativeById } from '../../helpers/emoji';
|
|
25
|
+
import {
|
|
26
|
+
quickReactionIds,
|
|
27
|
+
reactionPickerEnabled,
|
|
28
|
+
reactionsEnabled,
|
|
29
|
+
} from '../../helpers/reactionsConfig';
|
|
23
30
|
|
|
24
31
|
interface MessageInteractionsProps {
|
|
25
32
|
isReply?: boolean;
|
|
@@ -36,6 +43,8 @@ interface MessageInteractionsProps {
|
|
|
36
43
|
handleDeleteMessage: () => void;
|
|
37
44
|
handleEditMessage: () => void;
|
|
38
45
|
handleReactionMessage: (id: string) => void;
|
|
46
|
+
/** Opens the full emoji picker (the "+" in the reaction row). */
|
|
47
|
+
onOpenEmojiPicker?: () => void;
|
|
39
48
|
}
|
|
40
49
|
|
|
41
50
|
const MessageInteractions: React.FC<MessageInteractionsProps> = ({
|
|
@@ -47,6 +56,8 @@ const MessageInteractions: React.FC<MessageInteractionsProps> = ({
|
|
|
47
56
|
handleReplyMessage: replyMessage,
|
|
48
57
|
handleDeleteMessage,
|
|
49
58
|
handleEditMessage,
|
|
59
|
+
handleReactionMessage,
|
|
60
|
+
onOpenEmojiPicker,
|
|
50
61
|
}) => {
|
|
51
62
|
const { showToast } = useToast();
|
|
52
63
|
const { present, dismiss, originX, originY } = useInteractionsOverlay();
|
|
@@ -65,6 +76,27 @@ const MessageInteractions: React.FC<MessageInteractionsProps> = ({
|
|
|
65
76
|
);
|
|
66
77
|
|
|
67
78
|
const [menuSize, setMenuSize] = useState({ width: 0, height: 0 });
|
|
79
|
+
const [barSize, setBarSize] = useState({ width: 0, height: 0 });
|
|
80
|
+
const showReactions = reactionsEnabled(config);
|
|
81
|
+
const quickIds = useMemo(() => quickReactionIds(config), [config]);
|
|
82
|
+
const showPicker = reactionPickerEnabled(config) && !!onOpenEmojiPicker;
|
|
83
|
+
|
|
84
|
+
// Entrance: the menu pops out of its anchor (scale + fade) and the chat
|
|
85
|
+
// dims a touch behind it. Started once the menu has been measured and
|
|
86
|
+
// placed — before that it sits invisible at a provisional spot.
|
|
87
|
+
const reveal = useRef(new Animated.Value(0)).current;
|
|
88
|
+
const placed = !!position && !!menuSize.width && !!menuSize.height;
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
if (!placed) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
Animated.spring(reveal, {
|
|
94
|
+
toValue: 1,
|
|
95
|
+
useNativeDriver: true,
|
|
96
|
+
speed: 24,
|
|
97
|
+
bounciness: 7,
|
|
98
|
+
}).start();
|
|
99
|
+
}, [placed, reveal]);
|
|
68
100
|
|
|
69
101
|
// Live keyboard height so the menu knows the REAL space below the message.
|
|
70
102
|
// Without this the position math used the full screen height and happily
|
|
@@ -88,6 +120,13 @@ const MessageInteractions: React.FC<MessageInteractionsProps> = ({
|
|
|
88
120
|
return () => subs.forEach((s) => s.remove());
|
|
89
121
|
}, []);
|
|
90
122
|
|
|
123
|
+
const handleBarLayout = (e: LayoutChangeEvent) => {
|
|
124
|
+
const { width, height } = e.nativeEvent.layout;
|
|
125
|
+
if (width && height && (width !== barSize.width || height !== barSize.height)) {
|
|
126
|
+
setBarSize({ width, height });
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
91
130
|
const handleMenuLayout = (e: LayoutChangeEvent) => {
|
|
92
131
|
const { width, height } = e.nativeEvent.layout;
|
|
93
132
|
if (
|
|
@@ -132,12 +171,15 @@ const MessageInteractions: React.FC<MessageInteractionsProps> = ({
|
|
|
132
171
|
if (!menuSize.width || !menuSize.height) {
|
|
133
172
|
return { top: position.bottom, left: position.left, opacity: 0 };
|
|
134
173
|
}
|
|
174
|
+
const barReserve = showReactions && barSize.height ? barSize.height + 10 : 0;
|
|
135
175
|
|
|
136
176
|
const { width: screenWidth, height: screenHeight } =
|
|
137
177
|
Dimensions.get('window');
|
|
138
178
|
const bottomReserve = (config?.keyboardVerticalOffset ?? 0) + 24;
|
|
139
179
|
const topReserve = 16;
|
|
140
180
|
const sideMargin = 8;
|
|
181
|
+
// Breathing room between the bubble and the menu.
|
|
182
|
+
const gap = 10;
|
|
141
183
|
|
|
142
184
|
// The keyboard occludes the bottom `keyboardHeight` px, so the real
|
|
143
185
|
// visible bottom edge is above it. Measuring space below against this
|
|
@@ -145,14 +187,14 @@ const MessageInteractions: React.FC<MessageInteractionsProps> = ({
|
|
|
145
187
|
// when the keyboard is open and the message sits near the input.
|
|
146
188
|
const visibleBottom = screenHeight - keyboardHeight;
|
|
147
189
|
|
|
148
|
-
const spaceBelow = visibleBottom - position.bottom - bottomReserve;
|
|
149
|
-
const spaceAbove = position.top - topReserve;
|
|
190
|
+
const spaceBelow = visibleBottom - position.bottom - gap - bottomReserve;
|
|
191
|
+
const spaceAbove = position.top - gap - topReserve - barReserve;
|
|
150
192
|
|
|
151
193
|
let top: number;
|
|
152
194
|
if (spaceBelow >= menuSize.height) {
|
|
153
|
-
top = position.bottom;
|
|
195
|
+
top = position.bottom + gap;
|
|
154
196
|
} else if (spaceAbove >= menuSize.height) {
|
|
155
|
-
top = position.top - menuSize.height;
|
|
197
|
+
top = position.top - gap - menuSize.height;
|
|
156
198
|
} else {
|
|
157
199
|
top = Math.max(
|
|
158
200
|
topReserve,
|
|
@@ -171,7 +213,37 @@ const MessageInteractions: React.FC<MessageInteractionsProps> = ({
|
|
|
171
213
|
);
|
|
172
214
|
|
|
173
215
|
return { top, left };
|
|
174
|
-
}, [position, menuSize, config?.keyboardVerticalOffset, keyboardHeight]);
|
|
216
|
+
}, [position, menuSize, barSize.height, showReactions, config?.keyboardVerticalOffset, keyboardHeight]);
|
|
217
|
+
|
|
218
|
+
const barPosition = useMemo(() => {
|
|
219
|
+
if (!position || !showReactions) {return undefined;}
|
|
220
|
+
if (!barSize.width || !barSize.height) {
|
|
221
|
+
return { top: position.top, left: position.left, opacity: 0 };
|
|
222
|
+
}
|
|
223
|
+
const { width: screenWidth } = Dimensions.get('window');
|
|
224
|
+
const sideMargin = 8;
|
|
225
|
+
const gap = 8;
|
|
226
|
+
const rightAligned = screenWidth - position.right < position.left;
|
|
227
|
+
let left = rightAligned ? position.right - barSize.width : position.left;
|
|
228
|
+
left = Math.max(sideMargin, Math.min(left, screenWidth - barSize.width - sideMargin));
|
|
229
|
+
const above = position.top - gap - barSize.height;
|
|
230
|
+
const menuTop = (memoPosition as { top?: number } | undefined)?.top;
|
|
231
|
+
const top =
|
|
232
|
+
above >= 16
|
|
233
|
+
? above
|
|
234
|
+
: (menuTop ?? position.bottom) + (menuSize.height || 0) + gap;
|
|
235
|
+
return { top, left };
|
|
236
|
+
}, [position, showReactions, barSize, memoPosition, menuSize.height]);
|
|
237
|
+
|
|
238
|
+
const localBarPosition = useMemo(() => {
|
|
239
|
+
if (!barPosition) {return undefined;}
|
|
240
|
+
return {
|
|
241
|
+
...barPosition,
|
|
242
|
+
top: (barPosition as { top: number }).top - originY,
|
|
243
|
+
left: (barPosition as { left: number }).left - originX,
|
|
244
|
+
};
|
|
245
|
+
}, [barPosition, originX, originY]);
|
|
246
|
+
|
|
175
247
|
|
|
176
248
|
// Window coords (memoPosition) → host-local coords. The overlay host
|
|
177
249
|
// sits below the status bar / header, so subtract its measured origin.
|
|
@@ -187,16 +259,106 @@ const MessageInteractions: React.FC<MessageInteractionsProps> = ({
|
|
|
187
259
|
const content =
|
|
188
260
|
config?.disableInteractions || message.isDeleted ? null : (
|
|
189
261
|
<View style={styles.overlayFill}>
|
|
190
|
-
<Pressable style={StyleSheet.absoluteFill} onPress={closeMenu}
|
|
191
|
-
|
|
192
|
-
|
|
262
|
+
<Pressable style={StyleSheet.absoluteFill} onPress={closeMenu}>
|
|
263
|
+
<Animated.View
|
|
264
|
+
pointerEvents="none"
|
|
265
|
+
style={[styles.dim, { opacity: reveal }]}
|
|
266
|
+
/>
|
|
267
|
+
</Pressable>
|
|
268
|
+
{showReactions && (
|
|
269
|
+
<Animated.View
|
|
270
|
+
testID="reaction-bar"
|
|
271
|
+
onLayout={handleBarLayout}
|
|
272
|
+
style={[
|
|
273
|
+
styles.reactionBar,
|
|
274
|
+
{ backgroundColor: theme.surface, shadowColor: theme.shadow },
|
|
275
|
+
localBarPosition,
|
|
276
|
+
placed && barSize.height
|
|
277
|
+
? {
|
|
278
|
+
opacity: reveal,
|
|
279
|
+
transform: [
|
|
280
|
+
{
|
|
281
|
+
scale: reveal.interpolate({
|
|
282
|
+
inputRange: [0, 1],
|
|
283
|
+
outputRange: [0.6, 1],
|
|
284
|
+
}),
|
|
285
|
+
},
|
|
286
|
+
],
|
|
287
|
+
}
|
|
288
|
+
: null,
|
|
289
|
+
]}
|
|
290
|
+
>
|
|
291
|
+
{quickIds.map((id) => (
|
|
292
|
+
<Pressable
|
|
293
|
+
key={id}
|
|
294
|
+
testID={`quick-reaction-${id}`}
|
|
295
|
+
onPress={() => {
|
|
296
|
+
handleReactionMessage(id);
|
|
297
|
+
closeMenu();
|
|
298
|
+
}}
|
|
299
|
+
style={({ pressed }) => [
|
|
300
|
+
styles.reactionItem,
|
|
301
|
+
pressed && { backgroundColor: theme.surfaceHighlight },
|
|
302
|
+
]}
|
|
303
|
+
>
|
|
304
|
+
<Text style={styles.reactionGlyph}>{getEmojiNativeById(id)}</Text>
|
|
305
|
+
</Pressable>
|
|
306
|
+
))}
|
|
307
|
+
{showPicker && (
|
|
308
|
+
<Pressable
|
|
309
|
+
testID="quick-reaction-more"
|
|
310
|
+
onPress={() => {
|
|
311
|
+
closeMenu();
|
|
312
|
+
onOpenEmojiPicker?.();
|
|
313
|
+
}}
|
|
314
|
+
style={({ pressed }) => [
|
|
315
|
+
styles.reactionMore,
|
|
316
|
+
{ backgroundColor: theme.surfaceSecondary },
|
|
317
|
+
pressed && { backgroundColor: theme.surfaceHighlight },
|
|
318
|
+
]}
|
|
319
|
+
>
|
|
320
|
+
<Text style={[styles.reactionMoreText, { color: theme.text }]}>+</Text>
|
|
321
|
+
</Pressable>
|
|
322
|
+
)}
|
|
323
|
+
</Animated.View>
|
|
324
|
+
)}
|
|
325
|
+
<Animated.View
|
|
326
|
+
style={[
|
|
327
|
+
styles.contextMenu,
|
|
328
|
+
themedStyles.menu,
|
|
329
|
+
localPosition,
|
|
330
|
+
placed
|
|
331
|
+
? {
|
|
332
|
+
opacity: reveal,
|
|
333
|
+
transform: [
|
|
334
|
+
{
|
|
335
|
+
scale: reveal.interpolate({
|
|
336
|
+
inputRange: [0, 1],
|
|
337
|
+
outputRange: [0.6, 1],
|
|
338
|
+
}),
|
|
339
|
+
},
|
|
340
|
+
],
|
|
341
|
+
}
|
|
342
|
+
: null,
|
|
343
|
+
]}
|
|
193
344
|
onLayout={handleMenuLayout}
|
|
194
345
|
>
|
|
346
|
+
{!isReply && !config?.disableReplies && (
|
|
347
|
+
<>
|
|
348
|
+
<MenuItem testID="menu-reply" onPress={handleReplyMessage}>
|
|
349
|
+
<Text style={[styles.menuText, themedStyles.text]}>
|
|
350
|
+
{MESSAGE_INTERACTIONS.REPLY}
|
|
351
|
+
</Text>
|
|
352
|
+
<MESSAGE_INTERACTIONS_ICONS.REPLY color={theme.text} />
|
|
353
|
+
</MenuItem>
|
|
354
|
+
<Delimeter />
|
|
355
|
+
</>
|
|
356
|
+
)}
|
|
195
357
|
<MenuItem onPress={() => handleCopyMessage(message.body!)}>
|
|
196
358
|
<Text style={[styles.menuText, themedStyles.text]}>
|
|
197
359
|
{MESSAGE_INTERACTIONS.COPY}
|
|
198
360
|
</Text>
|
|
199
|
-
<MESSAGE_INTERACTIONS_ICONS.COPY />
|
|
361
|
+
<MESSAGE_INTERACTIONS_ICONS.COPY color={theme.text} />
|
|
200
362
|
</MenuItem>
|
|
201
363
|
{isUser && (
|
|
202
364
|
<>
|
|
@@ -209,7 +371,7 @@ const MessageInteractions: React.FC<MessageInteractionsProps> = ({
|
|
|
209
371
|
<Text style={[styles.menuText, themedStyles.text]}>
|
|
210
372
|
{MESSAGE_INTERACTIONS.EDIT}
|
|
211
373
|
</Text>
|
|
212
|
-
<MESSAGE_INTERACTIONS_ICONS.EDIT />
|
|
374
|
+
<MESSAGE_INTERACTIONS_ICONS.EDIT color={theme.text} />
|
|
213
375
|
</MenuItem>
|
|
214
376
|
</>
|
|
215
377
|
)}
|
|
@@ -222,7 +384,7 @@ const MessageInteractions: React.FC<MessageInteractionsProps> = ({
|
|
|
222
384
|
</MenuItem>
|
|
223
385
|
</>
|
|
224
386
|
)}
|
|
225
|
-
</View>
|
|
387
|
+
</Animated.View>
|
|
226
388
|
</View>
|
|
227
389
|
);
|
|
228
390
|
|
|
@@ -251,6 +413,45 @@ const styles = StyleSheet.create({
|
|
|
251
413
|
...StyleSheet.absoluteFill,
|
|
252
414
|
backgroundColor: 'transparent',
|
|
253
415
|
},
|
|
416
|
+
dim: {
|
|
417
|
+
...StyleSheet.absoluteFill,
|
|
418
|
+
backgroundColor: 'rgba(0,0,0,0.18)',
|
|
419
|
+
},
|
|
420
|
+
reactionBar: {
|
|
421
|
+
position: 'absolute',
|
|
422
|
+
flexDirection: 'row',
|
|
423
|
+
alignItems: 'center',
|
|
424
|
+
paddingHorizontal: 6,
|
|
425
|
+
paddingVertical: 4,
|
|
426
|
+
borderRadius: 28,
|
|
427
|
+
shadowOffset: { width: 0, height: 2 },
|
|
428
|
+
shadowOpacity: 0.18,
|
|
429
|
+
shadowRadius: 8,
|
|
430
|
+
elevation: 6,
|
|
431
|
+
},
|
|
432
|
+
reactionItem: {
|
|
433
|
+
width: 40,
|
|
434
|
+
height: 40,
|
|
435
|
+
borderRadius: 20,
|
|
436
|
+
alignItems: 'center',
|
|
437
|
+
justifyContent: 'center',
|
|
438
|
+
},
|
|
439
|
+
reactionGlyph: {
|
|
440
|
+
fontSize: 26,
|
|
441
|
+
},
|
|
442
|
+
reactionMore: {
|
|
443
|
+
width: 34,
|
|
444
|
+
height: 34,
|
|
445
|
+
borderRadius: 17,
|
|
446
|
+
alignItems: 'center',
|
|
447
|
+
justifyContent: 'center',
|
|
448
|
+
marginLeft: 2,
|
|
449
|
+
},
|
|
450
|
+
reactionMoreText: {
|
|
451
|
+
fontSize: 22,
|
|
452
|
+
lineHeight: 24,
|
|
453
|
+
fontWeight: '400',
|
|
454
|
+
},
|
|
254
455
|
contextMenu: {
|
|
255
456
|
position: 'absolute',
|
|
256
457
|
backgroundColor: 'white',
|