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