@ethora/chat-component-rn 26.9.1 → 26.10.0

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 (760) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +19 -0
  3. package/lib/commonjs/assets/icons.js +2 -2
  4. package/lib/commonjs/assets/icons.js.map +1 -1
  5. package/lib/commonjs/components/Header/HeaderRoomList.js +22 -6
  6. package/lib/commonjs/components/Header/HeaderRoomList.js.map +1 -1
  7. package/lib/commonjs/components/InputComponents/Search.js +38 -1
  8. package/lib/commonjs/components/InputComponents/Search.js.map +1 -1
  9. package/lib/commonjs/components/MainComponents/ArchivedMessageCard.js +166 -0
  10. package/lib/commonjs/components/MainComponents/ArchivedMessageCard.js.map +1 -0
  11. package/lib/commonjs/components/MainComponents/ChatHeader.js +22 -5
  12. package/lib/commonjs/components/MainComponents/ChatHeader.js.map +1 -1
  13. package/lib/commonjs/components/MainComponents/ChatRoom.js +56 -25
  14. package/lib/commonjs/components/MainComponents/ChatRoom.js.map +1 -1
  15. package/lib/commonjs/components/MainComponents/ChatWrapper.js +31 -1
  16. package/lib/commonjs/components/MainComponents/ChatWrapper.js.map +1 -1
  17. package/lib/commonjs/components/MainComponents/MediaMessage.js +18 -3
  18. package/lib/commonjs/components/MainComponents/MediaMessage.js.map +1 -1
  19. package/lib/commonjs/components/MainComponents/MessageList.js +739 -42
  20. package/lib/commonjs/components/MainComponents/MessageList.js.map +1 -1
  21. package/lib/commonjs/components/MainComponents/MessageSearchButton.js +58 -0
  22. package/lib/commonjs/components/MainComponents/MessageSearchButton.js.map +1 -0
  23. package/lib/commonjs/components/MainComponents/ReduxWrapper.js +15 -6
  24. package/lib/commonjs/components/MainComponents/ReduxWrapper.js.map +1 -1
  25. package/lib/commonjs/components/MainComponents/RoomList.js +110 -1
  26. package/lib/commonjs/components/MainComponents/RoomList.js.map +1 -1
  27. package/lib/commonjs/components/Menu/HeaderRoomListMenu.js +14 -3
  28. package/lib/commonjs/components/Menu/HeaderRoomListMenu.js.map +1 -1
  29. package/lib/commonjs/components/MessageBubble/BubbleHighlight.js +112 -0
  30. package/lib/commonjs/components/MessageBubble/BubbleHighlight.js.map +1 -0
  31. package/lib/commonjs/components/MessageBubble/Message.js +169 -125
  32. package/lib/commonjs/components/MessageBubble/Message.js.map +1 -1
  33. package/lib/commonjs/components/MessageBubble/MessageInteractions.js +98 -78
  34. package/lib/commonjs/components/MessageBubble/MessageInteractions.js.map +1 -1
  35. package/lib/commonjs/components/MessageBubble/messageMenuLayout.js +50 -0
  36. package/lib/commonjs/components/MessageBubble/messageMenuLayout.js.map +1 -0
  37. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js +73 -37
  38. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  39. package/lib/commonjs/components/Modals/MessageSearchModal/MessageHitResults.js +253 -0
  40. package/lib/commonjs/components/Modals/MessageSearchModal/MessageHitResults.js.map +1 -0
  41. package/lib/commonjs/components/Modals/MessageSearchModal/MessageSearchModal.js +488 -0
  42. package/lib/commonjs/components/Modals/MessageSearchModal/MessageSearchModal.js.map +1 -0
  43. package/lib/commonjs/components/Modals/MessageSearchModal/openHit.js +51 -0
  44. package/lib/commonjs/components/Modals/MessageSearchModal/openHit.js.map +1 -0
  45. package/lib/commonjs/components/Modals/MessageSearchModal/resolveSender.js +47 -0
  46. package/lib/commonjs/components/Modals/MessageSearchModal/resolveSender.js.map +1 -0
  47. package/lib/commonjs/components/Modals/MessageSearchModal/searchFilters.js +83 -0
  48. package/lib/commonjs/components/Modals/MessageSearchModal/searchFilters.js.map +1 -0
  49. package/lib/commonjs/components/Modals/MessageSearchModal/snippet.js +61 -0
  50. package/lib/commonjs/components/Modals/MessageSearchModal/snippet.js.map +1 -0
  51. package/lib/commonjs/components/Modals/MessageSearchModal/useMessageSearch.js +136 -0
  52. package/lib/commonjs/components/Modals/MessageSearchModal/useMessageSearch.js.map +1 -0
  53. package/lib/commonjs/components/Modals/PublicChatsModal/PublicChatsModal.js +306 -0
  54. package/lib/commonjs/components/Modals/PublicChatsModal/PublicChatsModal.js.map +1 -0
  55. package/lib/commonjs/components/Modals/PublicChatsModal/publicChatsLogic.js +36 -0
  56. package/lib/commonjs/components/Modals/PublicChatsModal/publicChatsLogic.js.map +1 -0
  57. package/lib/commonjs/components/Modals/SelectUsersModal/SelectUsersModal.js +8 -1
  58. package/lib/commonjs/components/Modals/SelectUsersModal/SelectUsersModal.js.map +1 -1
  59. package/lib/commonjs/components/RoomComponents/ChatRoomItem.js +13 -2
  60. package/lib/commonjs/components/RoomComponents/ChatRoomItem.js.map +1 -1
  61. package/lib/commonjs/components/RoomComponents/SwipeableRoomRow.js +38 -6
  62. package/lib/commonjs/components/RoomComponents/SwipeableRoomRow.js.map +1 -1
  63. package/lib/commonjs/components/Thread/ThreadHeader.js +3 -1
  64. package/lib/commonjs/components/Thread/ThreadHeader.js.map +1 -1
  65. package/lib/commonjs/components/Thread/ThreadWrapper.js +90 -10
  66. package/lib/commonjs/components/Thread/ThreadWrapper.js.map +1 -1
  67. package/lib/commonjs/components/styled/AudioDecoderHost.js +338 -0
  68. package/lib/commonjs/components/styled/AudioDecoderHost.js.map +1 -0
  69. package/lib/commonjs/components/styled/AudioMessage.js +493 -420
  70. package/lib/commonjs/components/styled/AudioMessage.js.map +1 -1
  71. package/lib/commonjs/components/styled/SendInput.js +31 -5
  72. package/lib/commonjs/components/styled/SendInput.js.map +1 -1
  73. package/lib/commonjs/context/MessageNotificationContext.js +14 -4
  74. package/lib/commonjs/context/MessageNotificationContext.js.map +1 -1
  75. package/lib/commonjs/context/xmppProvider.js +41 -77
  76. package/lib/commonjs/context/xmppProvider.js.map +1 -1
  77. package/lib/commonjs/helpers/audioWaveform.js +165 -0
  78. package/lib/commonjs/helpers/audioWaveform.js.map +1 -0
  79. package/lib/commonjs/helpers/backgroundJoinSweep.js +36 -0
  80. package/lib/commonjs/helpers/backgroundJoinSweep.js.map +1 -0
  81. package/lib/commonjs/helpers/bubbleHighlight.js +44 -0
  82. package/lib/commonjs/helpers/bubbleHighlight.js.map +1 -0
  83. package/lib/commonjs/helpers/checkUniqueUsers.js +14 -12
  84. package/lib/commonjs/helpers/checkUniqueUsers.js.map +1 -1
  85. package/lib/commonjs/helpers/constants/MODAL_COMPONENTS.js +5 -1
  86. package/lib/commonjs/helpers/constants/MODAL_COMPONENTS.js.map +1 -1
  87. package/lib/commonjs/helpers/constants/MODAL_TYPES.js +3 -1
  88. package/lib/commonjs/helpers/constants/MODAL_TYPES.js.map +1 -1
  89. package/lib/commonjs/helpers/createRoomFromApi.js +40 -3
  90. package/lib/commonjs/helpers/createRoomFromApi.js.map +1 -1
  91. package/lib/commonjs/helpers/dedupeMembers.js +34 -0
  92. package/lib/commonjs/helpers/dedupeMembers.js.map +1 -0
  93. package/lib/commonjs/helpers/ethoraLogger.js +56 -0
  94. package/lib/commonjs/helpers/ethoraLogger.js.map +1 -0
  95. package/lib/commonjs/helpers/getUnnamedUsers.js +9 -4
  96. package/lib/commonjs/helpers/getUnnamedUsers.js.map +1 -1
  97. package/lib/commonjs/helpers/historyPreloadConfig.js +44 -0
  98. package/lib/commonjs/helpers/historyPreloadConfig.js.map +1 -0
  99. package/lib/commonjs/helpers/historyPreloadScheduler.js +372 -137
  100. package/lib/commonjs/helpers/historyPreloadScheduler.js.map +1 -1
  101. package/lib/commonjs/helpers/initRoomsPresence.js +17 -5
  102. package/lib/commonjs/helpers/initRoomsPresence.js.map +1 -1
  103. package/lib/commonjs/helpers/insertMessageWithDelimiter.js +5 -0
  104. package/lib/commonjs/helpers/insertMessageWithDelimiter.js.map +1 -1
  105. package/lib/commonjs/helpers/isLikelyMucJid.js +67 -0
  106. package/lib/commonjs/helpers/isLikelyMucJid.js.map +1 -0
  107. package/lib/commonjs/helpers/isMessageSearchEnabled.js +18 -0
  108. package/lib/commonjs/helpers/isMessageSearchEnabled.js.map +1 -0
  109. package/lib/commonjs/helpers/joiningRoom.js +14 -0
  110. package/lib/commonjs/helpers/joiningRoom.js.map +1 -0
  111. package/lib/commonjs/helpers/jumpThread.js +100 -0
  112. package/lib/commonjs/helpers/jumpThread.js.map +1 -0
  113. package/lib/commonjs/helpers/jumpToMessage.js +58 -0
  114. package/lib/commonjs/helpers/jumpToMessage.js.map +1 -0
  115. package/lib/commonjs/helpers/jumpWindow.js +187 -0
  116. package/lib/commonjs/helpers/jumpWindow.js.map +1 -0
  117. package/lib/commonjs/helpers/loadRoomHistory.js +75 -0
  118. package/lib/commonjs/helpers/loadRoomHistory.js.map +1 -0
  119. package/lib/commonjs/helpers/notificationPolicy.js +77 -0
  120. package/lib/commonjs/helpers/notificationPolicy.js.map +1 -0
  121. package/lib/commonjs/helpers/openRoomAtMessage.js +47 -0
  122. package/lib/commonjs/helpers/openRoomAtMessage.js.map +1 -0
  123. package/lib/commonjs/helpers/openThreadForJump.js +72 -0
  124. package/lib/commonjs/helpers/openThreadForJump.js.map +1 -0
  125. package/lib/commonjs/helpers/parseMessageReference.js +27 -8
  126. package/lib/commonjs/helpers/parseMessageReference.js.map +1 -1
  127. package/lib/commonjs/helpers/publicChatsEntry.js +17 -0
  128. package/lib/commonjs/helpers/publicChatsEntry.js.map +1 -0
  129. package/lib/commonjs/helpers/roomActivityScore.js +37 -0
  130. package/lib/commonjs/helpers/roomActivityScore.js.map +1 -0
  131. package/lib/commonjs/helpers/roomUserCount.js +36 -0
  132. package/lib/commonjs/helpers/roomUserCount.js.map +1 -0
  133. package/lib/commonjs/helpers/runConfiguredHistoryPreload.js +68 -0
  134. package/lib/commonjs/helpers/runConfiguredHistoryPreload.js.map +1 -0
  135. package/lib/commonjs/helpers/sessionAppId.js +24 -0
  136. package/lib/commonjs/helpers/sessionAppId.js.map +1 -0
  137. package/lib/commonjs/helpers/timestamp.js +43 -0
  138. package/lib/commonjs/helpers/timestamp.js.map +1 -0
  139. package/lib/commonjs/helpers/userResolver.js +517 -0
  140. package/lib/commonjs/helpers/userResolver.js.map +1 -0
  141. package/lib/commonjs/helpers/xmppIdShape.js +52 -0
  142. package/lib/commonjs/helpers/xmppIdShape.js.map +1 -0
  143. package/lib/commonjs/hooks/useChatWrapperInit.js +27 -5
  144. package/lib/commonjs/hooks/useChatWrapperInit.js.map +1 -1
  145. package/lib/commonjs/hooks/useHeapSender.js +54 -3
  146. package/lib/commonjs/hooks/useHeapSender.js.map +1 -1
  147. package/lib/commonjs/hooks/useJumpToMessage.js +411 -0
  148. package/lib/commonjs/hooks/useJumpToMessage.js.map +1 -0
  149. package/lib/commonjs/hooks/useMessageLoaderQueue.js +32 -5
  150. package/lib/commonjs/hooks/useMessageLoaderQueue.js.map +1 -1
  151. package/lib/commonjs/hooks/useMessageQueue.js +13 -5
  152. package/lib/commonjs/hooks/useMessageQueue.js.map +1 -1
  153. package/lib/commonjs/hooks/useRoomDirectory.js +29 -0
  154. package/lib/commonjs/hooks/useRoomDirectory.js.map +1 -0
  155. package/lib/commonjs/hooks/useRoomInitialization.js +27 -5
  156. package/lib/commonjs/hooks/useRoomInitialization.js.map +1 -1
  157. package/lib/commonjs/hooks/useRoomMute.js +62 -0
  158. package/lib/commonjs/hooks/useRoomMute.js.map +1 -0
  159. package/lib/commonjs/hooks/useRoomState.js +3 -1
  160. package/lib/commonjs/hooks/useRoomState.js.map +1 -1
  161. package/lib/commonjs/hooks/useSendMessage.js +13 -3
  162. package/lib/commonjs/hooks/useSendMessage.js.map +1 -1
  163. package/lib/commonjs/i18n/strings.js +288 -6
  164. package/lib/commonjs/i18n/strings.js.map +1 -1
  165. package/lib/commonjs/networking/api-requests/messageSearch.api.js +87 -0
  166. package/lib/commonjs/networking/api-requests/messageSearch.api.js.map +1 -0
  167. package/lib/commonjs/networking/api-requests/publicChats.api.js +48 -0
  168. package/lib/commonjs/networking/api-requests/publicChats.api.js.map +1 -0
  169. package/lib/commonjs/networking/api-requests/roomMembers.api.js +253 -4
  170. package/lib/commonjs/networking/api-requests/roomMembers.api.js.map +1 -1
  171. package/lib/commonjs/networking/api-requests/rooms.api.js +48 -12
  172. package/lib/commonjs/networking/api-requests/rooms.api.js.map +1 -1
  173. package/lib/commonjs/networking/api-requests/xmppToken.api.js +71 -0
  174. package/lib/commonjs/networking/api-requests/xmppToken.api.js.map +1 -0
  175. package/lib/commonjs/networking/ethoraEvents.js +227 -0
  176. package/lib/commonjs/networking/ethoraEvents.js.map +1 -0
  177. package/lib/commonjs/networking/stanzaHandlers.js +153 -16
  178. package/lib/commonjs/networking/stanzaHandlers.js.map +1 -1
  179. package/lib/commonjs/networking/xmpp/allRoomPresences.xmpp.js +124 -3
  180. package/lib/commonjs/networking/xmpp/allRoomPresences.xmpp.js.map +1 -1
  181. package/lib/commonjs/networking/xmpp/getHistory.xmpp.js +319 -35
  182. package/lib/commonjs/networking/xmpp/getHistory.xmpp.js.map +1 -1
  183. package/lib/commonjs/networking/xmpp/handleStanzas.xmpp.js +7 -0
  184. package/lib/commonjs/networking/xmpp/handleStanzas.xmpp.js.map +1 -1
  185. package/lib/commonjs/networking/xmpp/historyCursor.js +101 -0
  186. package/lib/commonjs/networking/xmpp/historyCursor.js.map +1 -0
  187. package/lib/commonjs/networking/xmpp/mamQueryIds.js +17 -0
  188. package/lib/commonjs/networking/xmpp/mamQueryIds.js.map +1 -0
  189. package/lib/commonjs/networking/xmpp/mamRouter.js +24 -9
  190. package/lib/commonjs/networking/xmpp/mamRouter.js.map +1 -1
  191. package/lib/commonjs/networking/xmpp/presenceInRoom.xmpp.js +70 -65
  192. package/lib/commonjs/networking/xmpp/presenceInRoom.xmpp.js.map +1 -1
  193. package/lib/commonjs/networking/xmpp/roomJoinSweep.js +389 -0
  194. package/lib/commonjs/networking/xmpp/roomJoinSweep.js.map +1 -0
  195. package/lib/commonjs/networking/xmpp/sendMediaMessage.xmpp.js +0 -1
  196. package/lib/commonjs/networking/xmpp/sendMediaMessage.xmpp.js.map +1 -1
  197. package/lib/commonjs/networking/xmpp/sendTextMessage.xmpp.js +0 -1
  198. package/lib/commonjs/networking/xmpp/sendTextMessage.xmpp.js.map +1 -1
  199. package/lib/commonjs/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.js +0 -1
  200. package/lib/commonjs/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.js.map +1 -1
  201. package/lib/commonjs/networking/xmppClient.js +269 -41
  202. package/lib/commonjs/networking/xmppClient.js.map +1 -1
  203. package/lib/commonjs/networking/xmppCredentials.js +80 -0
  204. package/lib/commonjs/networking/xmppCredentials.js.map +1 -0
  205. package/lib/commonjs/roomStore/Middleware/jumpThreadMiddleware.js +158 -0
  206. package/lib/commonjs/roomStore/Middleware/jumpThreadMiddleware.js.map +1 -0
  207. package/lib/commonjs/roomStore/Middleware/reactionsMiddleware.js +6 -0
  208. package/lib/commonjs/roomStore/Middleware/reactionsMiddleware.js.map +1 -1
  209. package/lib/commonjs/roomStore/Middleware/unreadMidlleware.js +45 -7
  210. package/lib/commonjs/roomStore/Middleware/unreadMidlleware.js.map +1 -1
  211. package/lib/commonjs/roomStore/chatSettingsSlice.js +15 -1
  212. package/lib/commonjs/roomStore/chatSettingsSlice.js.map +1 -1
  213. package/lib/commonjs/roomStore/index.js +13 -3
  214. package/lib/commonjs/roomStore/index.js.map +1 -1
  215. package/lib/commonjs/roomStore/persistence.js +48 -1
  216. package/lib/commonjs/roomStore/persistence.js.map +1 -1
  217. package/lib/commonjs/roomStore/roomsSlice.js +474 -60
  218. package/lib/commonjs/roomStore/roomsSlice.js.map +1 -1
  219. package/lib/commonjs/roomStore/safeKey.js +44 -0
  220. package/lib/commonjs/roomStore/safeKey.js.map +1 -0
  221. package/lib/commonjs/roomStore/usersSetCap.js +37 -0
  222. package/lib/commonjs/roomStore/usersSetCap.js.map +1 -0
  223. package/lib/commonjs/version.js +1 -1
  224. package/lib/commonjs/version.js.map +1 -1
  225. package/lib/module/assets/icons.js +1 -1
  226. package/lib/module/assets/icons.js.map +1 -1
  227. package/lib/module/components/Header/HeaderRoomList.js +23 -7
  228. package/lib/module/components/Header/HeaderRoomList.js.map +1 -1
  229. package/lib/module/components/InputComponents/Search.js +39 -2
  230. package/lib/module/components/InputComponents/Search.js.map +1 -1
  231. package/lib/module/components/MainComponents/ArchivedMessageCard.js +161 -0
  232. package/lib/module/components/MainComponents/ArchivedMessageCard.js.map +1 -0
  233. package/lib/module/components/MainComponents/ChatHeader.js +22 -5
  234. package/lib/module/components/MainComponents/ChatHeader.js.map +1 -1
  235. package/lib/module/components/MainComponents/ChatRoom.js +57 -26
  236. package/lib/module/components/MainComponents/ChatRoom.js.map +1 -1
  237. package/lib/module/components/MainComponents/ChatWrapper.js +31 -1
  238. package/lib/module/components/MainComponents/ChatWrapper.js.map +1 -1
  239. package/lib/module/components/MainComponents/MediaMessage.js +18 -3
  240. package/lib/module/components/MainComponents/MediaMessage.js.map +1 -1
  241. package/lib/module/components/MainComponents/MessageList.js +740 -42
  242. package/lib/module/components/MainComponents/MessageList.js.map +1 -1
  243. package/lib/module/components/MainComponents/MessageSearchButton.js +48 -0
  244. package/lib/module/components/MainComponents/MessageSearchButton.js.map +1 -0
  245. package/lib/module/components/MainComponents/ReduxWrapper.js +15 -7
  246. package/lib/module/components/MainComponents/ReduxWrapper.js.map +1 -1
  247. package/lib/module/components/MainComponents/RoomList.js +111 -2
  248. package/lib/module/components/MainComponents/RoomList.js.map +1 -1
  249. package/lib/module/components/Menu/HeaderRoomListMenu.js +15 -4
  250. package/lib/module/components/Menu/HeaderRoomListMenu.js.map +1 -1
  251. package/lib/module/components/MessageBubble/BubbleHighlight.js +106 -0
  252. package/lib/module/components/MessageBubble/BubbleHighlight.js.map +1 -0
  253. package/lib/module/components/MessageBubble/Message.js +172 -127
  254. package/lib/module/components/MessageBubble/Message.js.map +1 -1
  255. package/lib/module/components/MessageBubble/MessageInteractions.js +98 -78
  256. package/lib/module/components/MessageBubble/MessageInteractions.js.map +1 -1
  257. package/lib/module/components/MessageBubble/messageMenuLayout.js +45 -0
  258. package/lib/module/components/MessageBubble/messageMenuLayout.js.map +1 -0
  259. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js +74 -38
  260. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  261. package/lib/module/components/Modals/MessageSearchModal/MessageHitResults.js +243 -0
  262. package/lib/module/components/Modals/MessageSearchModal/MessageHitResults.js.map +1 -0
  263. package/lib/module/components/Modals/MessageSearchModal/MessageSearchModal.js +482 -0
  264. package/lib/module/components/Modals/MessageSearchModal/MessageSearchModal.js.map +1 -0
  265. package/lib/module/components/Modals/MessageSearchModal/openHit.js +45 -0
  266. package/lib/module/components/Modals/MessageSearchModal/openHit.js.map +1 -0
  267. package/lib/module/components/Modals/MessageSearchModal/resolveSender.js +41 -0
  268. package/lib/module/components/Modals/MessageSearchModal/resolveSender.js.map +1 -0
  269. package/lib/module/components/Modals/MessageSearchModal/searchFilters.js +74 -0
  270. package/lib/module/components/Modals/MessageSearchModal/searchFilters.js.map +1 -0
  271. package/lib/module/components/Modals/MessageSearchModal/snippet.js +57 -0
  272. package/lib/module/components/Modals/MessageSearchModal/snippet.js.map +1 -0
  273. package/lib/module/components/Modals/MessageSearchModal/useMessageSearch.js +131 -0
  274. package/lib/module/components/Modals/MessageSearchModal/useMessageSearch.js.map +1 -0
  275. package/lib/module/components/Modals/PublicChatsModal/PublicChatsModal.js +300 -0
  276. package/lib/module/components/Modals/PublicChatsModal/PublicChatsModal.js.map +1 -0
  277. package/lib/module/components/Modals/PublicChatsModal/publicChatsLogic.js +30 -0
  278. package/lib/module/components/Modals/PublicChatsModal/publicChatsLogic.js.map +1 -0
  279. package/lib/module/components/Modals/SelectUsersModal/SelectUsersModal.js +8 -1
  280. package/lib/module/components/Modals/SelectUsersModal/SelectUsersModal.js.map +1 -1
  281. package/lib/module/components/RoomComponents/ChatRoomItem.js +14 -3
  282. package/lib/module/components/RoomComponents/ChatRoomItem.js.map +1 -1
  283. package/lib/module/components/RoomComponents/SwipeableRoomRow.js +39 -7
  284. package/lib/module/components/RoomComponents/SwipeableRoomRow.js.map +1 -1
  285. package/lib/module/components/Thread/ThreadHeader.js +3 -1
  286. package/lib/module/components/Thread/ThreadHeader.js.map +1 -1
  287. package/lib/module/components/Thread/ThreadWrapper.js +89 -9
  288. package/lib/module/components/Thread/ThreadWrapper.js.map +1 -1
  289. package/lib/module/components/styled/AudioDecoderHost.js +332 -0
  290. package/lib/module/components/styled/AudioDecoderHost.js.map +1 -0
  291. package/lib/module/components/styled/AudioMessage.js +495 -422
  292. package/lib/module/components/styled/AudioMessage.js.map +1 -1
  293. package/lib/module/components/styled/SendInput.js +32 -6
  294. package/lib/module/components/styled/SendInput.js.map +1 -1
  295. package/lib/module/context/MessageNotificationContext.js +15 -5
  296. package/lib/module/context/MessageNotificationContext.js.map +1 -1
  297. package/lib/module/context/xmppProvider.js +42 -78
  298. package/lib/module/context/xmppProvider.js.map +1 -1
  299. package/lib/module/helpers/audioWaveform.js +153 -0
  300. package/lib/module/helpers/audioWaveform.js.map +1 -0
  301. package/lib/module/helpers/backgroundJoinSweep.js +30 -0
  302. package/lib/module/helpers/backgroundJoinSweep.js.map +1 -0
  303. package/lib/module/helpers/bubbleHighlight.js +38 -0
  304. package/lib/module/helpers/bubbleHighlight.js.map +1 -0
  305. package/lib/module/helpers/checkUniqueUsers.js +15 -13
  306. package/lib/module/helpers/checkUniqueUsers.js.map +1 -1
  307. package/lib/module/helpers/constants/MODAL_COMPONENTS.js +5 -1
  308. package/lib/module/helpers/constants/MODAL_COMPONENTS.js.map +1 -1
  309. package/lib/module/helpers/constants/MODAL_TYPES.js +3 -1
  310. package/lib/module/helpers/constants/MODAL_TYPES.js.map +1 -1
  311. package/lib/module/helpers/createRoomFromApi.js +37 -3
  312. package/lib/module/helpers/createRoomFromApi.js.map +1 -1
  313. package/lib/module/helpers/dedupeMembers.js +28 -0
  314. package/lib/module/helpers/dedupeMembers.js.map +1 -0
  315. package/lib/module/helpers/ethoraLogger.js +52 -0
  316. package/lib/module/helpers/ethoraLogger.js.map +1 -0
  317. package/lib/module/helpers/getUnnamedUsers.js +9 -4
  318. package/lib/module/helpers/getUnnamedUsers.js.map +1 -1
  319. package/lib/module/helpers/historyPreloadConfig.js +39 -0
  320. package/lib/module/helpers/historyPreloadConfig.js.map +1 -0
  321. package/lib/module/helpers/historyPreloadScheduler.js +368 -134
  322. package/lib/module/helpers/historyPreloadScheduler.js.map +1 -1
  323. package/lib/module/helpers/initRoomsPresence.js +18 -5
  324. package/lib/module/helpers/initRoomsPresence.js.map +1 -1
  325. package/lib/module/helpers/insertMessageWithDelimiter.js +5 -0
  326. package/lib/module/helpers/insertMessageWithDelimiter.js.map +1 -1
  327. package/lib/module/helpers/isLikelyMucJid.js +60 -0
  328. package/lib/module/helpers/isLikelyMucJid.js.map +1 -0
  329. package/lib/module/helpers/isMessageSearchEnabled.js +13 -0
  330. package/lib/module/helpers/isMessageSearchEnabled.js.map +1 -0
  331. package/lib/module/helpers/joiningRoom.js +9 -0
  332. package/lib/module/helpers/joiningRoom.js.map +1 -0
  333. package/lib/module/helpers/jumpThread.js +83 -0
  334. package/lib/module/helpers/jumpThread.js.map +1 -0
  335. package/lib/module/helpers/jumpToMessage.js +52 -0
  336. package/lib/module/helpers/jumpToMessage.js.map +1 -0
  337. package/lib/module/helpers/jumpWindow.js +177 -0
  338. package/lib/module/helpers/jumpWindow.js.map +1 -0
  339. package/lib/module/helpers/loadRoomHistory.js +69 -0
  340. package/lib/module/helpers/loadRoomHistory.js.map +1 -0
  341. package/lib/module/helpers/notificationPolicy.js +70 -0
  342. package/lib/module/helpers/notificationPolicy.js.map +1 -0
  343. package/lib/module/helpers/openRoomAtMessage.js +41 -0
  344. package/lib/module/helpers/openRoomAtMessage.js.map +1 -0
  345. package/lib/module/helpers/openThreadForJump.js +68 -0
  346. package/lib/module/helpers/openThreadForJump.js.map +1 -0
  347. package/lib/module/helpers/parseMessageReference.js +26 -6
  348. package/lib/module/helpers/parseMessageReference.js.map +1 -1
  349. package/lib/module/helpers/publicChatsEntry.js +12 -0
  350. package/lib/module/helpers/publicChatsEntry.js.map +1 -0
  351. package/lib/module/helpers/roomActivityScore.js +30 -0
  352. package/lib/module/helpers/roomActivityScore.js.map +1 -0
  353. package/lib/module/helpers/roomUserCount.js +29 -0
  354. package/lib/module/helpers/roomUserCount.js.map +1 -0
  355. package/lib/module/helpers/runConfiguredHistoryPreload.js +63 -0
  356. package/lib/module/helpers/runConfiguredHistoryPreload.js.map +1 -0
  357. package/lib/module/helpers/sessionAppId.js +20 -0
  358. package/lib/module/helpers/sessionAppId.js.map +1 -0
  359. package/lib/module/helpers/timestamp.js +37 -0
  360. package/lib/module/helpers/timestamp.js.map +1 -0
  361. package/lib/module/helpers/userResolver.js +503 -0
  362. package/lib/module/helpers/userResolver.js.map +1 -0
  363. package/lib/module/helpers/xmppIdShape.js +46 -0
  364. package/lib/module/helpers/xmppIdShape.js.map +1 -0
  365. package/lib/module/hooks/useChatWrapperInit.js +16 -5
  366. package/lib/module/hooks/useChatWrapperInit.js.map +1 -1
  367. package/lib/module/hooks/useHeapSender.js +54 -3
  368. package/lib/module/hooks/useHeapSender.js.map +1 -1
  369. package/lib/module/hooks/useJumpToMessage.js +389 -0
  370. package/lib/module/hooks/useJumpToMessage.js.map +1 -0
  371. package/lib/module/hooks/useMessageLoaderQueue.js +32 -5
  372. package/lib/module/hooks/useMessageLoaderQueue.js.map +1 -1
  373. package/lib/module/hooks/useMessageQueue.js +13 -5
  374. package/lib/module/hooks/useMessageQueue.js.map +1 -1
  375. package/lib/module/hooks/useRoomDirectory.js +24 -0
  376. package/lib/module/hooks/useRoomDirectory.js.map +1 -0
  377. package/lib/module/hooks/useRoomInitialization.js +29 -7
  378. package/lib/module/hooks/useRoomInitialization.js.map +1 -1
  379. package/lib/module/hooks/useRoomMute.js +58 -0
  380. package/lib/module/hooks/useRoomMute.js.map +1 -0
  381. package/lib/module/hooks/useRoomState.js +3 -1
  382. package/lib/module/hooks/useRoomState.js.map +1 -1
  383. package/lib/module/hooks/useSendMessage.js +13 -3
  384. package/lib/module/hooks/useSendMessage.js.map +1 -1
  385. package/lib/module/i18n/strings.js +288 -6
  386. package/lib/module/i18n/strings.js.map +1 -1
  387. package/lib/module/networking/api-requests/messageSearch.api.js +81 -0
  388. package/lib/module/networking/api-requests/messageSearch.api.js.map +1 -0
  389. package/lib/module/networking/api-requests/publicChats.api.js +43 -0
  390. package/lib/module/networking/api-requests/publicChats.api.js.map +1 -0
  391. package/lib/module/networking/api-requests/roomMembers.api.js +248 -4
  392. package/lib/module/networking/api-requests/roomMembers.api.js.map +1 -1
  393. package/lib/module/networking/api-requests/rooms.api.js +48 -12
  394. package/lib/module/networking/api-requests/rooms.api.js.map +1 -1
  395. package/lib/module/networking/api-requests/xmppToken.api.js +66 -0
  396. package/lib/module/networking/api-requests/xmppToken.api.js.map +1 -0
  397. package/lib/module/networking/ethoraEvents.js +220 -0
  398. package/lib/module/networking/ethoraEvents.js.map +1 -0
  399. package/lib/module/networking/stanzaHandlers.js +155 -18
  400. package/lib/module/networking/stanzaHandlers.js.map +1 -1
  401. package/lib/module/networking/xmpp/allRoomPresences.xmpp.js +125 -3
  402. package/lib/module/networking/xmpp/allRoomPresences.xmpp.js.map +1 -1
  403. package/lib/module/networking/xmpp/getHistory.xmpp.js +318 -35
  404. package/lib/module/networking/xmpp/getHistory.xmpp.js.map +1 -1
  405. package/lib/module/networking/xmpp/handleStanzas.xmpp.js +7 -0
  406. package/lib/module/networking/xmpp/handleStanzas.xmpp.js.map +1 -1
  407. package/lib/module/networking/xmpp/historyCursor.js +95 -0
  408. package/lib/module/networking/xmpp/historyCursor.js.map +1 -0
  409. package/lib/module/networking/xmpp/mamQueryIds.js +12 -0
  410. package/lib/module/networking/xmpp/mamQueryIds.js.map +1 -0
  411. package/lib/module/networking/xmpp/mamRouter.js +22 -8
  412. package/lib/module/networking/xmpp/mamRouter.js.map +1 -1
  413. package/lib/module/networking/xmpp/presenceInRoom.xmpp.js +68 -62
  414. package/lib/module/networking/xmpp/presenceInRoom.xmpp.js.map +1 -1
  415. package/lib/module/networking/xmpp/roomJoinSweep.js +385 -0
  416. package/lib/module/networking/xmpp/roomJoinSweep.js.map +1 -0
  417. package/lib/module/networking/xmpp/sendMediaMessage.xmpp.js +0 -1
  418. package/lib/module/networking/xmpp/sendMediaMessage.xmpp.js.map +1 -1
  419. package/lib/module/networking/xmpp/sendTextMessage.xmpp.js +0 -1
  420. package/lib/module/networking/xmpp/sendTextMessage.xmpp.js.map +1 -1
  421. package/lib/module/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.js +0 -1
  422. package/lib/module/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.js.map +1 -1
  423. package/lib/module/networking/xmppClient.js +269 -41
  424. package/lib/module/networking/xmppClient.js.map +1 -1
  425. package/lib/module/networking/xmppCredentials.js +76 -0
  426. package/lib/module/networking/xmppCredentials.js.map +1 -0
  427. package/lib/module/roomStore/Middleware/jumpThreadMiddleware.js +153 -0
  428. package/lib/module/roomStore/Middleware/jumpThreadMiddleware.js.map +1 -0
  429. package/lib/module/roomStore/Middleware/reactionsMiddleware.js +6 -0
  430. package/lib/module/roomStore/Middleware/reactionsMiddleware.js.map +1 -1
  431. package/lib/module/roomStore/Middleware/unreadMidlleware.js +46 -7
  432. package/lib/module/roomStore/Middleware/unreadMidlleware.js.map +1 -1
  433. package/lib/module/roomStore/chatSettingsSlice.js +13 -0
  434. package/lib/module/roomStore/chatSettingsSlice.js.map +1 -1
  435. package/lib/module/roomStore/index.js +7 -3
  436. package/lib/module/roomStore/index.js.map +1 -1
  437. package/lib/module/roomStore/persistence.js +46 -1
  438. package/lib/module/roomStore/persistence.js.map +1 -1
  439. package/lib/module/roomStore/roomsSlice.js +459 -58
  440. package/lib/module/roomStore/roomsSlice.js.map +1 -1
  441. package/lib/module/roomStore/safeKey.js +37 -0
  442. package/lib/module/roomStore/safeKey.js.map +1 -0
  443. package/lib/module/roomStore/usersSetCap.js +32 -0
  444. package/lib/module/roomStore/usersSetCap.js.map +1 -0
  445. package/lib/module/version.js +1 -1
  446. package/lib/module/version.js.map +1 -1
  447. package/lib/typescript/assets/icons.d.ts +4 -0
  448. package/lib/typescript/assets/icons.d.ts.map +1 -1
  449. package/lib/typescript/components/Header/HeaderRoomList.d.ts.map +1 -1
  450. package/lib/typescript/components/InputComponents/Search.d.ts.map +1 -1
  451. package/lib/typescript/components/MainComponents/ArchivedMessageCard.d.ts +12 -0
  452. package/lib/typescript/components/MainComponents/ArchivedMessageCard.d.ts.map +1 -0
  453. package/lib/typescript/components/MainComponents/ChatHeader.d.ts.map +1 -1
  454. package/lib/typescript/components/MainComponents/ChatRoom.d.ts.map +1 -1
  455. package/lib/typescript/components/MainComponents/ChatWrapper.d.ts.map +1 -1
  456. package/lib/typescript/components/MainComponents/MediaMessage.d.ts.map +1 -1
  457. package/lib/typescript/components/MainComponents/MessageList.d.ts +22 -1
  458. package/lib/typescript/components/MainComponents/MessageList.d.ts.map +1 -1
  459. package/lib/typescript/components/MainComponents/MessageSearchButton.d.ts +7 -0
  460. package/lib/typescript/components/MainComponents/MessageSearchButton.d.ts.map +1 -0
  461. package/lib/typescript/components/MainComponents/ReduxWrapper.d.ts.map +1 -1
  462. package/lib/typescript/components/MainComponents/RoomList.d.ts.map +1 -1
  463. package/lib/typescript/components/Menu/HeaderRoomListMenu.d.ts.map +1 -1
  464. package/lib/typescript/components/MessageBubble/BubbleHighlight.d.ts +12 -0
  465. package/lib/typescript/components/MessageBubble/BubbleHighlight.d.ts.map +1 -0
  466. package/lib/typescript/components/MessageBubble/Message.d.ts.map +1 -1
  467. package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts +5 -0
  468. package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts.map +1 -1
  469. package/lib/typescript/components/MessageBubble/messageMenuLayout.d.ts +29 -0
  470. package/lib/typescript/components/MessageBubble/messageMenuLayout.d.ts.map +1 -0
  471. package/lib/typescript/components/Modals/ChatProfileModal/ChatProfileModal.d.ts +2 -0
  472. package/lib/typescript/components/Modals/ChatProfileModal/ChatProfileModal.d.ts.map +1 -1
  473. package/lib/typescript/components/Modals/MessageSearchModal/MessageHitResults.d.ts +44 -0
  474. package/lib/typescript/components/Modals/MessageSearchModal/MessageHitResults.d.ts.map +1 -0
  475. package/lib/typescript/components/Modals/MessageSearchModal/MessageSearchModal.d.ts +12 -0
  476. package/lib/typescript/components/Modals/MessageSearchModal/MessageSearchModal.d.ts.map +1 -0
  477. package/lib/typescript/components/Modals/MessageSearchModal/openHit.d.ts +15 -0
  478. package/lib/typescript/components/Modals/MessageSearchModal/openHit.d.ts.map +1 -0
  479. package/lib/typescript/components/Modals/MessageSearchModal/resolveSender.d.ts +34 -0
  480. package/lib/typescript/components/Modals/MessageSearchModal/resolveSender.d.ts.map +1 -0
  481. package/lib/typescript/components/Modals/MessageSearchModal/searchFilters.d.ts +41 -0
  482. package/lib/typescript/components/Modals/MessageSearchModal/searchFilters.d.ts.map +1 -0
  483. package/lib/typescript/components/Modals/MessageSearchModal/snippet.d.ts +15 -0
  484. package/lib/typescript/components/Modals/MessageSearchModal/snippet.d.ts.map +1 -0
  485. package/lib/typescript/components/Modals/MessageSearchModal/useMessageSearch.d.ts +23 -0
  486. package/lib/typescript/components/Modals/MessageSearchModal/useMessageSearch.d.ts.map +1 -0
  487. package/lib/typescript/components/Modals/PublicChatsModal/PublicChatsModal.d.ts +16 -0
  488. package/lib/typescript/components/Modals/PublicChatsModal/PublicChatsModal.d.ts.map +1 -0
  489. package/lib/typescript/components/Modals/PublicChatsModal/publicChatsLogic.d.ts +14 -0
  490. package/lib/typescript/components/Modals/PublicChatsModal/publicChatsLogic.d.ts.map +1 -0
  491. package/lib/typescript/components/Modals/SelectUsersModal/SelectUsersModal.d.ts.map +1 -1
  492. package/lib/typescript/components/RoomComponents/ChatRoomItem.d.ts.map +1 -1
  493. package/lib/typescript/components/RoomComponents/SwipeableRoomRow.d.ts +5 -1
  494. package/lib/typescript/components/RoomComponents/SwipeableRoomRow.d.ts.map +1 -1
  495. package/lib/typescript/components/Thread/ThreadHeader.d.ts.map +1 -1
  496. package/lib/typescript/components/Thread/ThreadWrapper.d.ts +1 -0
  497. package/lib/typescript/components/Thread/ThreadWrapper.d.ts.map +1 -1
  498. package/lib/typescript/components/styled/AudioDecoderHost.d.ts +39 -0
  499. package/lib/typescript/components/styled/AudioDecoderHost.d.ts.map +1 -0
  500. package/lib/typescript/components/styled/AudioMessage.d.ts +14 -1
  501. package/lib/typescript/components/styled/AudioMessage.d.ts.map +1 -1
  502. package/lib/typescript/components/styled/SendInput.d.ts.map +1 -1
  503. package/lib/typescript/context/MessageNotificationContext.d.ts.map +1 -1
  504. package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
  505. package/lib/typescript/helpers/audioWaveform.d.ts +54 -0
  506. package/lib/typescript/helpers/audioWaveform.d.ts.map +1 -0
  507. package/lib/typescript/helpers/backgroundJoinSweep.d.ts +14 -0
  508. package/lib/typescript/helpers/backgroundJoinSweep.d.ts.map +1 -0
  509. package/lib/typescript/helpers/bubbleHighlight.d.ts +17 -0
  510. package/lib/typescript/helpers/bubbleHighlight.d.ts.map +1 -0
  511. package/lib/typescript/helpers/checkUniqueUsers.d.ts +2 -2
  512. package/lib/typescript/helpers/checkUniqueUsers.d.ts.map +1 -1
  513. package/lib/typescript/helpers/constants/MODAL_COMPONENTS.d.ts.map +1 -1
  514. package/lib/typescript/helpers/constants/MODAL_TYPES.d.ts +2 -0
  515. package/lib/typescript/helpers/constants/MODAL_TYPES.d.ts.map +1 -1
  516. package/lib/typescript/helpers/createRoomFromApi.d.ts +25 -0
  517. package/lib/typescript/helpers/createRoomFromApi.d.ts.map +1 -1
  518. package/lib/typescript/helpers/dedupeMembers.d.ts +11 -0
  519. package/lib/typescript/helpers/dedupeMembers.d.ts.map +1 -0
  520. package/lib/typescript/helpers/ethoraLogger.d.ts +13 -0
  521. package/lib/typescript/helpers/ethoraLogger.d.ts.map +1 -0
  522. package/lib/typescript/helpers/getUnnamedUsers.d.ts.map +1 -1
  523. package/lib/typescript/helpers/historyPreloadConfig.d.ts +26 -0
  524. package/lib/typescript/helpers/historyPreloadConfig.d.ts.map +1 -0
  525. package/lib/typescript/helpers/historyPreloadScheduler.d.ts +8 -4
  526. package/lib/typescript/helpers/historyPreloadScheduler.d.ts.map +1 -1
  527. package/lib/typescript/helpers/initRoomsPresence.d.ts +7 -0
  528. package/lib/typescript/helpers/initRoomsPresence.d.ts.map +1 -1
  529. package/lib/typescript/helpers/insertMessageWithDelimiter.d.ts.map +1 -1
  530. package/lib/typescript/helpers/isLikelyMucJid.d.ts +19 -0
  531. package/lib/typescript/helpers/isLikelyMucJid.d.ts.map +1 -0
  532. package/lib/typescript/helpers/isMessageSearchEnabled.d.ts +12 -0
  533. package/lib/typescript/helpers/isMessageSearchEnabled.d.ts.map +1 -0
  534. package/lib/typescript/helpers/joiningRoom.d.ts +7 -0
  535. package/lib/typescript/helpers/joiningRoom.d.ts.map +1 -0
  536. package/lib/typescript/helpers/jumpThread.d.ts +48 -0
  537. package/lib/typescript/helpers/jumpThread.d.ts.map +1 -0
  538. package/lib/typescript/helpers/jumpToMessage.d.ts +26 -0
  539. package/lib/typescript/helpers/jumpToMessage.d.ts.map +1 -0
  540. package/lib/typescript/helpers/jumpWindow.d.ts +74 -0
  541. package/lib/typescript/helpers/jumpWindow.d.ts.map +1 -0
  542. package/lib/typescript/helpers/loadRoomHistory.d.ts +34 -0
  543. package/lib/typescript/helpers/loadRoomHistory.d.ts.map +1 -0
  544. package/lib/typescript/helpers/notificationPolicy.d.ts +42 -0
  545. package/lib/typescript/helpers/notificationPolicy.d.ts.map +1 -0
  546. package/lib/typescript/helpers/openRoomAtMessage.d.ts +17 -0
  547. package/lib/typescript/helpers/openRoomAtMessage.d.ts.map +1 -0
  548. package/lib/typescript/helpers/openThreadForJump.d.ts +27 -0
  549. package/lib/typescript/helpers/openThreadForJump.d.ts.map +1 -0
  550. package/lib/typescript/helpers/parseMessageReference.d.ts +18 -5
  551. package/lib/typescript/helpers/parseMessageReference.d.ts.map +1 -1
  552. package/lib/typescript/helpers/publicChatsEntry.d.ts +11 -0
  553. package/lib/typescript/helpers/publicChatsEntry.d.ts.map +1 -0
  554. package/lib/typescript/helpers/roomActivityScore.d.ts +5 -0
  555. package/lib/typescript/helpers/roomActivityScore.d.ts.map +1 -0
  556. package/lib/typescript/helpers/roomUserCount.d.ts +20 -0
  557. package/lib/typescript/helpers/roomUserCount.d.ts.map +1 -0
  558. package/lib/typescript/helpers/runConfiguredHistoryPreload.d.ts +21 -0
  559. package/lib/typescript/helpers/runConfiguredHistoryPreload.d.ts.map +1 -0
  560. package/lib/typescript/helpers/sessionAppId.d.ts +8 -0
  561. package/lib/typescript/helpers/sessionAppId.d.ts.map +1 -0
  562. package/lib/typescript/helpers/timestamp.d.ts +3 -0
  563. package/lib/typescript/helpers/timestamp.d.ts.map +1 -0
  564. package/lib/typescript/helpers/userResolver.d.ts +58 -0
  565. package/lib/typescript/helpers/userResolver.d.ts.map +1 -0
  566. package/lib/typescript/helpers/xmppIdShape.d.ts +10 -0
  567. package/lib/typescript/helpers/xmppIdShape.d.ts.map +1 -0
  568. package/lib/typescript/hooks/useChatWrapperInit.d.ts +2 -0
  569. package/lib/typescript/hooks/useChatWrapperInit.d.ts.map +1 -1
  570. package/lib/typescript/hooks/useHeapSender.d.ts.map +1 -1
  571. package/lib/typescript/hooks/useJumpToMessage.d.ts +116 -0
  572. package/lib/typescript/hooks/useJumpToMessage.d.ts.map +1 -0
  573. package/lib/typescript/hooks/useMessageLoaderQueue.d.ts.map +1 -1
  574. package/lib/typescript/hooks/useMessageQueue.d.ts.map +1 -1
  575. package/lib/typescript/hooks/useRoomDirectory.d.ts +12 -0
  576. package/lib/typescript/hooks/useRoomDirectory.d.ts.map +1 -0
  577. package/lib/typescript/hooks/useRoomInitialization.d.ts.map +1 -1
  578. package/lib/typescript/hooks/useRoomMute.d.ts +8 -0
  579. package/lib/typescript/hooks/useRoomMute.d.ts.map +1 -0
  580. package/lib/typescript/hooks/useRoomState.d.ts +1 -0
  581. package/lib/typescript/hooks/useRoomState.d.ts.map +1 -1
  582. package/lib/typescript/hooks/useSendMessage.d.ts.map +1 -1
  583. package/lib/typescript/i18n/strings.d.ts.map +1 -1
  584. package/lib/typescript/networking/api-requests/messageSearch.api.d.ts +57 -0
  585. package/lib/typescript/networking/api-requests/messageSearch.api.d.ts.map +1 -0
  586. package/lib/typescript/networking/api-requests/publicChats.api.d.ts +24 -0
  587. package/lib/typescript/networking/api-requests/publicChats.api.d.ts.map +1 -0
  588. package/lib/typescript/networking/api-requests/roomMembers.api.d.ts +19 -1
  589. package/lib/typescript/networking/api-requests/roomMembers.api.d.ts.map +1 -1
  590. package/lib/typescript/networking/api-requests/rooms.api.d.ts.map +1 -1
  591. package/lib/typescript/networking/api-requests/xmppToken.api.d.ts +2 -0
  592. package/lib/typescript/networking/api-requests/xmppToken.api.d.ts.map +1 -0
  593. package/lib/typescript/networking/ethoraEvents.d.ts +45 -0
  594. package/lib/typescript/networking/ethoraEvents.d.ts.map +1 -0
  595. package/lib/typescript/networking/stanzaHandlers.d.ts +1 -1
  596. package/lib/typescript/networking/stanzaHandlers.d.ts.map +1 -1
  597. package/lib/typescript/networking/xmpp/allRoomPresences.xmpp.d.ts +32 -1
  598. package/lib/typescript/networking/xmpp/allRoomPresences.xmpp.d.ts.map +1 -1
  599. package/lib/typescript/networking/xmpp/getHistory.xmpp.d.ts +64 -0
  600. package/lib/typescript/networking/xmpp/getHistory.xmpp.d.ts.map +1 -1
  601. package/lib/typescript/networking/xmpp/handleStanzas.xmpp.d.ts.map +1 -1
  602. package/lib/typescript/networking/xmpp/historyCursor.d.ts +35 -0
  603. package/lib/typescript/networking/xmpp/historyCursor.d.ts.map +1 -0
  604. package/lib/typescript/networking/xmpp/mamQueryIds.d.ts +10 -0
  605. package/lib/typescript/networking/xmpp/mamQueryIds.d.ts.map +1 -0
  606. package/lib/typescript/networking/xmpp/mamRouter.d.ts +7 -0
  607. package/lib/typescript/networking/xmpp/mamRouter.d.ts.map +1 -1
  608. package/lib/typescript/networking/xmpp/presenceInRoom.xmpp.d.ts +13 -2
  609. package/lib/typescript/networking/xmpp/presenceInRoom.xmpp.d.ts.map +1 -1
  610. package/lib/typescript/networking/xmpp/roomJoinSweep.d.ts +82 -0
  611. package/lib/typescript/networking/xmpp/roomJoinSweep.d.ts.map +1 -0
  612. package/lib/typescript/networking/xmpp/sendMediaMessage.xmpp.d.ts.map +1 -1
  613. package/lib/typescript/networking/xmpp/sendTextMessage.xmpp.d.ts.map +1 -1
  614. package/lib/typescript/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.d.ts.map +1 -1
  615. package/lib/typescript/networking/xmppClient.d.ts +90 -4
  616. package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
  617. package/lib/typescript/networking/xmppCredentials.d.ts +12 -0
  618. package/lib/typescript/networking/xmppCredentials.d.ts.map +1 -0
  619. package/lib/typescript/roomStore/Middleware/jumpThreadMiddleware.d.ts +3 -0
  620. package/lib/typescript/roomStore/Middleware/jumpThreadMiddleware.d.ts.map +1 -0
  621. package/lib/typescript/roomStore/Middleware/reactionsMiddleware.d.ts.map +1 -1
  622. package/lib/typescript/roomStore/Middleware/unreadMidlleware.d.ts.map +1 -1
  623. package/lib/typescript/roomStore/chatSettingsSlice.d.ts +8 -1
  624. package/lib/typescript/roomStore/chatSettingsSlice.d.ts.map +1 -1
  625. package/lib/typescript/roomStore/index.d.ts +2 -1
  626. package/lib/typescript/roomStore/index.d.ts.map +1 -1
  627. package/lib/typescript/roomStore/persistence.d.ts +8 -0
  628. package/lib/typescript/roomStore/persistence.d.ts.map +1 -1
  629. package/lib/typescript/roomStore/roomsSlice.d.ts +138 -2
  630. package/lib/typescript/roomStore/roomsSlice.d.ts.map +1 -1
  631. package/lib/typescript/roomStore/safeKey.d.ts +13 -0
  632. package/lib/typescript/roomStore/safeKey.d.ts.map +1 -0
  633. package/lib/typescript/roomStore/usersSetCap.d.ts +16 -0
  634. package/lib/typescript/roomStore/usersSetCap.d.ts.map +1 -0
  635. package/lib/typescript/types/models/media.model.d.ts +2 -0
  636. package/lib/typescript/types/models/media.model.d.ts.map +1 -1
  637. package/lib/typescript/types/models/room.model.d.ts +18 -5
  638. package/lib/typescript/types/models/room.model.d.ts.map +1 -1
  639. package/lib/typescript/types/models/xmpp.model.d.ts +8 -1
  640. package/lib/typescript/types/models/xmpp.model.d.ts.map +1 -1
  641. package/lib/typescript/types/types.d.ts +88 -5
  642. package/lib/typescript/types/types.d.ts.map +1 -1
  643. package/lib/typescript/version.d.ts +1 -1
  644. package/lib/typescript/version.d.ts.map +1 -1
  645. package/package.json +1 -1
  646. package/src/assets/icons.tsx +13 -0
  647. package/src/components/Header/HeaderRoomList.tsx +21 -4
  648. package/src/components/InputComponents/Search.tsx +33 -0
  649. package/src/components/MainComponents/ArchivedMessageCard.tsx +139 -0
  650. package/src/components/MainComponents/ChatHeader.tsx +19 -3
  651. package/src/components/MainComponents/ChatRoom.tsx +60 -23
  652. package/src/components/MainComponents/ChatWrapper.tsx +23 -4
  653. package/src/components/MainComponents/MediaMessage.tsx +17 -0
  654. package/src/components/MainComponents/MessageList.tsx +823 -84
  655. package/src/components/MainComponents/MessageSearchButton.tsx +48 -0
  656. package/src/components/MainComponents/ReduxWrapper.tsx +16 -5
  657. package/src/components/MainComponents/RoomList.tsx +125 -0
  658. package/src/components/Menu/HeaderRoomListMenu.tsx +26 -2
  659. package/src/components/MessageBubble/BubbleHighlight.tsx +106 -0
  660. package/src/components/MessageBubble/Message.tsx +265 -177
  661. package/src/components/MessageBubble/MessageInteractions.tsx +109 -86
  662. package/src/components/MessageBubble/messageMenuLayout.ts +59 -0
  663. package/src/components/Modals/ChatProfileModal/ChatProfileModal.tsx +92 -21
  664. package/src/components/Modals/MessageSearchModal/MessageHitResults.tsx +271 -0
  665. package/src/components/Modals/MessageSearchModal/MessageSearchModal.tsx +487 -0
  666. package/src/components/Modals/MessageSearchModal/openHit.ts +50 -0
  667. package/src/components/Modals/MessageSearchModal/resolveSender.ts +57 -0
  668. package/src/components/Modals/MessageSearchModal/searchFilters.ts +109 -0
  669. package/src/components/Modals/MessageSearchModal/snippet.ts +58 -0
  670. package/src/components/Modals/MessageSearchModal/useMessageSearch.ts +151 -0
  671. package/src/components/Modals/PublicChatsModal/PublicChatsModal.tsx +288 -0
  672. package/src/components/Modals/PublicChatsModal/publicChatsLogic.ts +36 -0
  673. package/src/components/Modals/SelectUsersModal/SelectUsersModal.tsx +8 -1
  674. package/src/components/RoomComponents/ChatRoomItem.tsx +16 -3
  675. package/src/components/RoomComponents/SwipeableRoomRow.tsx +39 -6
  676. package/src/components/Thread/ThreadHeader.tsx +3 -1
  677. package/src/components/Thread/ThreadWrapper.tsx +96 -10
  678. package/src/components/styled/AudioDecoderHost.tsx +351 -0
  679. package/src/components/styled/AudioMessage.tsx +482 -425
  680. package/src/components/styled/SendInput.tsx +42 -4
  681. package/src/context/MessageNotificationContext.tsx +19 -5
  682. package/src/context/xmppProvider.tsx +44 -91
  683. package/src/helpers/audioWaveform.ts +142 -0
  684. package/src/helpers/backgroundJoinSweep.ts +35 -0
  685. package/src/helpers/bubbleHighlight.ts +47 -0
  686. package/src/helpers/checkUniqueUsers.tsx +15 -17
  687. package/src/helpers/constants/MODAL_COMPONENTS.tsx +4 -0
  688. package/src/helpers/constants/MODAL_TYPES.ts +3 -0
  689. package/src/helpers/createRoomFromApi.tsx +45 -4
  690. package/src/helpers/dedupeMembers.ts +29 -0
  691. package/src/helpers/ethoraLogger.ts +50 -0
  692. package/src/helpers/getUnnamedUsers.ts +7 -4
  693. package/src/helpers/historyPreloadConfig.ts +68 -0
  694. package/src/helpers/historyPreloadScheduler.ts +437 -199
  695. package/src/helpers/initRoomsPresence.ts +20 -7
  696. package/src/helpers/insertMessageWithDelimiter.ts +3 -0
  697. package/src/helpers/isLikelyMucJid.ts +72 -0
  698. package/src/helpers/isMessageSearchEnabled.ts +18 -0
  699. package/src/helpers/joiningRoom.ts +13 -0
  700. package/src/helpers/jumpThread.ts +102 -0
  701. package/src/helpers/jumpToMessage.ts +57 -0
  702. package/src/helpers/jumpWindow.ts +212 -0
  703. package/src/helpers/loadRoomHistory.ts +83 -0
  704. package/src/helpers/notificationPolicy.ts +82 -0
  705. package/src/helpers/openRoomAtMessage.ts +38 -0
  706. package/src/helpers/openThreadForJump.ts +96 -0
  707. package/src/helpers/parseMessageReference.ts +37 -8
  708. package/src/helpers/publicChatsEntry.ts +15 -0
  709. package/src/helpers/roomActivityScore.ts +45 -0
  710. package/src/helpers/roomUserCount.ts +44 -0
  711. package/src/helpers/runConfiguredHistoryPreload.ts +76 -0
  712. package/src/helpers/sessionAppId.ts +17 -0
  713. package/src/helpers/timestamp.ts +44 -0
  714. package/src/helpers/userResolver.ts +542 -0
  715. package/src/helpers/xmppIdShape.ts +46 -0
  716. package/src/hooks/useChatWrapperInit.ts +22 -4
  717. package/src/hooks/useHeapSender.tsx +54 -3
  718. package/src/hooks/useJumpToMessage.ts +538 -0
  719. package/src/hooks/useMessageLoaderQueue.tsx +40 -4
  720. package/src/hooks/useMessageQueue.ts +9 -5
  721. package/src/hooks/useRoomDirectory.ts +39 -0
  722. package/src/hooks/useRoomInitialization.tsx +33 -7
  723. package/src/hooks/useRoomMute.ts +42 -0
  724. package/src/hooks/useRoomState.tsx +6 -0
  725. package/src/hooks/useSendMessage.tsx +14 -2
  726. package/src/i18n/strings.ts +282 -0
  727. package/src/networking/api-requests/messageSearch.api.ts +130 -0
  728. package/src/networking/api-requests/publicChats.api.ts +71 -0
  729. package/src/networking/api-requests/roomMembers.api.ts +263 -11
  730. package/src/networking/api-requests/rooms.api.ts +52 -12
  731. package/src/networking/api-requests/xmppToken.api.ts +62 -0
  732. package/src/networking/ethoraEvents.ts +262 -0
  733. package/src/networking/stanzaHandlers.ts +145 -19
  734. package/src/networking/xmpp/allRoomPresences.xmpp.ts +155 -5
  735. package/src/networking/xmpp/getHistory.xmpp.ts +433 -47
  736. package/src/networking/xmpp/handleStanzas.xmpp.ts +8 -0
  737. package/src/networking/xmpp/historyCursor.ts +124 -0
  738. package/src/networking/xmpp/mamQueryIds.ts +11 -0
  739. package/src/networking/xmpp/mamRouter.ts +24 -10
  740. package/src/networking/xmpp/presenceInRoom.xmpp.ts +91 -68
  741. package/src/networking/xmpp/roomJoinSweep.ts +412 -0
  742. package/src/networking/xmpp/sendMediaMessage.xmpp.ts +0 -1
  743. package/src/networking/xmpp/sendTextMessage.xmpp.ts +0 -1
  744. package/src/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.ts +0 -1
  745. package/src/networking/xmppClient.ts +323 -47
  746. package/src/networking/xmppCredentials.ts +87 -0
  747. package/src/roomStore/Middleware/jumpThreadMiddleware.ts +163 -0
  748. package/src/roomStore/Middleware/reactionsMiddleware.tsx +4 -0
  749. package/src/roomStore/Middleware/unreadMidlleware.tsx +59 -11
  750. package/src/roomStore/chatSettingsSlice.ts +11 -0
  751. package/src/roomStore/index.ts +10 -3
  752. package/src/roomStore/persistence.ts +54 -0
  753. package/src/roomStore/roomsSlice.ts +547 -65
  754. package/src/roomStore/safeKey.ts +49 -0
  755. package/src/roomStore/usersSetCap.ts +37 -0
  756. package/src/types/models/media.model.ts +4 -0
  757. package/src/types/models/room.model.ts +18 -5
  758. package/src/types/models/xmpp.model.ts +15 -1
  759. package/src/types/types.ts +93 -5
  760. package/src/version.ts +1 -1
@@ -4,6 +4,10 @@ import { EditAction, HistoryPreloadState, IMessage, IRoom } from '../types/types
4
4
  import { insertMessageWithDelimiter } from '../helpers/insertMessageWithDelimiter';
5
5
  import { msgSortableMs } from '../helpers/msgSortableMs';
6
6
  import type XmppClient from '../networking/xmppClient';
7
+ import type { TranslationObject } from '../helpers/transformTranslatations';
8
+ import { isSafeKey } from './safeKey';
9
+ import { mergeUsersSet as mergeIntoUsersSet } from './usersSetCap';
10
+ import { getRoomUserCount } from '../helpers/roomUserCount';
7
11
  import { subscribeRoomForPush } from '../services/pushSubscriptionService';
8
12
 
9
13
  // Per-room runtime message cap. Mirrors the persistence layer's
@@ -175,6 +179,64 @@ const insertRoomMessage = (
175
179
  return { grew, atHead };
176
180
  };
177
181
 
182
+ /**
183
+ * Keeps the jump window's separate copy of a message in step with the live
184
+ * list: an echo of a message the reader sees flips it from "sending" to
185
+ * delivered there too, call-log entries collapse per callId, and (only for a
186
+ * live single-message add, `allowAppend`) a reader parked at the LIVE END of
187
+ * a window (hasNewer false) sees what arrives next. While hasNewer is true
188
+ * the window is not adjacent to the tail and the message waits in
189
+ * room.messages until the reader returns to live.
190
+ */
191
+ const syncJumpWindowWithMessage = (
192
+ state: WritableDraft<RoomMessagesState>,
193
+ roomJID: string,
194
+ message: IMessage,
195
+ start: boolean | undefined,
196
+ allowAppend: boolean
197
+ ): void => {
198
+ const win = state.jumpWindow;
199
+ if (!win || win.roomJID !== roomJID || isCallSignalMessage(message)) {return;}
200
+ const incomingCallLog = message.callLog;
201
+ if (incomingCallLog?.callId) {
202
+ const wi = win.messages.findIndex(
203
+ (msg) => msg.callLog?.callId === incomingCallLog.callId
204
+ );
205
+ if (wi !== -1) {
206
+ const wExisting = win.messages[wi];
207
+ const wMerged = mergeCallLogEntries(wExisting, message);
208
+ if (wMerged !== wExisting) {win.messages[wi] = wMerged;}
209
+ return;
210
+ }
211
+ }
212
+ if (win.messages.length === 0) {return;}
213
+ const isKnownMessage = (msg: IMessage) =>
214
+ msg.id === message.id ||
215
+ (!!message.xmppId && msg.id === message.xmppId) ||
216
+ (!!msg.xmppId && msg.xmppId === message.id);
217
+ const wi = win.messages.findIndex(isKnownMessage);
218
+ if (wi !== -1) {
219
+ const prev = win.messages[wi];
220
+ const next: Record<string, unknown> = { ...prev };
221
+ for (const [key, value] of Object.entries(message)) {
222
+ if (value !== undefined && isSafeKey(key)) {next[key] = value;}
223
+ }
224
+ // The sender identity captured on first insert wins, as in the live
225
+ // list (the echo parses a different id form).
226
+ next.user = prev.user;
227
+ next.pending = false;
228
+ win.messages[wi] = next as unknown as IMessage;
229
+ } else if (allowAppend && !win.hasNewer && !start) {
230
+ win.messages.push({ ...message });
231
+ if (win.messages.length > JUMP_WINDOW_MAX_MESSAGES) {
232
+ win.messages = win.messages.slice(-JUMP_WINDOW_MAX_MESSAGES);
233
+ const oldest = Number(win.messages[0].id);
234
+ win.olderCursor = Number.isFinite(oldest) ? oldest : win.olderCursor;
235
+ win.hasOlder = true;
236
+ }
237
+ }
238
+ };
239
+
178
240
  const capAfterGrowth = (
179
241
  messages: IMessage[],
180
242
  lengthBefore: number,
@@ -208,6 +270,7 @@ const upsertRoom = (
208
270
  state: WritableDraft<RoomMessagesState>,
209
271
  roomData: IRoom
210
272
  ): void => {
273
+ if (!isSafeKey(roomData?.jid)) {return;}
211
274
  const existing = state.rooms[roomData.jid];
212
275
  let lastViewed: number;
213
276
  const serverMarker = state.privateStoreMarkers?.[roomData.jid] || 0;
@@ -237,13 +300,23 @@ const upsertRoom = (
237
300
  : [];
238
301
  state.rooms[roomData.jid] = {
239
302
  ...roomData,
303
+ usersCnt: resolveIncomingUsersCnt(roomData, existing),
304
+ // A seed for the room-list preview: take the freshest API value but
305
+ // never let a payload without one erase what is already there.
306
+ lastMessage: roomData.lastMessage ?? existing?.lastMessage,
307
+ ...resolveApiUnread(state, roomData.jid, roomData, existing),
240
308
  icon: roomData.icon !== undefined ? roomData.icon : existing?.icon,
241
309
  roomBg: roomData.roomBg !== undefined ? roomData.roomBg : existing?.roomBg,
242
310
  messages:
243
311
  existingMessages.length > 0 ? existingMessages : incomingMessages,
244
312
  lastViewedTimestamp: lastViewed,
313
+ // A server-seeded count must not badge a room that is open right now.
245
314
  unreadMessages:
246
- roomData.unreadMessages ?? existing?.unreadMessages ?? 0,
315
+ roomData.apiUnreadCount !== undefined &&
316
+ (state.activeRoomJID === roomData.jid ||
317
+ state.visibleRoomJID === roomData.jid)
318
+ ? existing?.unreadMessages ?? 0
319
+ : roomData.unreadMessages ?? existing?.unreadMessages ?? 0,
247
320
  unreadBaselineTimestamp:
248
321
  existing?.unreadBaselineTimestamp ??
249
322
  existing?.lastViewedTimestamp ??
@@ -427,12 +500,91 @@ function mergeHistoryIntoCache(
427
500
  return [...capTail(merged), ...pending];
428
501
  }
429
502
 
503
+ /** A message found in the archive that the chat cannot show in place. */
504
+ export interface ArchivedMessage {
505
+ roomJID: string;
506
+ sender: string;
507
+ body: string;
508
+ /** ISO timestamp from the archive. */
509
+ createdAt: string;
510
+ }
511
+
512
+ /** A request to scroll a room's transcript to one message. */
513
+ export interface PendingJump {
514
+ roomJID: string;
515
+ /** Any id the message may be known by: message id, stanza id, xmpp id. */
516
+ ids: string[];
517
+ /**
518
+ * For a message that has no usable id (search archive rows often carry
519
+ * none): its timestamp and text, matched against the loaded transcript.
520
+ */
521
+ createdAt?: string;
522
+ body?: string;
523
+ /**
524
+ * What to show if the message cannot be reached in the transcript: the hit
525
+ * itself, so a tap on a search result always ends with the message on
526
+ * screen rather than an error.
527
+ */
528
+ preview?: ArchivedMessage;
529
+ /** Epoch ms the request was made, to drop one nobody ever fulfilled. */
530
+ at: number;
531
+ }
532
+
533
+ /**
534
+ * A short slice of a room's archive around one message, shown INSTEAD of the
535
+ * live list while the reader is away from the tail (a search jump to a message
536
+ * far back). Separate from room.messages on purpose: it is not contiguous with
537
+ * the live history, so it must not touch messageStats or historyComplete.
538
+ * Never persisted.
539
+ */
540
+ export interface JumpWindow {
541
+ roomJID: string;
542
+ /** Ascending by time, like room.messages. */
543
+ messages: IMessage[];
544
+ /** Id of the message the jump named, as it appears in `messages`. */
545
+ targetId: string;
546
+ /** RSM cursor (microseconds) to continue older paging from. */
547
+ olderCursor: number | null;
548
+ hasOlder: boolean;
549
+ /** RSM cursor (microseconds) to continue newer paging from. */
550
+ newerCursor: number | null;
551
+ hasNewer: boolean;
552
+ }
553
+
554
+ /** The window is trimmed on the far side past this many messages. */
555
+ export const JUMP_WINDOW_MAX_MESSAGES = 300;
556
+
557
+ const mergeWindowMessages = (a: IMessage[], b: IMessage[]): IMessage[] => {
558
+ const seen = new Set<string>();
559
+ const merged: IMessage[] = [];
560
+ for (const message of [...a, ...b]) {
561
+ const key = String(message.id);
562
+ if (seen.has(key)) {continue;}
563
+ seen.add(key);
564
+ merged.push(message);
565
+ }
566
+ const num = (message: IMessage) => {
567
+ const id = Number(message.id);
568
+ return Number.isFinite(id) && id > 0
569
+ ? id
570
+ : new Date(message.date).getTime();
571
+ };
572
+ // Archive ids are microsecond stanza ids and ascending in time; fall back to
573
+ // the date when an id is not numeric.
574
+ return merged.sort((x, y) => num(x) - num(y));
575
+ };
576
+
430
577
  export interface RoomPreloadPatch {
431
578
  jid: string;
432
579
  messages?: IMessage[];
433
580
  historyPreloadState?: HistoryPreloadState;
434
581
  unreadCapped?: boolean;
435
582
  historyComplete?: boolean;
583
+ /**
584
+ * RSM bounds of the loaded history (epoch ms or archive microseconds, the
585
+ * same units the room already stores): merged into room.messageStats.
586
+ */
587
+ messageStats?: { firstMessageTimestamp?: number; lastMessageTimestamp?: number };
436
588
  }
437
589
 
438
590
  export interface RoomMessagesState {
@@ -444,6 +596,10 @@ export interface RoomMessagesState {
444
596
  loadingText?: string;
445
597
  isUnreadSyncing?: boolean;
446
598
  usersSet?: Record<string, any>;
599
+ /** See JumpWindow. Never persisted. */
600
+ jumpWindow: JumpWindow | null;
601
+ /** See ArchivedMessage. Never persisted. */
602
+ archivedMessage: ArchivedMessage | null;
447
603
  pendingNotificationJid?: string | null;
448
604
  // Server-side read markers fetched from the XMPP private store
449
605
  // (`{ roomJID: lastViewedMs }`). Kept here so a room that loads AFTER
@@ -451,6 +607,19 @@ export interface RoomMessagesState {
451
607
  // — see `applyPrivateStoreMarkers`. Re-fetched every init/reconnect,
452
608
  // so it is intentionally NOT persisted.
453
609
  privateStoreMarkers: Record<string, number>;
610
+ // A pending "scroll this room's transcript to that message" request (a
611
+ // message search hit, later a notification). Fulfilled by MessageList's
612
+ // useJumpToMessage: scroll if mounted, else page older history until the
613
+ // message shows up. `at` bounds how long a request may stay alive. Never
614
+ // persisted (persistence.ts only picks `rooms`).
615
+ pendingJump: PendingJump | null;
616
+ // The room this session is joining right now (opened from the public chats
617
+ // directory, a link or a QR code) which is not in the room list yet. The
618
+ // server registers the membership a moment after our presence join, so
619
+ // "not in the list" is not yet "unavailable": ChatRoom shows a loader while
620
+ // this names the active room. Set and cleared by useRoomInitialization.
621
+ // Never persisted.
622
+ joiningRoomJID: string | null;
454
623
  /** `{ roomJID: { messageId: reactions[] } }` — reactions that arrived
455
624
  * before their message (a newer history page), applied on insert. */
456
625
  pendingReactions?: Record<string, Record<string, PendingReaction[]>>;
@@ -497,16 +666,97 @@ const initialState: RoomMessagesState = {
497
666
  },
498
667
  pendingNotificationJid: null,
499
668
  privateStoreMarkers: {},
669
+ pendingJump: null,
670
+ joiningRoomJID: null,
500
671
  readBoundaries: {},
672
+ usersSet: {},
673
+ jumpWindow: null,
674
+ archivedMessage: null,
501
675
  pendingReactions: {},
502
676
  };
503
677
 
504
678
  const isValidRoomJid = (jid: unknown): jid is string => {
505
- if (typeof jid !== 'string' || !jid) return false;
506
- if (!jid.includes('@')) return false;
679
+ if (!isSafeKey(jid)) {return false;}
680
+ if (!jid.includes('@')) {return false;}
507
681
  return true;
508
682
  };
509
683
 
684
+ /**
685
+ * Visits every stored copy of one message: the live copy in
686
+ * state.rooms[jid].messages and, when a jump window is open on that room, the
687
+ * window's separate copy. The window is a second list of the same messages, so
688
+ * every reducer that mutates ONE message must go through this or the reader
689
+ * sees stale edits/reactions/delivery state while away from the live tail.
690
+ * `matches` is an id, or a predicate when a reducer also matches on xmppId.
691
+ */
692
+ const forEachMessageCopy = (
693
+ state: RoomMessagesState,
694
+ jid: string,
695
+ matches: string | ((message: IMessage) => boolean),
696
+ fn: (message: IMessage) => void
697
+ ) => {
698
+ const pred =
699
+ typeof matches === 'function'
700
+ ? matches
701
+ : (message: IMessage) => message.id === matches;
702
+ const live = state.rooms[jid]?.messages;
703
+ if (Array.isArray(live)) {
704
+ for (const message of live) {if (pred(message)) {fn(message);}}
705
+ }
706
+ const win = state.jumpWindow;
707
+ if (win && win.roomJID === jid && Array.isArray(win.messages)) {
708
+ for (const message of win.messages) {if (pred(message)) {fn(message);}}
709
+ }
710
+ };
711
+
712
+ /**
713
+ * usersCnt after a /chats/my refresh. The API reports the true total in
714
+ * `usersCnt` but at most 30 `members` for a big room, so the members array
715
+ * length is only a floor; and a count already known (a larger one from an
716
+ * earlier response or a live join) is never lowered by a refresh.
717
+ */
718
+ const resolveIncomingUsersCnt = (
719
+ incoming: Pick<IRoom, 'members' | 'usersCnt'> & { participants?: number },
720
+ existing: IRoom | undefined
721
+ ): number => {
722
+ const apiTotal =
723
+ typeof incoming.usersCnt === 'number' && incoming.usersCnt > 0
724
+ ? incoming.usersCnt
725
+ : 0;
726
+ const membersLen = Array.isArray(incoming.members)
727
+ ? incoming.members.length
728
+ : 0;
729
+ const next = Math.max(apiTotal, membersLen);
730
+ return Math.max(next, existing ? getRoomUserCount(existing) : 0);
731
+ };
732
+
733
+ /**
734
+ * The server-reported unread snapshot of a room after an incoming /chats/my
735
+ * item: the freshest API value wins while the user is not looking at the
736
+ * room; one being read right now keeps none. Absent from the response =
737
+ * keep what an earlier, richer response set.
738
+ */
739
+ const resolveApiUnread = (
740
+ state: RoomMessagesState,
741
+ jid: string,
742
+ incoming: Partial<IRoom>,
743
+ existing: IRoom | undefined
744
+ ): Pick<IRoom, 'apiUnreadCount' | 'apiUnreadSeededAt'> => {
745
+ if (state.activeRoomJID === jid || state.visibleRoomJID === jid) {
746
+ return { apiUnreadCount: undefined, apiUnreadSeededAt: undefined };
747
+ }
748
+ if (incoming.apiUnreadCount !== undefined) {
749
+ return {
750
+ apiUnreadCount: incoming.apiUnreadCount,
751
+ apiUnreadSeededAt: incoming.apiUnreadSeededAt ?? Date.now(),
752
+ };
753
+ }
754
+ return {
755
+ apiUnreadCount: existing?.apiUnreadCount,
756
+ apiUnreadSeededAt: existing?.apiUnreadSeededAt,
757
+ };
758
+ };
759
+
510
760
  export const addRoomViaApi = createAsyncThunk(
511
761
  'roomMessages/addRoomViaApi',
512
762
  async (
@@ -549,6 +799,7 @@ const reducers = {
549
799
  },
550
800
  deleteRoom(state: WritableDraft<RoomMessagesState>, action: PayloadAction<{ jid: string }>) {
551
801
  const { jid } = action.payload;
802
+ if (!isSafeKey(jid)) {return;}
552
803
  if (state.rooms[jid]) {
553
804
  delete state.rooms[jid];
554
805
  if (state.visibleRoomJID === jid) {
@@ -561,20 +812,41 @@ const reducers = {
561
812
  action: PayloadAction<{ jid: string; updates: Partial<IRoom> }>
562
813
  ) {
563
814
  const { jid, updates } = action.payload;
815
+ if (!isSafeKey(jid)) {return;}
564
816
  const existingRoom = state.rooms[jid];
817
+ if (!existingRoom) {return;}
565
818
 
566
- if (existingRoom) {
567
- state.rooms[jid] = {
568
- ...existingRoom,
569
- ...updates,
570
- };
819
+ const merged: IRoom = { ...existingRoom, ...updates };
820
+
821
+ if (typeof updates.usersCnt === 'number') {
822
+ // An explicit count (a live join or leave) is taken as given, with the
823
+ // known members as a floor.
824
+ const newMembers = Array.isArray(updates.members)
825
+ ? updates.members
826
+ : existingRoom.members;
827
+ const floor = Array.isArray(newMembers) ? newMembers.length : 0;
828
+ merged.usersCnt = Math.max(updates.usersCnt, floor);
829
+ } else if (Array.isArray(updates.members)) {
830
+ // A members-only refresh of a truncated big room (usersCnt > the
831
+ // first page) must not collapse the true total to the page length.
832
+ const prevMembers = Array.isArray(existingRoom.members)
833
+ ? existingRoom.members.length
834
+ : 0;
835
+ const prevCnt =
836
+ typeof existingRoom.usersCnt === 'number' ? existingRoom.usersCnt : 0;
837
+ merged.usersCnt =
838
+ prevCnt > prevMembers
839
+ ? Math.max(prevCnt, updates.members.length)
840
+ : updates.members.length;
571
841
  }
842
+ state.rooms[jid] = merged;
572
843
  },
573
844
  setRoomMessages(
574
845
  state: WritableDraft<RoomMessagesState>,
575
846
  action: PayloadAction<{ roomJID: string; messages: IMessage[] }>
576
847
  ) {
577
848
  const { roomJID, messages } = action.payload;
849
+ if (!isSafeKey(roomJID)) {return;}
578
850
  if (state.rooms[roomJID]) {
579
851
  // Cap to the runtime limit on replace too — guards against a
580
852
  // single MAM page returning more than the limit (would balloon
@@ -591,6 +863,7 @@ const reducers = {
591
863
  action: PayloadAction<{ roomJID: string; messageId: string }>
592
864
  ) {
593
865
  const { roomJID, messageId } = action.payload;
866
+ if (!isSafeKey(roomJID)) {return;}
594
867
  const room = state.rooms[roomJID];
595
868
  if (!room?.messages) {return;}
596
869
  // The "New messages" divider is a transient UI marker, not a real
@@ -603,11 +876,9 @@ const reducers = {
603
876
  if (idx !== -1) {room.messages.splice(idx, 1);}
604
877
  return;
605
878
  }
606
- room.messages.map((message) => {
607
- if (message.id === messageId) {
608
- message.isDeleted = true;
609
- message.reaction = undefined;
610
- }
879
+ forEachMessageCopy(state, roomJID, messageId, (message) => {
880
+ message.isDeleted = true;
881
+ message.reaction = undefined;
611
882
  });
612
883
  },
613
884
  setEditAction: (state: WritableDraft<RoomMessagesState>, action: PayloadAction<EditAction | undefined>) => {
@@ -631,17 +902,58 @@ const reducers = {
631
902
  }>
632
903
  ) {
633
904
  const { roomJID, messageId, text } = action.payload;
634
- if (state.rooms[roomJID]) {
635
- state.rooms[roomJID].messages.map((message) => {
636
- if (message.id === messageId) {
637
- message.body = text;
638
- // Flag the correction so the bubble can render an "edited" marker.
639
- // Covers both the author's own edit and edits from other users —
640
- // every <replace> echo flows through here.
641
- message.isEdited = true;
642
- }
643
- });
905
+ if (!isSafeKey(roomJID)) {return;}
906
+ // Edited in place on every copy (live list and jump window).
907
+ forEachMessageCopy(state, roomJID, messageId, (message) => {
908
+ message.body = text;
909
+ // Flag the correction so the bubble can render an "edited" marker.
910
+ // Covers both the author's own edit and edits from other users -
911
+ // every <replace> echo flows through here.
912
+ message.isEdited = true;
913
+ });
914
+ },
915
+ /**
916
+ * Hard removal, unlike `deleteRoomMessage`'s tombstone. For optimistic
917
+ * messages that never made it onto the wire.
918
+ */
919
+ removeRoomMessage(
920
+ state: WritableDraft<RoomMessagesState>,
921
+ action: PayloadAction<{ roomJID: string; messageId: string }>
922
+ ) {
923
+ const { roomJID, messageId } = action.payload;
924
+ if (!isSafeKey(roomJID)) {return;}
925
+ if (state.rooms[roomJID]?.messages) {
926
+ state.rooms[roomJID].messages = state.rooms[roomJID].messages.filter(
927
+ (message) => message.id !== messageId
928
+ );
644
929
  }
930
+ const win = state.jumpWindow;
931
+ if (win && win.roomJID === roomJID) {
932
+ win.messages = win.messages.filter(
933
+ (message) => message.id !== messageId
934
+ );
935
+ }
936
+ },
937
+ /**
938
+ * Caches one translation onto the message it belongs to, keyed by the
939
+ * locale tag the translate service echoed back. Applied to the live
940
+ * copy and the jump window's copy.
941
+ */
942
+ setMessageTranslation(
943
+ state: WritableDraft<RoomMessagesState>,
944
+ action: PayloadAction<{
945
+ roomJID: string;
946
+ messageId: string;
947
+ locale: string;
948
+ entry: TranslationObject[string];
949
+ }>
950
+ ) {
951
+ const { roomJID, messageId, locale, entry } = action.payload;
952
+ if (!isSafeKey(roomJID) || !isSafeKey(locale)) {return;}
953
+ forEachMessageCopy(state, roomJID, messageId, (message) => {
954
+ if (!message.translations) {message.translations = {};}
955
+ message.translations[locale] = entry;
956
+ });
645
957
  },
646
958
  addRoomMessage(
647
959
  state: WritableDraft<RoomMessagesState>,
@@ -652,12 +964,15 @@ const reducers = {
652
964
  }>
653
965
  ) {
654
966
  const { roomJID, message, start } = action.payload;
967
+ if (!isSafeKey(roomJID)) {return;}
655
968
  const lengthBefore = state.rooms[roomJID]?.messages?.length || 0;
656
969
  const outcome = insertRoomMessage(state, roomJID, message, start);
657
970
  if (outcome.grew) {
658
971
  capAfterGrowth(state.rooms[roomJID].messages!, lengthBefore, outcome.atHead);
659
972
  }
660
973
  bumpLastMessageTimestamp(state, roomJID, message);
974
+ // Keep the jump window's copy in step with the live list.
975
+ syncJumpWindowWithMessage(state, roomJID, message, start, true);
661
976
  },
662
977
  /**
663
978
  * A whole page of messages (a MAM history page) in ONE action: the same
@@ -674,7 +989,7 @@ const reducers = {
674
989
  }>
675
990
  ) {
676
991
  const { roomJID, messages, start } = action.payload;
677
- if (!state.rooms[roomJID] || !Array.isArray(messages)) {
992
+ if (!isSafeKey(roomJID) || !state.rooms[roomJID] || !Array.isArray(messages)) {
678
993
  return;
679
994
  }
680
995
  const lengthBefore = state.rooms[roomJID].messages?.length || 0;
@@ -690,6 +1005,9 @@ const reducers = {
690
1005
  if (!newest || Number(message?.id) > Number(newest.id)) {
691
1006
  newest = message;
692
1007
  }
1008
+ // History pages never append to a reader's jump window (they are not
1009
+ // "the newest message"); they only refresh copies it already holds.
1010
+ syncJumpWindowWithMessage(state, roomJID, message, start, false);
693
1011
  }
694
1012
  if (grew) {
695
1013
  // A page that only prepended older history is never trimmed.
@@ -701,6 +1019,7 @@ const reducers = {
701
1019
  },
702
1020
  deleteAllRooms(state: WritableDraft<RoomMessagesState>) {
703
1021
  state.rooms = {};
1022
+ state.jumpWindow = null;
704
1023
  state.visibleRoomJID = null;
705
1024
  state.privateStoreMarkers = {};
706
1025
  state.readBoundaries = {};
@@ -715,6 +1034,7 @@ const reducers = {
715
1034
  }>
716
1035
  ) {
717
1036
  const { chatJID, composing, composingList } = action.payload;
1037
+ if (!isSafeKey(chatJID) || !state.rooms[chatJID]) {return;}
718
1038
  state.rooms[chatJID].composing = composing;
719
1039
  state.rooms[chatJID].composingList = composingList;
720
1040
  },
@@ -723,7 +1043,7 @@ const reducers = {
723
1043
  action: PayloadAction<{ chatJID?: string; loading: boolean; loadingText?: string }>
724
1044
  ) => {
725
1045
  const { chatJID, loading, loadingText } = action.payload;
726
- if (chatJID && state.rooms?.[chatJID]) {
1046
+ if (isSafeKey(chatJID) && state.rooms?.[chatJID]) {
727
1047
  state.rooms[chatJID].isLoading = loading;
728
1048
  }
729
1049
  state.isLoading = loading;
@@ -740,7 +1060,7 @@ const reducers = {
740
1060
  action: PayloadAction<{ chatJID: string; timestamp: number }>
741
1061
  ) => {
742
1062
  const { chatJID, timestamp } = action.payload;
743
- if (state.rooms[chatJID]) {
1063
+ if (isSafeKey(chatJID) && state.rooms[chatJID]) {
744
1064
  state.rooms[chatJID].lastViewedTimestamp = timestamp;
745
1065
  // A non-positive timestamp means "no persisted marker yet" and
746
1066
  // must not inflate unread from history that predates the first
@@ -793,7 +1113,7 @@ const reducers = {
793
1113
  if (!state.privateStoreMarkers) {state.privateStoreMarkers = {};}
794
1114
  for (const jid of Object.keys(markers)) {
795
1115
  const ts = Number(markers[jid]);
796
- if (!jid || !Number.isFinite(ts) || ts <= 0) {continue;}
1116
+ if (!isSafeKey(jid) || !Number.isFinite(ts) || ts <= 0) {continue;}
797
1117
 
798
1118
  const room = state.rooms[jid];
799
1119
  const newestKnownMs = room ? newestAckedMessageMs(room.messages) : 0;
@@ -839,7 +1159,7 @@ const reducers = {
839
1159
  action: PayloadAction<{ jid: string; ts: number | null }>
840
1160
  ) => {
841
1161
  const { jid, ts } = action.payload;
842
- if (!jid) {return;}
1162
+ if (!isSafeKey(jid)) {return;}
843
1163
  if (!state.readBoundaries) {state.readBoundaries = {};}
844
1164
  if (typeof ts === 'number' && Number.isFinite(ts) && ts > 0) {
845
1165
  state.readBoundaries[jid] = ts;
@@ -858,14 +1178,14 @@ const reducers = {
858
1178
  action: PayloadAction<{ jid: string }>
859
1179
  ) => {
860
1180
  const { jid } = action.payload;
861
- if (jid && state.readBoundaries) {delete state.readBoundaries[jid];}
1181
+ if (isSafeKey(jid) && state.readBoundaries) {delete state.readBoundaries[jid];}
862
1182
  },
863
1183
  setRoomRole: (
864
1184
  state: WritableDraft<RoomMessagesState>,
865
1185
  action: PayloadAction<{ chatJID: string; role: string }>
866
1186
  ) => {
867
1187
  const { chatJID, role } = action.payload;
868
- if (state.rooms[chatJID]) {
1188
+ if (isSafeKey(chatJID) && state.rooms[chatJID]) {
869
1189
  state.rooms[chatJID].role = role;
870
1190
  }
871
1191
  },
@@ -874,7 +1194,7 @@ const reducers = {
874
1194
  action: PayloadAction<{ value: boolean; chatJID?: string }>
875
1195
  ) => {
876
1196
  const { value, chatJID } = action.payload;
877
- if (chatJID) {
1197
+ if (isSafeKey(chatJID) && state.rooms[chatJID]) {
878
1198
  state.rooms[chatJID].noMessages = value;
879
1199
  }
880
1200
  },
@@ -884,16 +1204,30 @@ const reducers = {
884
1204
  ) => {
885
1205
  // Accept null/empty so callers can clear the active room (e.g.
886
1206
  // back button from chat → return to RoomList). Mirrors web.
887
- state.activeRoomJID = action.payload.roomJID || '';
1207
+ const { roomJID } = action.payload;
1208
+ if (roomJID && !isSafeKey(roomJID)) {return;}
1209
+ state.activeRoomJID = roomJID || '';
1210
+ // Opening a room reads it: the server's earlier unread count no longer
1211
+ // applies (the unread middleware would otherwise bring it back the
1212
+ // moment the user leaves the room again).
1213
+ if (roomJID && state.rooms[roomJID]?.apiUnreadCount !== undefined) {
1214
+ state.rooms[roomJID].apiUnreadCount = undefined;
1215
+ state.rooms[roomJID].apiUnreadSeededAt = undefined;
1216
+ state.rooms[roomJID].unreadMessages = 0;
1217
+ }
888
1218
  },
889
1219
  setVisibleRoom: (
890
1220
  state: WritableDraft<RoomMessagesState>,
891
1221
  action: PayloadAction<{ roomJID: string | null }>
892
1222
  ) => {
893
- state.visibleRoomJID = action.payload.roomJID || null;
1223
+ const requested = action.payload.roomJID;
1224
+ if (requested && !isSafeKey(requested)) {return;}
1225
+ state.visibleRoomJID = requested || null;
894
1226
  const jid = state.visibleRoomJID;
895
1227
  if (jid && state.rooms[jid]) {
896
1228
  state.rooms[jid].unreadMessages = 0;
1229
+ state.rooms[jid].apiUnreadCount = undefined;
1230
+ state.rooms[jid].apiUnreadSeededAt = undefined;
897
1231
  }
898
1232
  },
899
1233
  clearVisibleRoom: (state: WritableDraft<RoomMessagesState>) => {
@@ -920,6 +1254,116 @@ const reducers = {
920
1254
  clearPendingNotificationJid: (state: WritableDraft<RoomMessagesState>) => {
921
1255
  state.pendingNotificationJid = null;
922
1256
  },
1257
+ requestJumpToMessage: (
1258
+ state: WritableDraft<RoomMessagesState>,
1259
+ action: PayloadAction<{
1260
+ roomJID: string;
1261
+ ids: string[];
1262
+ createdAt?: string;
1263
+ body?: string;
1264
+ preview?: ArchivedMessage;
1265
+ }>
1266
+ ) => {
1267
+ const ids = action.payload.ids.filter(Boolean);
1268
+ const canMatchByContent = Boolean(
1269
+ action.payload.createdAt && action.payload.body
1270
+ );
1271
+ if (!action.payload.roomJID || (ids.length === 0 && !canMatchByContent)) {
1272
+ return;
1273
+ }
1274
+ state.pendingJump = {
1275
+ roomJID: action.payload.roomJID,
1276
+ ids,
1277
+ createdAt: action.payload.createdAt,
1278
+ body: action.payload.body,
1279
+ preview: action.payload.preview,
1280
+ at: Date.now(),
1281
+ };
1282
+ },
1283
+ clearPendingJump: (state: WritableDraft<RoomMessagesState>) => {
1284
+ state.pendingJump = null;
1285
+ },
1286
+ showArchivedMessage: (
1287
+ state: WritableDraft<RoomMessagesState>,
1288
+ action: PayloadAction<ArchivedMessage>
1289
+ ) => {
1290
+ state.archivedMessage = action.payload;
1291
+ },
1292
+ clearArchivedMessage: (state: WritableDraft<RoomMessagesState>) => {
1293
+ state.archivedMessage = null;
1294
+ },
1295
+ setJumpWindow: (
1296
+ state: WritableDraft<RoomMessagesState>,
1297
+ action: PayloadAction<JumpWindow>
1298
+ ) => {
1299
+ state.jumpWindow = {
1300
+ ...action.payload,
1301
+ messages: mergeWindowMessages(action.payload.messages, []),
1302
+ };
1303
+ },
1304
+ prependJumpWindowMessages: (
1305
+ state: WritableDraft<RoomMessagesState>,
1306
+ action: PayloadAction<{
1307
+ roomJID: string;
1308
+ messages: IMessage[];
1309
+ olderCursor: number | null;
1310
+ hasOlder: boolean;
1311
+ }>
1312
+ ) => {
1313
+ const win = state.jumpWindow;
1314
+ if (!win || win.roomJID !== action.payload.roomJID) {return;}
1315
+ win.messages = mergeWindowMessages(action.payload.messages, win.messages);
1316
+ win.olderCursor = action.payload.olderCursor;
1317
+ win.hasOlder = action.payload.hasOlder;
1318
+ if (win.messages.length > JUMP_WINDOW_MAX_MESSAGES) {
1319
+ // Reader is going up: drop the far (newest) side and page it again if
1320
+ // they come back down.
1321
+ win.messages = win.messages.slice(0, JUMP_WINDOW_MAX_MESSAGES);
1322
+ const newest = Number(win.messages[win.messages.length - 1].id);
1323
+ win.newerCursor = Number.isFinite(newest) ? newest : win.newerCursor;
1324
+ win.hasNewer = true;
1325
+ }
1326
+ },
1327
+ appendJumpWindowMessages: (
1328
+ state: WritableDraft<RoomMessagesState>,
1329
+ action: PayloadAction<{
1330
+ roomJID: string;
1331
+ messages: IMessage[];
1332
+ newerCursor: number | null;
1333
+ hasNewer: boolean;
1334
+ }>
1335
+ ) => {
1336
+ const win = state.jumpWindow;
1337
+ if (!win || win.roomJID !== action.payload.roomJID) {return;}
1338
+ win.messages = mergeWindowMessages(win.messages, action.payload.messages);
1339
+ win.newerCursor = action.payload.newerCursor;
1340
+ win.hasNewer = action.payload.hasNewer;
1341
+ if (win.messages.length > JUMP_WINDOW_MAX_MESSAGES) {
1342
+ win.messages = win.messages.slice(-JUMP_WINDOW_MAX_MESSAGES);
1343
+ const oldest = Number(win.messages[0].id);
1344
+ win.olderCursor = Number.isFinite(oldest) ? oldest : win.olderCursor;
1345
+ win.hasOlder = true;
1346
+ }
1347
+ },
1348
+ clearJumpWindow: (state: WritableDraft<RoomMessagesState>) => {
1349
+ state.jumpWindow = null;
1350
+ },
1351
+ setJoiningRoom: (
1352
+ state: WritableDraft<RoomMessagesState>,
1353
+ action: PayloadAction<string>
1354
+ ) => {
1355
+ state.joiningRoomJID = action.payload || null;
1356
+ },
1357
+ // Only clears when it still names that room, so a slow join for a room
1358
+ // the user already left cannot wipe the join of the current one.
1359
+ clearJoiningRoom: (
1360
+ state: WritableDraft<RoomMessagesState>,
1361
+ action: PayloadAction<string>
1362
+ ) => {
1363
+ if (state.joiningRoomJID === action.payload) {
1364
+ state.joiningRoomJID = null;
1365
+ }
1366
+ },
923
1367
  /**
924
1368
  * Stamp a message in `state.rooms[roomJID].messages` with an updated
925
1369
  * reactions list. The reactionsMiddleware listens for this action to
@@ -938,15 +1382,28 @@ const reducers = {
938
1382
  ) => {
939
1383
  const { roomJID, messageId, reactions, from, data, latestReactionTimestamp } =
940
1384
  action.payload;
941
- if (!from) {return;}
942
- const room = state.rooms[roomJID];
943
- const target = room?.messages?.find(
944
- (msg) => msg?.id === messageId || msg?.xmppId === messageId
1385
+ if (!isSafeKey(roomJID) || !from) {return;}
1386
+ // `fromId` becomes a property name on the message's reaction map.
1387
+ const fromId = String(from).split('/')[0].split('@')[0];
1388
+ if (!isSafeKey(fromId)) {return;}
1389
+ let found = false;
1390
+ // Applied on every stored copy (live list and jump window).
1391
+ forEachMessageCopy(
1392
+ state,
1393
+ roomJID,
1394
+ (msg) => msg?.id === messageId || msg?.xmppId === messageId,
1395
+ (message) => {
1396
+ found = true;
1397
+ applyReactionToMessage(
1398
+ message as IMessage,
1399
+ from,
1400
+ reactions,
1401
+ data,
1402
+ latestReactionTimestamp
1403
+ );
1404
+ }
945
1405
  );
946
- if (target) {
947
- applyReactionToMessage(target as IMessage, from, reactions, data, latestReactionTimestamp);
948
- return;
949
- }
1406
+ if (found) {return;}
950
1407
  // The message is not loaded (yet): keep the reaction for it.
951
1408
  if (!state.pendingReactions) {state.pendingReactions = {};}
952
1409
  const byMessage = (state.pendingReactions[roomJID] ||= {});
@@ -964,30 +1421,39 @@ const reducers = {
964
1421
  state.isUnreadSyncing = false;
965
1422
  state.privateStoreMarkers = {};
966
1423
  state.readBoundaries = {};
1424
+ state.usersSet = {};
1425
+ state.jumpWindow = null;
1426
+ state.archivedMessage = null;
1427
+ state.pendingJump = null;
967
1428
  },
968
1429
  setActiveMessage: (
969
1430
  state: WritableDraft<RoomMessagesState>,
970
1431
  action: PayloadAction<{ id: string; chatJID: string }>
971
1432
  ) => {
972
1433
  const { id, chatJID } = action.payload;
973
-
974
- state.rooms[chatJID].messages.map((message) => {
975
- if (message.id === id) {
976
- message.activeMessage = true;
977
- } else {
978
- message.activeMessage = false;
979
- }
980
- });
1434
+ if (!isSafeKey(chatJID) || !state.rooms[chatJID]) {return;}
1435
+ const flag = (message: IMessage) => {
1436
+ message.activeMessage = message.id === id;
1437
+ };
1438
+ state.rooms[chatJID].messages.forEach(flag);
1439
+ if (state.jumpWindow?.roomJID === chatJID) {
1440
+ state.jumpWindow.messages.forEach(flag);
1441
+ }
981
1442
  },
982
1443
  setCloseActiveMessage: (
983
1444
  state: WritableDraft<RoomMessagesState>,
984
1445
  action: PayloadAction<{ chatJID: string }>
985
1446
  ) => {
986
1447
  const { chatJID } = action.payload;
987
-
988
- state.rooms[chatJID].messages.map((message) => {
1448
+ if (!isSafeKey(chatJID) || !state.rooms[chatJID]) {return;}
1449
+ state.rooms[chatJID].messages.forEach((message) => {
989
1450
  message.activeMessage = false;
990
1451
  });
1452
+ if (state.jumpWindow?.roomJID === chatJID) {
1453
+ state.jumpWindow.messages.forEach((message) => {
1454
+ message.activeMessage = false;
1455
+ });
1456
+ }
991
1457
  },
992
1458
  /**
993
1459
  * Batched update used by the history preload scheduler. Each patch is
@@ -1008,22 +1474,15 @@ const reducers = {
1008
1474
  ...existing,
1009
1475
  ...room,
1010
1476
  title: room.title || existing?.title || room.title,
1011
- usersCnt: (() => {
1012
- const incoming =
1013
- typeof room.usersCnt === 'number' && room.usersCnt > 0
1014
- ? room.usersCnt
1015
- : 0;
1016
- const previous =
1017
- typeof existing?.usersCnt === 'number' && existing.usersCnt > 0
1018
- ? existing.usersCnt
1019
- : 0;
1020
- if (incoming === 0 && previous === 0) return room.usersCnt;
1021
- return Math.max(incoming, previous);
1022
- })(),
1477
+ usersCnt: resolveIncomingUsersCnt(room, existing),
1023
1478
  icon: room.icon ?? existing?.icon,
1024
1479
  messages:
1025
1480
  existingMessages.length > 0 ? existingMessages : incomingMessages,
1481
+ // A seed for the room-list preview (the API's `lastMessage`): take the
1482
+ // freshest value, never let a refresh without one erase it.
1483
+ lastMessage: room.lastMessage ?? existing?.lastMessage,
1026
1484
  unreadMessages: existing?.unreadMessages ?? room.unreadMessages ?? 0,
1485
+ ...resolveApiUnread(state, room.jid, room, existing),
1027
1486
  lastViewedTimestamp:
1028
1487
  existing?.lastViewedTimestamp ?? room.lastViewedTimestamp ?? 0,
1029
1488
  unreadBaselineTimestamp:
@@ -1047,8 +1506,9 @@ const reducers = {
1047
1506
  ) => {
1048
1507
  const { rooms } = action.payload;
1049
1508
  for (const patch of rooms) {
1509
+ if (!isSafeKey(patch?.jid)) {continue;}
1050
1510
  const room = state.rooms[patch.jid];
1051
- if (!room) {continue;}
1511
+ if (!room || typeof room !== 'object' || Array.isArray(room)) {continue;}
1052
1512
  if (typeof patch.historyPreloadState !== 'undefined') {
1053
1513
  room.historyPreloadState = patch.historyPreloadState;
1054
1514
  }
@@ -1058,6 +1518,9 @@ const reducers = {
1058
1518
  if (typeof patch.historyComplete !== 'undefined') {
1059
1519
  room.historyComplete = patch.historyComplete;
1060
1520
  }
1521
+ if (patch.messageStats) {
1522
+ room.messageStats = { ...room.messageStats, ...patch.messageStats };
1523
+ }
1061
1524
  if (Array.isArray(patch.messages)) {
1062
1525
  for (const m of patch.messages) {takePendingReactions(state, patch.jid, m);}
1063
1526
  // Merge the fetched page into cache by message id rather than
@@ -1076,7 +1539,13 @@ const reducers = {
1076
1539
  state: WritableDraft<RoomMessagesState>,
1077
1540
  action: PayloadAction<{ members: Record<string, any> }>
1078
1541
  ) {
1079
- state.usersSet = { ...(state.usersSet || {}), ...action.payload.members };
1542
+ // A real merge with a cap: fresh entries win (and move to the newest
1543
+ // position), entries fetched lazily earlier stay, the oldest-inserted
1544
+ // are evicted past USERS_SET_CAP. Unsafe keys are skipped.
1545
+ if (!state.usersSet || typeof state.usersSet !== 'object') {
1546
+ state.usersSet = {};
1547
+ }
1548
+ mergeIntoUsersSet(state.usersSet, action.payload?.members || {});
1080
1549
  },
1081
1550
  };
1082
1551
 
@@ -1197,8 +1666,21 @@ export const {
1197
1666
  setUnreadCounts,
1198
1667
  setPendingNotificationJid,
1199
1668
  clearPendingNotificationJid,
1669
+ requestJumpToMessage,
1670
+ clearPendingJump,
1671
+ setJoiningRoom,
1672
+ clearJoiningRoom,
1200
1673
  setReactions,
1201
1674
  mergeUsersSet,
1675
+ removeRoomMessage,
1676
+ addRoomFromApi,
1677
+ setMessageTranslation,
1678
+ showArchivedMessage,
1679
+ clearArchivedMessage,
1680
+ setJumpWindow,
1681
+ prependJumpWindowMessages,
1682
+ appendJumpWindowMessages,
1683
+ clearJumpWindow,
1202
1684
  } = roomsStore.actions;
1203
1685
 
1204
1686
  export default roomsStore.reducer;