@ethora/chat-component-rn 26.8.4 → 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.
Files changed (417) hide show
  1. package/README.md +55 -0
  2. package/lib/commonjs/assets/emojiData.js +40 -0
  3. package/lib/commonjs/assets/emojiData.js.map +1 -0
  4. package/lib/commonjs/assets/icons.js +1 -1
  5. package/lib/commonjs/assets/icons.js.map +1 -1
  6. package/lib/commonjs/components/Header/HeaderRoomList.js +12 -7
  7. package/lib/commonjs/components/Header/HeaderRoomList.js.map +1 -1
  8. package/lib/commonjs/components/MainComponents/ChatHeader.js +7 -9
  9. package/lib/commonjs/components/MainComponents/ChatHeader.js.map +1 -1
  10. package/lib/commonjs/components/MainComponents/ChatRoom.js +47 -26
  11. package/lib/commonjs/components/MainComponents/ChatRoom.js.map +1 -1
  12. package/lib/commonjs/components/MainComponents/ChatWrapper.js +58 -21
  13. package/lib/commonjs/components/MainComponents/ChatWrapper.js.map +1 -1
  14. package/lib/commonjs/components/MainComponents/KeyboardInputDock.js +40 -0
  15. package/lib/commonjs/components/MainComponents/KeyboardInputDock.js.map +1 -0
  16. package/lib/commonjs/components/MainComponents/MessageContainer.js +4 -12
  17. package/lib/commonjs/components/MainComponents/MessageContainer.js.map +1 -1
  18. package/lib/commonjs/components/MainComponents/MessageList.js +110 -35
  19. package/lib/commonjs/components/MainComponents/MessageList.js.map +1 -1
  20. package/lib/commonjs/components/MainComponents/RoomList.js +32 -29
  21. package/lib/commonjs/components/MainComponents/RoomList.js.map +1 -1
  22. package/lib/commonjs/components/MainComponents/RoomStack.js +270 -0
  23. package/lib/commonjs/components/MainComponents/RoomStack.js.map +1 -0
  24. package/lib/commonjs/components/MainComponents/SystemMessage.js +6 -2
  25. package/lib/commonjs/components/MainComponents/SystemMessage.js.map +1 -1
  26. package/lib/commonjs/components/MessageBubble/Avatar.js +5 -1
  27. package/lib/commonjs/components/MessageBubble/Avatar.js.map +1 -1
  28. package/lib/commonjs/components/MessageBubble/BottomReplyContainer.js +17 -21
  29. package/lib/commonjs/components/MessageBubble/BottomReplyContainer.js.map +1 -1
  30. package/lib/commonjs/components/MessageBubble/EmojiPickerSheet.js +447 -0
  31. package/lib/commonjs/components/MessageBubble/EmojiPickerSheet.js.map +1 -0
  32. package/lib/commonjs/components/MessageBubble/Message.js +200 -132
  33. package/lib/commonjs/components/MessageBubble/Message.js.map +1 -1
  34. package/lib/commonjs/components/MessageBubble/MessageInteractions.js +213 -12
  35. package/lib/commonjs/components/MessageBubble/MessageInteractions.js.map +1 -1
  36. package/lib/commonjs/components/MessageBubble/MessageReaction.js +113 -94
  37. package/lib/commonjs/components/MessageBubble/MessageReaction.js.map +1 -1
  38. package/lib/commonjs/components/MessageBubble/MessageReply.js +66 -24
  39. package/lib/commonjs/components/MessageBubble/MessageReply.js.map +1 -1
  40. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js +6 -1
  41. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  42. package/lib/commonjs/components/Modals/Modal/Modal.js +24 -0
  43. package/lib/commonjs/components/Modals/Modal/Modal.js.map +1 -1
  44. package/lib/commonjs/components/Modals/Modal/SwipeBackLayer.js +178 -0
  45. package/lib/commonjs/components/Modals/Modal/SwipeBackLayer.js.map +1 -0
  46. package/lib/commonjs/components/Modals/ProfileHeader/ProfileHeader.js +28 -10
  47. package/lib/commonjs/components/Modals/ProfileHeader/ProfileHeader.js.map +1 -1
  48. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js +9 -3
  49. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  50. package/lib/commonjs/components/RoomComponents/ChatRoomItem.js +5 -4
  51. package/lib/commonjs/components/RoomComponents/ChatRoomItem.js.map +1 -1
  52. package/lib/commonjs/components/RoomComponents/styled/LastMessageEmoji.js +8 -9
  53. package/lib/commonjs/components/RoomComponents/styled/LastMessageEmoji.js.map +1 -1
  54. package/lib/commonjs/components/Thread/ThreadWrapper.js +265 -152
  55. package/lib/commonjs/components/Thread/ThreadWrapper.js.map +1 -1
  56. package/lib/commonjs/components/styled/DateLabel.js +6 -3
  57. package/lib/commonjs/components/styled/DateLabel.js.map +1 -1
  58. package/lib/commonjs/context/MessageNotificationContext.js +188 -9
  59. package/lib/commonjs/context/MessageNotificationContext.js.map +1 -1
  60. package/lib/commonjs/context/xmppProvider.js +22 -7
  61. package/lib/commonjs/context/xmppProvider.js.map +1 -1
  62. package/lib/commonjs/helpers/createRoomFromApi.js +36 -2
  63. package/lib/commonjs/helpers/createRoomFromApi.js.map +1 -1
  64. package/lib/commonjs/helpers/emoji.js +51 -0
  65. package/lib/commonjs/helpers/emoji.js.map +1 -0
  66. package/lib/commonjs/helpers/haptics.js +29 -0
  67. package/lib/commonjs/helpers/haptics.js.map +1 -0
  68. package/lib/commonjs/helpers/historyPreloadScheduler.js +32 -66
  69. package/lib/commonjs/helpers/historyPreloadScheduler.js.map +1 -1
  70. package/lib/commonjs/helpers/insertMessageWithDelimiter.js +21 -12
  71. package/lib/commonjs/helpers/insertMessageWithDelimiter.js.map +1 -1
  72. package/lib/commonjs/helpers/keyboardLayout.js +23 -1
  73. package/lib/commonjs/helpers/keyboardLayout.js.map +1 -1
  74. package/lib/commonjs/helpers/mamReactions.js +57 -0
  75. package/lib/commonjs/helpers/mamReactions.js.map +1 -0
  76. package/lib/commonjs/helpers/parseMessageReference.js +42 -0
  77. package/lib/commonjs/helpers/parseMessageReference.js.map +1 -0
  78. package/lib/commonjs/helpers/reactionsConfig.js +17 -0
  79. package/lib/commonjs/helpers/reactionsConfig.js.map +1 -0
  80. package/lib/commonjs/helpers/updateMessagesTillLast.js +17 -2
  81. package/lib/commonjs/helpers/updateMessagesTillLast.js.map +1 -1
  82. package/lib/commonjs/hooks/useHeapSender.js +3 -1
  83. package/lib/commonjs/hooks/useHeapSender.js.map +1 -1
  84. package/lib/commonjs/hooks/useMessageHeapState.js +3 -2
  85. package/lib/commonjs/hooks/useMessageHeapState.js.map +1 -1
  86. package/lib/commonjs/hooks/useRoomInitialization.js +16 -7
  87. package/lib/commonjs/hooks/useRoomInitialization.js.map +1 -1
  88. package/lib/commonjs/hooks/useSendMessage.js +14 -8
  89. package/lib/commonjs/hooks/useSendMessage.js.map +1 -1
  90. package/lib/commonjs/hooks/useUnreadMessagesCounter.js +1 -1
  91. package/lib/commonjs/hooks/useUnreadMessagesCounter.js.map +1 -1
  92. package/lib/commonjs/networking/stanzaHandlers.js +106 -50
  93. package/lib/commonjs/networking/stanzaHandlers.js.map +1 -1
  94. package/lib/commonjs/networking/xmpp/getHistory.xmpp.js +29 -74
  95. package/lib/commonjs/networking/xmpp/getHistory.xmpp.js.map +1 -1
  96. package/lib/commonjs/networking/xmpp/handleStanzas.xmpp.js +4 -0
  97. package/lib/commonjs/networking/xmpp/handleStanzas.xmpp.js.map +1 -1
  98. package/lib/commonjs/networking/xmpp/mamRouter.js +186 -0
  99. package/lib/commonjs/networking/xmpp/mamRouter.js.map +1 -0
  100. package/lib/commonjs/networking/xmpp/presenceInRoom.xmpp.js +41 -4
  101. package/lib/commonjs/networking/xmpp/presenceInRoom.xmpp.js.map +1 -1
  102. package/lib/commonjs/networking/xmppClient.js +98 -23
  103. package/lib/commonjs/networking/xmppClient.js.map +1 -1
  104. package/lib/commonjs/roomStore/Middleware/newMessageMidlleware.js +29 -34
  105. package/lib/commonjs/roomStore/Middleware/newMessageMidlleware.js.map +1 -1
  106. package/lib/commonjs/roomStore/Middleware/perfMiddleware.js +51 -0
  107. package/lib/commonjs/roomStore/Middleware/perfMiddleware.js.map +1 -0
  108. package/lib/commonjs/roomStore/Middleware/reactionsMiddleware.js +66 -43
  109. package/lib/commonjs/roomStore/Middleware/reactionsMiddleware.js.map +1 -1
  110. package/lib/commonjs/roomStore/Middleware/unreadMidlleware.js +88 -68
  111. package/lib/commonjs/roomStore/Middleware/unreadMidlleware.js.map +1 -1
  112. package/lib/commonjs/roomStore/index.js +18 -14
  113. package/lib/commonjs/roomStore/index.js.map +1 -1
  114. package/lib/commonjs/roomStore/persistBackend.js +96 -0
  115. package/lib/commonjs/roomStore/persistBackend.js.map +1 -0
  116. package/lib/commonjs/roomStore/persistence.js +245 -50
  117. package/lib/commonjs/roomStore/persistence.js.map +1 -1
  118. package/lib/commonjs/roomStore/roomsSlice.js +282 -136
  119. package/lib/commonjs/roomStore/roomsSlice.js.map +1 -1
  120. package/lib/commonjs/theme/theme.js +3 -0
  121. package/lib/commonjs/theme/theme.js.map +1 -1
  122. package/lib/commonjs/utils/devLogger.js +14 -0
  123. package/lib/commonjs/utils/devLogger.js.map +1 -1
  124. package/lib/commonjs/version.js +1 -1
  125. package/lib/module/assets/emojiData.js +36 -0
  126. package/lib/module/assets/emojiData.js.map +1 -0
  127. package/lib/module/assets/icons.js +1 -1
  128. package/lib/module/assets/icons.js.map +1 -1
  129. package/lib/module/components/Header/HeaderRoomList.js +12 -7
  130. package/lib/module/components/Header/HeaderRoomList.js.map +1 -1
  131. package/lib/module/components/MainComponents/ChatHeader.js +8 -10
  132. package/lib/module/components/MainComponents/ChatHeader.js.map +1 -1
  133. package/lib/module/components/MainComponents/ChatRoom.js +49 -28
  134. package/lib/module/components/MainComponents/ChatRoom.js.map +1 -1
  135. package/lib/module/components/MainComponents/ChatWrapper.js +60 -23
  136. package/lib/module/components/MainComponents/ChatWrapper.js.map +1 -1
  137. package/lib/module/components/MainComponents/KeyboardInputDock.js +33 -0
  138. package/lib/module/components/MainComponents/KeyboardInputDock.js.map +1 -0
  139. package/lib/module/components/MainComponents/MessageContainer.js +7 -14
  140. package/lib/module/components/MainComponents/MessageContainer.js.map +1 -1
  141. package/lib/module/components/MainComponents/MessageList.js +110 -35
  142. package/lib/module/components/MainComponents/MessageList.js.map +1 -1
  143. package/lib/module/components/MainComponents/RoomList.js +32 -29
  144. package/lib/module/components/MainComponents/RoomList.js.map +1 -1
  145. package/lib/module/components/MainComponents/RoomStack.js +266 -0
  146. package/lib/module/components/MainComponents/RoomStack.js.map +1 -0
  147. package/lib/module/components/MainComponents/SystemMessage.js +6 -2
  148. package/lib/module/components/MainComponents/SystemMessage.js.map +1 -1
  149. package/lib/module/components/MessageBubble/Avatar.js +5 -1
  150. package/lib/module/components/MessageBubble/Avatar.js.map +1 -1
  151. package/lib/module/components/MessageBubble/BottomReplyContainer.js +18 -22
  152. package/lib/module/components/MessageBubble/BottomReplyContainer.js.map +1 -1
  153. package/lib/module/components/MessageBubble/EmojiPickerSheet.js +438 -0
  154. package/lib/module/components/MessageBubble/EmojiPickerSheet.js.map +1 -0
  155. package/lib/module/components/MessageBubble/Message.js +203 -134
  156. package/lib/module/components/MessageBubble/Message.js.map +1 -1
  157. package/lib/module/components/MessageBubble/MessageInteractions.js +215 -14
  158. package/lib/module/components/MessageBubble/MessageInteractions.js.map +1 -1
  159. package/lib/module/components/MessageBubble/MessageReaction.js +115 -94
  160. package/lib/module/components/MessageBubble/MessageReaction.js.map +1 -1
  161. package/lib/module/components/MessageBubble/MessageReply.js +65 -24
  162. package/lib/module/components/MessageBubble/MessageReply.js.map +1 -1
  163. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js +6 -1
  164. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  165. package/lib/module/components/Modals/Modal/Modal.js +25 -1
  166. package/lib/module/components/Modals/Modal/Modal.js.map +1 -1
  167. package/lib/module/components/Modals/Modal/SwipeBackLayer.js +172 -0
  168. package/lib/module/components/Modals/Modal/SwipeBackLayer.js.map +1 -0
  169. package/lib/module/components/Modals/ProfileHeader/ProfileHeader.js +29 -11
  170. package/lib/module/components/Modals/ProfileHeader/ProfileHeader.js.map +1 -1
  171. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js +10 -4
  172. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  173. package/lib/module/components/RoomComponents/ChatRoomItem.js +5 -4
  174. package/lib/module/components/RoomComponents/ChatRoomItem.js.map +1 -1
  175. package/lib/module/components/RoomComponents/styled/LastMessageEmoji.js +8 -9
  176. package/lib/module/components/RoomComponents/styled/LastMessageEmoji.js.map +1 -1
  177. package/lib/module/components/Thread/ThreadWrapper.js +264 -152
  178. package/lib/module/components/Thread/ThreadWrapper.js.map +1 -1
  179. package/lib/module/components/styled/DateLabel.js +6 -3
  180. package/lib/module/components/styled/DateLabel.js.map +1 -1
  181. package/lib/module/context/MessageNotificationContext.js +190 -11
  182. package/lib/module/context/MessageNotificationContext.js.map +1 -1
  183. package/lib/module/context/xmppProvider.js +22 -7
  184. package/lib/module/context/xmppProvider.js.map +1 -1
  185. package/lib/module/helpers/createRoomFromApi.js +36 -2
  186. package/lib/module/helpers/createRoomFromApi.js.map +1 -1
  187. package/lib/module/helpers/emoji.js +44 -0
  188. package/lib/module/helpers/emoji.js.map +1 -0
  189. package/lib/module/helpers/haptics.js +24 -0
  190. package/lib/module/helpers/haptics.js.map +1 -0
  191. package/lib/module/helpers/historyPreloadScheduler.js +32 -66
  192. package/lib/module/helpers/historyPreloadScheduler.js.map +1 -1
  193. package/lib/module/helpers/insertMessageWithDelimiter.js +21 -12
  194. package/lib/module/helpers/insertMessageWithDelimiter.js.map +1 -1
  195. package/lib/module/helpers/keyboardLayout.js +20 -0
  196. package/lib/module/helpers/keyboardLayout.js.map +1 -1
  197. package/lib/module/helpers/mamReactions.js +51 -0
  198. package/lib/module/helpers/mamReactions.js.map +1 -0
  199. package/lib/module/helpers/parseMessageReference.js +37 -0
  200. package/lib/module/helpers/parseMessageReference.js.map +1 -0
  201. package/lib/module/helpers/reactionsConfig.js +10 -0
  202. package/lib/module/helpers/reactionsConfig.js.map +1 -0
  203. package/lib/module/helpers/updateMessagesTillLast.js +17 -2
  204. package/lib/module/helpers/updateMessagesTillLast.js.map +1 -1
  205. package/lib/module/hooks/useHeapSender.js +3 -1
  206. package/lib/module/hooks/useHeapSender.js.map +1 -1
  207. package/lib/module/hooks/useMessageHeapState.js +3 -2
  208. package/lib/module/hooks/useMessageHeapState.js.map +1 -1
  209. package/lib/module/hooks/useRoomInitialization.js +17 -8
  210. package/lib/module/hooks/useRoomInitialization.js.map +1 -1
  211. package/lib/module/hooks/useSendMessage.js +14 -8
  212. package/lib/module/hooks/useSendMessage.js.map +1 -1
  213. package/lib/module/hooks/useUnreadMessagesCounter.js +1 -1
  214. package/lib/module/hooks/useUnreadMessagesCounter.js.map +1 -1
  215. package/lib/module/networking/stanzaHandlers.js +107 -51
  216. package/lib/module/networking/stanzaHandlers.js.map +1 -1
  217. package/lib/module/networking/xmpp/getHistory.xmpp.js +29 -74
  218. package/lib/module/networking/xmpp/getHistory.xmpp.js.map +1 -1
  219. package/lib/module/networking/xmpp/handleStanzas.xmpp.js +4 -0
  220. package/lib/module/networking/xmpp/handleStanzas.xmpp.js.map +1 -1
  221. package/lib/module/networking/xmpp/mamRouter.js +175 -0
  222. package/lib/module/networking/xmpp/mamRouter.js.map +1 -0
  223. package/lib/module/networking/xmpp/presenceInRoom.xmpp.js +38 -2
  224. package/lib/module/networking/xmpp/presenceInRoom.xmpp.js.map +1 -1
  225. package/lib/module/networking/xmppClient.js +99 -24
  226. package/lib/module/networking/xmppClient.js.map +1 -1
  227. package/lib/module/roomStore/Middleware/newMessageMidlleware.js +29 -34
  228. package/lib/module/roomStore/Middleware/newMessageMidlleware.js.map +1 -1
  229. package/lib/module/roomStore/Middleware/perfMiddleware.js +45 -0
  230. package/lib/module/roomStore/Middleware/perfMiddleware.js.map +1 -0
  231. package/lib/module/roomStore/Middleware/reactionsMiddleware.js +66 -43
  232. package/lib/module/roomStore/Middleware/reactionsMiddleware.js.map +1 -1
  233. package/lib/module/roomStore/Middleware/unreadMidlleware.js +89 -69
  234. package/lib/module/roomStore/Middleware/unreadMidlleware.js.map +1 -1
  235. package/lib/module/roomStore/index.js +19 -15
  236. package/lib/module/roomStore/index.js.map +1 -1
  237. package/lib/module/roomStore/persistBackend.js +89 -0
  238. package/lib/module/roomStore/persistBackend.js.map +1 -0
  239. package/lib/module/roomStore/persistence.js +245 -48
  240. package/lib/module/roomStore/persistence.js.map +1 -1
  241. package/lib/module/roomStore/roomsSlice.js +275 -134
  242. package/lib/module/roomStore/roomsSlice.js.map +1 -1
  243. package/lib/module/theme/theme.js +3 -0
  244. package/lib/module/theme/theme.js.map +1 -1
  245. package/lib/module/utils/devLogger.js +13 -0
  246. package/lib/module/utils/devLogger.js.map +1 -1
  247. package/lib/module/version.js +1 -1
  248. package/lib/typescript/assets/emojiData.d.ts +8 -0
  249. package/lib/typescript/assets/emojiData.d.ts.map +1 -0
  250. package/lib/typescript/components/Header/HeaderRoomList.d.ts.map +1 -1
  251. package/lib/typescript/components/MainComponents/ChatHeader.d.ts.map +1 -1
  252. package/lib/typescript/components/MainComponents/ChatRoom.d.ts.map +1 -1
  253. package/lib/typescript/components/MainComponents/ChatWrapper.d.ts.map +1 -1
  254. package/lib/typescript/components/MainComponents/KeyboardInputDock.d.ts +16 -0
  255. package/lib/typescript/components/MainComponents/KeyboardInputDock.d.ts.map +1 -0
  256. package/lib/typescript/components/MainComponents/MessageContainer.d.ts +2 -2
  257. package/lib/typescript/components/MainComponents/MessageContainer.d.ts.map +1 -1
  258. package/lib/typescript/components/MainComponents/MessageList.d.ts.map +1 -1
  259. package/lib/typescript/components/MainComponents/RoomList.d.ts.map +1 -1
  260. package/lib/typescript/components/MainComponents/RoomStack.d.ts +42 -0
  261. package/lib/typescript/components/MainComponents/RoomStack.d.ts.map +1 -0
  262. package/lib/typescript/components/MainComponents/SystemMessage.d.ts.map +1 -1
  263. package/lib/typescript/components/MessageBubble/Avatar.d.ts +2 -0
  264. package/lib/typescript/components/MessageBubble/Avatar.d.ts.map +1 -1
  265. package/lib/typescript/components/MessageBubble/BottomReplyContainer.d.ts.map +1 -1
  266. package/lib/typescript/components/MessageBubble/EmojiPickerSheet.d.ts +11 -0
  267. package/lib/typescript/components/MessageBubble/EmojiPickerSheet.d.ts.map +1 -0
  268. package/lib/typescript/components/MessageBubble/Message.d.ts +2 -2
  269. package/lib/typescript/components/MessageBubble/Message.d.ts.map +1 -1
  270. package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts +2 -0
  271. package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts.map +1 -1
  272. package/lib/typescript/components/MessageBubble/MessageReaction.d.ts +1 -0
  273. package/lib/typescript/components/MessageBubble/MessageReaction.d.ts.map +1 -1
  274. package/lib/typescript/components/MessageBubble/MessageReply.d.ts +7 -7
  275. package/lib/typescript/components/MessageBubble/MessageReply.d.ts.map +1 -1
  276. package/lib/typescript/components/Modals/ChatProfileModal/ChatProfileModal.d.ts.map +1 -1
  277. package/lib/typescript/components/Modals/Modal/Modal.d.ts.map +1 -1
  278. package/lib/typescript/components/Modals/Modal/SwipeBackLayer.d.ts +16 -0
  279. package/lib/typescript/components/Modals/Modal/SwipeBackLayer.d.ts.map +1 -0
  280. package/lib/typescript/components/Modals/ProfileHeader/ProfileHeader.d.ts.map +1 -1
  281. package/lib/typescript/components/Modals/UserProfileModal/UserProfileModal.d.ts.map +1 -1
  282. package/lib/typescript/components/RoomComponents/ChatRoomItem.d.ts +2 -2
  283. package/lib/typescript/components/RoomComponents/ChatRoomItem.d.ts.map +1 -1
  284. package/lib/typescript/components/RoomComponents/styled/LastMessageEmoji.d.ts +3 -6
  285. package/lib/typescript/components/RoomComponents/styled/LastMessageEmoji.d.ts.map +1 -1
  286. package/lib/typescript/components/Thread/ThreadWrapper.d.ts +7 -1
  287. package/lib/typescript/components/Thread/ThreadWrapper.d.ts.map +1 -1
  288. package/lib/typescript/components/styled/DateLabel.d.ts +1 -0
  289. package/lib/typescript/components/styled/DateLabel.d.ts.map +1 -1
  290. package/lib/typescript/context/MessageNotificationContext.d.ts.map +1 -1
  291. package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
  292. package/lib/typescript/helpers/createRoomFromApi.d.ts.map +1 -1
  293. package/lib/typescript/helpers/emoji.d.ts +6 -0
  294. package/lib/typescript/helpers/emoji.d.ts.map +1 -0
  295. package/lib/typescript/helpers/haptics.d.ts +3 -0
  296. package/lib/typescript/helpers/haptics.d.ts.map +1 -0
  297. package/lib/typescript/helpers/historyPreloadScheduler.d.ts +1 -0
  298. package/lib/typescript/helpers/historyPreloadScheduler.d.ts.map +1 -1
  299. package/lib/typescript/helpers/insertMessageWithDelimiter.d.ts.map +1 -1
  300. package/lib/typescript/helpers/keyboardLayout.d.ts +6 -0
  301. package/lib/typescript/helpers/keyboardLayout.d.ts.map +1 -1
  302. package/lib/typescript/helpers/mamReactions.d.ts +13 -0
  303. package/lib/typescript/helpers/mamReactions.d.ts.map +1 -0
  304. package/lib/typescript/helpers/parseMessageReference.d.ts +16 -0
  305. package/lib/typescript/helpers/parseMessageReference.d.ts.map +1 -0
  306. package/lib/typescript/helpers/reactionsConfig.d.ts +5 -0
  307. package/lib/typescript/helpers/reactionsConfig.d.ts.map +1 -0
  308. package/lib/typescript/helpers/updateMessagesTillLast.d.ts.map +1 -1
  309. package/lib/typescript/hooks/useHeapSender.d.ts.map +1 -1
  310. package/lib/typescript/hooks/useMessageHeapState.d.ts.map +1 -1
  311. package/lib/typescript/hooks/useRoomInitialization.d.ts +2 -2
  312. package/lib/typescript/hooks/useRoomInitialization.d.ts.map +1 -1
  313. package/lib/typescript/hooks/useSendMessage.d.ts.map +1 -1
  314. package/lib/typescript/networking/stanzaHandlers.d.ts +4 -2
  315. package/lib/typescript/networking/stanzaHandlers.d.ts.map +1 -1
  316. package/lib/typescript/networking/xmpp/getHistory.xmpp.d.ts +3 -1
  317. package/lib/typescript/networking/xmpp/getHistory.xmpp.d.ts.map +1 -1
  318. package/lib/typescript/networking/xmpp/handleStanzas.xmpp.d.ts.map +1 -1
  319. package/lib/typescript/networking/xmpp/mamRouter.d.ts +23 -0
  320. package/lib/typescript/networking/xmpp/mamRouter.d.ts.map +1 -0
  321. package/lib/typescript/networking/xmpp/presenceInRoom.xmpp.d.ts +1 -0
  322. package/lib/typescript/networking/xmpp/presenceInRoom.xmpp.d.ts.map +1 -1
  323. package/lib/typescript/networking/xmppClient.d.ts +22 -1
  324. package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
  325. package/lib/typescript/roomStore/Middleware/newMessageMidlleware.d.ts.map +1 -1
  326. package/lib/typescript/roomStore/Middleware/perfMiddleware.d.ts +4 -0
  327. package/lib/typescript/roomStore/Middleware/perfMiddleware.d.ts.map +1 -0
  328. package/lib/typescript/roomStore/Middleware/reactionsMiddleware.d.ts +9 -0
  329. package/lib/typescript/roomStore/Middleware/reactionsMiddleware.d.ts.map +1 -1
  330. package/lib/typescript/roomStore/Middleware/unreadMidlleware.d.ts.map +1 -1
  331. package/lib/typescript/roomStore/index.d.ts.map +1 -1
  332. package/lib/typescript/roomStore/persistBackend.d.ts +11 -0
  333. package/lib/typescript/roomStore/persistBackend.d.ts.map +1 -0
  334. package/lib/typescript/roomStore/persistence.d.ts +5 -6
  335. package/lib/typescript/roomStore/persistence.d.ts.map +1 -1
  336. package/lib/typescript/roomStore/roomsSlice.d.ts +41 -1
  337. package/lib/typescript/roomStore/roomsSlice.d.ts.map +1 -1
  338. package/lib/typescript/theme/theme.d.ts +4 -1
  339. package/lib/typescript/theme/theme.d.ts.map +1 -1
  340. package/lib/typescript/types/models/message.model.d.ts +4 -0
  341. package/lib/typescript/types/models/message.model.d.ts.map +1 -1
  342. package/lib/typescript/types/models/room.model.d.ts +16 -0
  343. package/lib/typescript/types/models/room.model.d.ts.map +1 -1
  344. package/lib/typescript/types/models/xmpp.model.d.ts +6 -1
  345. package/lib/typescript/types/models/xmpp.model.d.ts.map +1 -1
  346. package/lib/typescript/types/types.d.ts +15 -3
  347. package/lib/typescript/types/types.d.ts.map +1 -1
  348. package/lib/typescript/utils/devLogger.d.ts +8 -0
  349. package/lib/typescript/utils/devLogger.d.ts.map +1 -1
  350. package/lib/typescript/version.d.ts +1 -1
  351. package/package.json +10 -1
  352. package/src/assets/emojiData.ts +2146 -0
  353. package/src/assets/icons.tsx +5 -5
  354. package/src/components/Header/HeaderRoomList.tsx +7 -5
  355. package/src/components/MainComponents/ChatHeader.tsx +16 -7
  356. package/src/components/MainComponents/ChatRoom.tsx +64 -32
  357. package/src/components/MainComponents/ChatWrapper.tsx +73 -21
  358. package/src/components/MainComponents/KeyboardInputDock.tsx +47 -0
  359. package/src/components/MainComponents/MessageContainer.tsx +7 -15
  360. package/src/components/MainComponents/MessageList.tsx +96 -43
  361. package/src/components/MainComponents/RoomList.tsx +28 -29
  362. package/src/components/MainComponents/RoomStack.tsx +309 -0
  363. package/src/components/MainComponents/SystemMessage.tsx +6 -2
  364. package/src/components/MessageBubble/Avatar.tsx +7 -1
  365. package/src/components/MessageBubble/BottomReplyContainer.tsx +19 -21
  366. package/src/components/MessageBubble/EmojiPickerSheet.tsx +468 -0
  367. package/src/components/MessageBubble/Message.tsx +146 -87
  368. package/src/components/MessageBubble/MessageInteractions.tsx +213 -12
  369. package/src/components/MessageBubble/MessageReaction.tsx +120 -96
  370. package/src/components/MessageBubble/MessageReply.tsx +65 -32
  371. package/src/components/Modals/ChatProfileModal/ChatProfileModal.tsx +4 -0
  372. package/src/components/Modals/Modal/Modal.tsx +32 -1
  373. package/src/components/Modals/Modal/SwipeBackLayer.tsx +221 -0
  374. package/src/components/Modals/ProfileHeader/ProfileHeader.tsx +29 -8
  375. package/src/components/Modals/UserProfileModal/UserProfileModal.tsx +6 -3
  376. package/src/components/RoomComponents/ChatRoomItem.tsx +11 -4
  377. package/src/components/RoomComponents/styled/LastMessageEmoji.tsx +7 -8
  378. package/src/components/Thread/ThreadWrapper.tsx +274 -198
  379. package/src/components/styled/DateLabel.tsx +7 -3
  380. package/src/context/MessageNotificationContext.tsx +205 -7
  381. package/src/context/xmppProvider.tsx +29 -9
  382. package/src/helpers/createRoomFromApi.tsx +37 -1
  383. package/src/helpers/emoji.ts +39 -0
  384. package/src/helpers/haptics.ts +19 -0
  385. package/src/helpers/historyPreloadScheduler.ts +37 -69
  386. package/src/helpers/insertMessageWithDelimiter.ts +23 -16
  387. package/src/helpers/keyboardLayout.ts +26 -0
  388. package/src/helpers/mamReactions.ts +59 -0
  389. package/src/helpers/parseMessageReference.ts +38 -0
  390. package/src/helpers/reactionsConfig.ts +13 -0
  391. package/src/helpers/updateMessagesTillLast.tsx +14 -2
  392. package/src/hooks/useHeapSender.tsx +3 -2
  393. package/src/hooks/useMessageHeapState.tsx +6 -2
  394. package/src/hooks/useRoomInitialization.tsx +32 -15
  395. package/src/hooks/useSendMessage.tsx +18 -11
  396. package/src/hooks/useUnreadMessagesCounter.ts +1 -1
  397. package/src/networking/stanzaHandlers.ts +116 -60
  398. package/src/networking/xmpp/getHistory.xmpp.ts +44 -106
  399. package/src/networking/xmpp/handleStanzas.xmpp.ts +2 -0
  400. package/src/networking/xmpp/mamRouter.ts +176 -0
  401. package/src/networking/xmpp/presenceInRoom.xmpp.ts +46 -3
  402. package/src/networking/xmppClient.ts +105 -20
  403. package/src/roomStore/Middleware/newMessageMidlleware.tsx +35 -38
  404. package/src/roomStore/Middleware/perfMiddleware.ts +50 -0
  405. package/src/roomStore/Middleware/reactionsMiddleware.tsx +53 -52
  406. package/src/roomStore/Middleware/unreadMidlleware.tsx +92 -80
  407. package/src/roomStore/index.ts +28 -27
  408. package/src/roomStore/persistBackend.ts +96 -0
  409. package/src/roomStore/persistence.ts +280 -73
  410. package/src/roomStore/roomsSlice.ts +304 -155
  411. package/src/theme/theme.ts +10 -1
  412. package/src/types/models/message.model.ts +4 -0
  413. package/src/types/models/room.model.ts +17 -0
  414. package/src/types/models/xmpp.model.ts +4 -2
  415. package/src/types/types.ts +15 -3
  416. package/src/utils/devLogger.ts +16 -0
  417. 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 { MessageFooter } from '../styled/StyledComponents';
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
- if (!isReply && message.mainMessage) {
250
- const messageCore = JSON.parse(message.mainMessage);
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({ id: messageCore.id, chatJID: messageCore.roomJid })
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 (!message.reaction) {
294
- return client?.sendMessageReactionStanza(
295
- message.id,
296
- message.roomJid,
297
- [emoji],
298
- `${user.firstName} ${user.lastName}` as any
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 messageText = config?.messageTextFilter?.enabled
387
- ? parseMessageBody(
388
- config?.messageTextFilter.filterFunction(bodyToRender),
389
- bodyTextStyle,
390
- theme
391
- )
392
- : parseMessageBody(bodyToRender, bodyTextStyle, theme);
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: !!message?.reply?.length || message?.reaction && !!Object.keys(message?.reaction)?.length
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 && message.mainMessage && (
518
+ {!isReply && !!replyRef?.text && (
492
519
  <MessageReply
493
520
  handleReplyMessage={handleReplyMessage}
494
521
  isUser={isUser}
495
- text={JSON.parse(message.mainMessage).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
- {new Date(message.date).toLocaleTimeString([], {
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
- export { Message };
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
- <View
192
- style={[styles.contextMenu, themedStyles.menu, localPosition]}
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',