@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,11 +4,11 @@
4
4
 
5
5
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
6
6
  import { View, Text, StyleSheet, FlatList, Image, TouchableOpacity } from 'react-native';
7
- import { msgSortableMs } from '../../roomStore/roomsSlice';
7
+ import { useDispatch, useSelector, useStore } from 'react-redux';
8
+ import { appendJumpWindowMessages, clearJumpWindow, msgSortableMs, prependJumpWindowMessages, setJumpWindow } from '../../roomStore/roomsSlice';
8
9
  import Composing from '../styled/StyledInputComponents/Composing';
9
10
  import TreadLabel from '../styled/TreadLabel';
10
11
  import { MessageContainer } from './MessageContainer';
11
- import { useSelector } from 'react-redux';
12
12
  import Loader from '../styled/Loader';
13
13
  import { ArowDownIcon } from '../../assets/icons';
14
14
  import CustomTypingIndicator from '../styled/StyledInputComponents/CustomTypingIndicator';
@@ -16,7 +16,43 @@ import { getIconColor } from '../../helpers/getIconColor';
16
16
  import { getChatBackgroundColor } from '../../helpers/getChatBackground';
17
17
  import { useTheme } from '../../hooks/useTheme';
18
18
  import { isOwnMessage } from '../../helpers/isOwnMessage';
19
+ import { useJumpToMessage } from '../../hooks/useJumpToMessage';
20
+ import { scrollRetryOffset } from '../../helpers/jumpToMessage';
21
+ import { loadJumpWindow, loadNewerWindowPage, loadOlderWindowPage } from '../../helpers/jumpWindow';
22
+ import { mergeById, replyParentId, useJumpThread } from '../../helpers/jumpThread';
23
+ import { openThreadForJump } from '../../helpers/openThreadForJump';
24
+ import { setBubbleHighlight } from '../../helpers/bubbleHighlight';
25
+ import { requestSendersOf } from '../../helpers/userResolver';
26
+ import { useT } from '../../i18n/useT';
27
+
28
+ // Older history is requested while the reader is still this far from the top
29
+ // (or 3 screens, whichever is more), so a normal scroll finds it already there
30
+ // and never sees a spinner. The spinner is for a reader who outruns the
31
+ // request and reaches the very top while it is in flight.
19
32
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
33
+ export const PREFETCH_MIN_DISTANCE = 1200;
34
+ export const PREFETCH_SCREENS = 3;
35
+ // FlatList's own trigger distance, in screens: generous on purpose.
36
+ const END_REACHED_THRESHOLD = 4;
37
+ export const HISTORY_PAGE_SIZE = 100;
38
+ // Consecutive history pages that added nothing to the list before auto-paging
39
+ // stops (a reader scroll re-arms it).
40
+ export const MAX_IDLE_AUTO_PAGES = 40;
41
+ // A request that settled without moving the oldest message or the cursor gets
42
+ // exactly one more try after this long.
43
+ export const NO_PROGRESS_RETRY_MS = 1000;
44
+ const AUTO_FILL_DELAY_MS = 100;
45
+ // After a page lands, the content size change it causes is not new content.
46
+ const GROWTH_GUARD_MS = 500;
47
+ // A window page that failed is not retried in a hot loop.
48
+ export const WINDOW_RETRY_AFTER_FAILURE_MS = 3000;
49
+ // The scroll that centres the target must not be read as the reader scrolling
50
+ // toward an edge (it would page, or leave the window).
51
+ export const WINDOW_SETTLE_MS = 600;
52
+ export const WINDOW_EXIT_GRACE_MS = 1500;
53
+
54
+ /** The slice of the XMPP client the list needs. */
55
+
20
56
  const mainIdCache = new WeakMap();
21
57
  const mainMessageIdOf = m => {
22
58
  if (!m?.mainMessage) {
@@ -69,17 +105,21 @@ const MessageList = ({
69
105
  loading,
70
106
  isReply,
71
107
  activeMessage,
108
+ client,
72
109
  onReadBoundaryChange
73
110
  }) => {
74
111
  const room = useSelector(state => state.rooms.rooms?.[roomJID]);
75
112
  const composing = room?.composing;
76
113
  const composingList = room?.composingList;
77
114
  const messages = room?.messages || EMPTY_LIST;
115
+ const historyComplete = room?.historyComplete;
78
116
  const theme = useTheme();
117
+ const t = useT();
118
+ const dispatch = useDispatch();
119
+ const reduxStore = useStore();
79
120
  const [isUserAtBottom, setIsUserAtBottom] = useState(true);
80
121
  const [showNewMessageIndicator, setShowNewMessageIndicator] = useState(false);
81
122
  const [isLoadingMore, setIsLoadingMore] = useState(false);
82
- const [isScrollBlocked, setIsScrollBlocked] = useState(false);
83
123
  // Count of messages that arrived while the user was scrolled up.
84
124
  // Renders as a badge on the scroll-to-bottom arrow so they know how
85
125
  // much they're missing without having to scroll to find out.
@@ -96,10 +136,57 @@ const MessageList = ({
96
136
  // to jump back to the bottom when a new message landed mid-scroll.
97
137
  const isUserAtBottomRef = useRef(true);
98
138
  const hasUserScrolledRef = useRef(false);
139
+ const mountedRef = useRef(true);
140
+ useEffect(() => () => {
141
+ mountedRef.current = false;
142
+ }, []);
99
143
  const flatListRef = useRef(null);
144
+
145
+ // Viewport metrics, kept in refs: the loaders read them from timers and
146
+ // FlatList callbacks, never from a stale render closure.
147
+ const scrollOffsetRef = useRef(0);
148
+ const contentHeightRef = useRef(0);
149
+ const viewportHeightRef = useRef(0);
150
+
151
+ // A short slice of archive around a far jump target, shown instead of the
152
+ // live list until the reader returns to the latest messages. Not for threads.
153
+ const jumpWindow = useSelector(state => state.rooms.jumpWindow);
154
+ const joiningRoomJID = useSelector(state => typeof state.rooms.joiningRoomJID === 'string' ? state.rooms.joiningRoomJID : null);
155
+ const windowActive = !isReply && jumpWindow?.roomJID === roomJID;
156
+ const windowActiveRef = useRef(windowActive);
157
+ windowActiveRef.current = windowActive;
158
+ const loadingPropRef = useRef(loading);
159
+ loadingPropRef.current = loading;
160
+
161
+ // A thread list also sees what a jump to one of its replies brought in (and
162
+ // any window on screen): those replies may be older than the live list.
163
+ const jumpThread = useJumpThread();
164
+ const threadExtras = useMemo(() => {
165
+ if (!isReply || !activeMessage) {
166
+ return null;
167
+ }
168
+ const fromWindow = jumpWindow?.roomJID === roomJID ? jumpWindow.messages : [];
169
+ const fromJump = jumpThread && jumpThread.roomJID === roomJID && jumpThread.parentId === String(activeMessage.id) ? jumpThread.replies : [];
170
+ if (fromWindow.length === 0 && fromJump.length === 0) {
171
+ return null;
172
+ }
173
+ return mergeById(fromWindow, fromJump);
174
+ }, [isReply, activeMessage, jumpWindow, jumpThread, roomJID]);
175
+ const sourceMessages = useMemo(() => {
176
+ if (windowActive && jumpWindow) {
177
+ return jumpWindow.messages;
178
+ }
179
+ if (threadExtras) {
180
+ return mergeById(messages ?? [], threadExtras);
181
+ }
182
+ return messages;
183
+ }, [windowActive, jumpWindow, threadExtras, messages]);
100
184
  const addReplyMessages = useMemo(() => {
185
+ // Index replies by their parent once instead of filtering the whole list
186
+ // per message. Only a message that has replies is copied, so the others
187
+ // keep their identity (memoized rows do not re-render).
101
188
  const repliesByParent = new Map();
102
- for (const mess of messages) {
189
+ for (const mess of sourceMessages) {
103
190
  if (!mess?.mainMessage || mess.isDeleted) {
104
191
  continue;
105
192
  }
@@ -115,16 +202,16 @@ const MessageList = ({
115
202
  }
116
203
  }
117
204
  if (repliesByParent.size === 0) {
118
- return messages;
205
+ return sourceMessages;
119
206
  }
120
- return messages.map(message => {
207
+ return sourceMessages.map(message => {
121
208
  const reply = repliesByParent.get(String(message.id));
122
209
  return reply ? {
123
210
  ...message,
124
211
  reply
125
212
  } : message;
126
213
  });
127
- }, [messages]);
214
+ }, [sourceMessages]);
128
215
  const memoizedMessages = useMemo(() => {
129
216
  const nonDeletedMessages = addReplyMessages.filter(item => !item.isDeleted);
130
217
  let filtered;
@@ -141,8 +228,8 @@ const MessageList = ({
141
228
  // matching their date.toString() comparison, but redux state can
142
229
  // emit intermediate snapshots between dispatches, so the FlatList
143
230
  // briefly renders messages out-of-order until the final batch
144
- // settles. Sorting here makes each render a stable, ordered view —
145
- // even mid-stream — so the user sees them appear from oldest to
231
+ // settles. Sorting here makes each render a stable, ordered view,
232
+ // even mid-stream, so the user sees them appear from oldest to
146
233
  // newest with no swap animation.
147
234
  let sorted = filtered;
148
235
  for (let i = 1; i < filtered.length; i++) {
@@ -163,31 +250,460 @@ const MessageList = ({
163
250
  }
164
251
  return deduped;
165
252
  }, [addReplyMessages, isReply, roomJID, activeMessage]);
166
- const handleLoadMore = useCallback(async () => {
167
- if (loading || isLoadingMore || !memoizedMessages.length || isScrollBlocked) {
253
+ const memoizedMessagesRef = useRef(memoizedMessages);
254
+ memoizedMessagesRef.current = memoizedMessages;
255
+
256
+ // ---------------------------------------------------------------------
257
+ // History loading (live list)
258
+ // ---------------------------------------------------------------------
259
+
260
+ const isLoadingMoreRef = useRef(false);
261
+ const growthGuardUntilRef = useRef(0);
262
+ // The request key (oldest displayed message + cursor) last kicked off.
263
+ // Guards against a busy loop: a short conversation that never fills the
264
+ // viewport keeps asking, and a request that settles without moving anything
265
+ // would be asked again immediately, forever.
266
+ const lastRequestedKeyRef = useRef(null);
267
+ const idleLoadsRef = useRef(0);
268
+ const idleLoadsLenRef = useRef(-1);
269
+ // Request keys that already got their one delayed retry.
270
+ const retriedKeysRef = useRef(new Set());
271
+ const retryTimerRef = useRef(null);
272
+ const autoFillTimerRef = useRef(null);
273
+ const checkLoadMoreRef = useRef(() => {});
274
+ const checkWindowLoadMoreRef = useRef(() => {});
275
+
276
+ // The paging cursor moved (or regressed through another path) or the
277
+ // history completed: the repeat guard no longer describes the room, so a key
278
+ // seen before is allowed again. The idle page cap still bounds any loop.
279
+ const pagingCursor = useSelector(state => state.rooms.rooms[roomJID]?.messageStats?.firstMessageTimestamp);
280
+ const roomHistoryComplete = useSelector(state => Boolean(state.rooms.rooms[roomJID]?.historyComplete));
281
+ useEffect(() => {
282
+ lastRequestedKeyRef.current = null;
283
+ retriedKeysRef.current = new Set();
284
+ }, [pagingCursor, roomHistoryComplete]);
285
+ useEffect(() => {
286
+ // A new room's oldest message is unrelated to whatever the previous room
287
+ // last attempted: do not let a stale guard block its first load.
288
+ lastRequestedKeyRef.current = null;
289
+ retriedKeysRef.current = new Set();
290
+ idleLoadsRef.current = 0;
291
+ idleLoadsLenRef.current = -1;
292
+ if (retryTimerRef.current) {
293
+ clearTimeout(retryTimerRef.current);
294
+ retryTimerRef.current = null;
295
+ }
296
+ }, [roomJID]);
297
+ useEffect(() => () => {
298
+ if (retryTimerRef.current) {
299
+ clearTimeout(retryTimerRef.current);
300
+ }
301
+ if (autoFillTimerRef.current) {
302
+ clearTimeout(autoFillTimerRef.current);
303
+ }
304
+ }, []);
305
+ const prefetchDistance = () => Math.max(PREFETCH_MIN_DISTANCE, viewportHeightRef.current * PREFETCH_SCREENS);
306
+ // Inverted list: offset 0 is the newest end; older history is at the far end.
307
+ const distanceToOlderEdge = () => contentHeightRef.current - viewportHeightRef.current - scrollOffsetRef.current;
308
+
309
+ // After a request settled, if the guard still holds the same key nothing
310
+ // moved (oldest id and cursor identical). Allow exactly one retry per key
311
+ // after ~1 s, then stay stopped until the cursor, historyComplete or the
312
+ // room changes (those clear the retried set).
313
+ const scheduleNoProgressRetry = useCallback(key => {
314
+ if (retriedKeysRef.current.has(key)) {
315
+ return;
316
+ }
317
+ if (retryTimerRef.current) {
318
+ clearTimeout(retryTimerRef.current);
319
+ }
320
+ const forRoom = roomJID;
321
+ retryTimerRef.current = setTimeout(() => {
322
+ retryTimerRef.current = null;
323
+ if (forRoom !== roomJID || !mountedRef.current) {
324
+ return;
325
+ }
326
+ if (lastRequestedKeyRef.current !== key) {
327
+ return;
328
+ }
329
+ if (reduxStore.getState().rooms.rooms[forRoom]?.historyComplete) {
330
+ return;
331
+ }
332
+ retriedKeysRef.current.add(key);
333
+ lastRequestedKeyRef.current = null;
334
+ checkLoadMoreRef.current();
335
+ }, NO_PROGRESS_RETRY_MS);
336
+ }, [roomJID, reduxStore]);
337
+ const checkIfLoadMore = useCallback((assumeNear = false) => {
338
+ // While a jump window is on screen the live list is not what the reader
339
+ // is looking at: no background prefetch of it, the window pages instead.
340
+ if (windowActiveRef.current) {
341
+ checkWindowLoadMoreRef.current();
342
+ return;
343
+ }
344
+ if (isLoadingMoreRef.current) {
345
+ return;
346
+ }
347
+ const state = reduxStore.getState().rooms;
348
+ // A jump is already paging this room's history. A second request in
349
+ // flight at the same time makes the server's answers interleave and one
350
+ // of them time out, which the jump reads as the history being
351
+ // unreachable.
352
+ if (state.pendingJump?.roomJID === roomJID) {
353
+ return;
354
+ }
355
+ // Read the room at call time: a closure over an old render would act on
356
+ // an old cursor.
357
+ const room = state.rooms[roomJID];
358
+ if (!room || room.historyComplete) {
168
359
  return;
169
360
  }
170
- const oldestMessage = memoizedMessages[0];
171
- if (!oldestMessage || !oldestMessage.id) {
361
+ if (!assumeNear && viewportHeightRef.current > 0 && distanceToOlderEdge() >= prefetchDistance()) {
362
+ return;
363
+ }
364
+ const displayed = memoizedMessagesRef.current;
365
+ const first = displayed.find(message => !String(message.id).startsWith('delimiter-new'));
366
+ // Continue from the server's own cursor (the oldest ROW the last page
367
+ // returned), never from the oldest DISPLAYED message alone: a page made
368
+ // only of receipts or reactions displays nothing, the oldest message
369
+ // does not change, and paging would stall with plenty of history left.
370
+ const serverCursor = room.messageStats?.firstMessageTimestamp;
371
+ const oldestDisplayed = first ? Number(first.id) : NaN;
372
+ let before;
373
+ if (Number.isFinite(oldestDisplayed)) {
374
+ before = typeof serverCursor === 'number' && serverCursor < oldestDisplayed ? serverCursor : oldestDisplayed;
375
+ } else if (typeof serverCursor === 'number' && Number.isFinite(serverCursor)) {
376
+ before = serverCursor;
377
+ } else {
172
378
  return;
173
379
  }
380
+
381
+ // A repeat of the same request (same oldest message AND same cursor) is
382
+ // what the guard stops; a page that only moved the cursor is progress.
383
+ const key = `${first ? first.id : 'none'}|${before}`;
384
+ if (key === lastRequestedKeyRef.current) {
385
+ return;
386
+ }
387
+
388
+ // Hard stop for a long run of pages that add nothing to the list (an
389
+ // archive that is almost all receipts): a reader scroll re-arms it.
390
+ if (displayed.length !== idleLoadsLenRef.current) {
391
+ idleLoadsLenRef.current = displayed.length;
392
+ idleLoadsRef.current = 0;
393
+ }
394
+ if (idleLoadsRef.current >= MAX_IDLE_AUTO_PAGES) {
395
+ return;
396
+ }
397
+ idleLoadsRef.current += 1;
398
+ isLoadingMoreRef.current = true;
399
+ lastRequestedKeyRef.current = key;
174
400
  setIsLoadingMore(true);
175
- setIsScrollBlocked(true);
401
+ let request;
176
402
  try {
177
- await loadMoreMessages(oldestMessage.roomJid || roomJID, 15, Number(oldestMessage.id));
403
+ request = Promise.resolve(loadMoreMessages(roomJID, HISTORY_PAGE_SIZE, before));
178
404
  } catch (error) {
179
- console.error(error);
405
+ request = Promise.reject(error);
406
+ }
407
+ request.catch(() => undefined).then(() => {
408
+ isLoadingMoreRef.current = false;
409
+ growthGuardUntilRef.current = Date.now() + GROWTH_GUARD_MS;
410
+ if (mountedRef.current) {
411
+ setIsLoadingMore(false);
412
+ }
413
+ scheduleNoProgressRetry(key);
414
+ // Keep the buffer above the reader topped up without waiting for the
415
+ // next scroll event: a reader still inside the prefetch distance
416
+ // once this page is in gets the next page right away, so a
417
+ // normal-speed scroll never catches up with the loader. Deferred so
418
+ // the new page has been rendered and measured first.
419
+ setTimeout(() => {
420
+ if (mountedRef.current) {
421
+ checkLoadMoreRef.current();
422
+ }
423
+ }, 80);
424
+ });
425
+ }, [loadMoreMessages, roomJID, reduxStore, scheduleNoProgressRetry]);
426
+ checkLoadMoreRef.current = checkIfLoadMore;
427
+
428
+ // A list that does not fill the viewport cannot scroll, so no scroll event
429
+ // ever asks for older history (a first page made of receipts displays one
430
+ // message and the archive behind it stays unreachable). Ask without one; the
431
+ // chain in checkIfLoadMore then pages until the viewport fills. Threads load
432
+ // through their own wrapper; a jump window pages itself.
433
+ const scheduleAutoFill = useCallback(() => {
434
+ if (isReply || windowActiveRef.current || !roomJID) {
435
+ return;
436
+ }
437
+ if (autoFillTimerRef.current) {
438
+ clearTimeout(autoFillTimerRef.current);
439
+ }
440
+ autoFillTimerRef.current = setTimeout(() => {
441
+ autoFillTimerRef.current = null;
442
+ if (!mountedRef.current) {
443
+ return;
444
+ }
445
+ if (windowActiveRef.current || isLoadingMoreRef.current) {
446
+ return;
447
+ }
448
+ const state = reduxStore.getState().rooms;
449
+ if (state.pendingJump?.roomJID === roomJID) {
450
+ return;
451
+ }
452
+ if (state.rooms[roomJID]?.historyComplete) {
453
+ return;
454
+ }
455
+ // Not laid out yet: nothing to compare against.
456
+ if (viewportHeightRef.current <= 0 || contentHeightRef.current <= 0) {
457
+ return;
458
+ }
459
+ const unfilled = contentHeightRef.current <= viewportHeightRef.current || distanceToOlderEdge() < prefetchDistance();
460
+ if (!unfilled || !isUserAtBottomRef.current) {
461
+ return;
462
+ }
463
+ checkLoadMoreRef.current();
464
+ }, AUTO_FILL_DELAY_MS);
465
+ }, [isReply, roomJID, reduxStore]);
466
+ useEffect(() => {
467
+ if (isReply || windowActive || loading || historyComplete) {
468
+ return;
469
+ }
470
+ scheduleAutoFill();
471
+ }, [roomJID, isReply, windowActive, loading, historyComplete, memoizedMessages.length, pagingCursor, scheduleAutoFill]);
472
+ const handleLoadMore = useCallback(() => {
473
+ // FlatList says the reader is near the older end.
474
+ checkLoadMoreRef.current(true);
475
+ }, []);
476
+
477
+ // ---------------------------------------------------------------------
478
+ // Jump window (a far jump shows a short slice of the archive around it)
479
+ // ---------------------------------------------------------------------
480
+
481
+ const [windowLoading, setWindowLoading] = useState(null);
482
+ const windowBusyRef = useRef(null);
483
+ const windowFailedAtRef = useRef({
484
+ older: 0,
485
+ newer: 0
486
+ });
487
+ const windowOpenedAtRef = useRef(0);
488
+ const pendingExitScrollRef = useRef(false);
489
+ const prevWindowActiveRef = useRef(windowActive);
490
+ const exitWindow = useCallback(() => {
491
+ if (!windowActiveRef.current) {
492
+ return;
493
+ }
494
+ isUserAtBottomRef.current = true;
495
+ hasUserScrolledRef.current = false;
496
+ pendingExitScrollRef.current = true;
497
+ setShowNewMessageIndicator(false);
498
+ setIsUserAtBottom(true);
499
+ setUnreadWhileScrolledUp(0);
500
+ newestSeenTsRef.current = null;
501
+ onReadBoundaryChange?.(null);
502
+ dispatch(clearJumpWindow());
503
+ }, [dispatch, onReadBoundaryChange]);
504
+
505
+ // Back on the live list: land on its newest message.
506
+ useEffect(() => {
507
+ const was = prevWindowActiveRef.current;
508
+ prevWindowActiveRef.current = windowActive;
509
+ if (windowActive || !was) {
510
+ return;
511
+ }
512
+ // A new jump is about to scroll somewhere else: leave the position to it.
513
+ if (reduxStore.getState().rooms.pendingJump?.roomJID === roomJID) {
514
+ return;
515
+ }
516
+ isUserAtBottomRef.current = true;
517
+ pendingExitScrollRef.current = false;
518
+ setIsUserAtBottom(true);
519
+ const toLiveEnd = () => flatListRef.current?.scrollToOffset({
520
+ offset: 0,
521
+ animated: false
522
+ });
523
+ toLiveEnd();
524
+ // The list swaps its data a frame later on a slow device: once more.
525
+ const timer = setTimeout(toLiveEnd, 50);
526
+ return () => clearTimeout(timer);
527
+ }, [windowActive, roomJID, reduxStore]);
528
+
529
+ // A window must not outlive the list that showed it.
530
+ useEffect(() => () => {
531
+ if (windowActiveRef.current) {
532
+ dispatch(clearJumpWindow());
533
+ }
534
+ }, [dispatch]);
535
+ useEffect(() => {
536
+ if (!jumpWindow?.targetId || !windowActive) {
537
+ return;
538
+ }
539
+ windowOpenedAtRef.current = Date.now();
540
+ windowFailedAtRef.current = {
541
+ older: 0,
542
+ newer: 0
543
+ };
544
+ isUserAtBottomRef.current = false;
545
+ setIsUserAtBottom(false);
546
+ setShowNewMessageIndicator(false);
547
+ // Reading a window of old history is not reading the latest messages: from
548
+ // here on the read boundary is the newest live message the reader had
549
+ // reached, never "everything".
550
+ if (newestSeenTsRef.current === null) {
551
+ const newest = (messages ?? []).reduce((mx, m) => Math.max(mx, msgSortableMs(m)), 0);
552
+ if (newest > 0) {
553
+ newestSeenTsRef.current = newest;
554
+ onReadBoundaryChange?.(newest);
555
+ }
556
+ }
557
+ // A window that does not fill the viewport never scrolls, so nothing would
558
+ // ask for the next page: look once it has settled.
559
+ const timer = setTimeout(() => checkWindowLoadMoreRef.current(), WINDOW_SETTLE_MS + 100);
560
+ return () => clearTimeout(timer);
561
+ // eslint-disable-next-line react-hooks/exhaustive-deps
562
+ }, [jumpWindow?.targetId, windowActive]);
563
+ const loadWindowPage = useCallback(async direction => {
564
+ const win = reduxStore.getState().rooms.jumpWindow;
565
+ if (!win || win.roomJID !== roomJID || !client) {
566
+ return;
567
+ }
568
+ if (windowBusyRef.current) {
569
+ return;
570
+ }
571
+ windowBusyRef.current = direction;
572
+ setWindowLoading(direction);
573
+ try {
574
+ const page = direction === 'older' ? await loadOlderWindowPage(client, win) : await loadNewerWindowPage(client, win);
575
+ const current = reduxStore.getState().rooms.jumpWindow;
576
+ // The reader left, or opened another window, while this was in flight.
577
+ if (!current || current.targetId !== win.targetId) {
578
+ return;
579
+ }
580
+ if (!page) {
581
+ windowFailedAtRef.current[direction] = Date.now();
582
+ return;
583
+ }
584
+ requestSendersOf(page.messages);
585
+ if ('olderCursor' in page) {
586
+ dispatch(prependJumpWindowMessages({
587
+ roomJID,
588
+ ...page
589
+ }));
590
+ } else {
591
+ dispatch(appendJumpWindowMessages({
592
+ roomJID,
593
+ ...page
594
+ }));
595
+ }
596
+ } catch {
597
+ windowFailedAtRef.current[direction] = Date.now();
180
598
  } finally {
599
+ windowBusyRef.current = null;
600
+ if (mountedRef.current) {
601
+ setWindowLoading(null);
602
+ }
181
603
  setTimeout(() => {
182
- setIsLoadingMore(false);
183
- setIsScrollBlocked(false);
184
- }, 500);
604
+ if (mountedRef.current) {
605
+ checkWindowLoadMoreRef.current();
606
+ }
607
+ }, 80);
608
+ }
609
+ }, [client, roomJID, dispatch, reduxStore]);
610
+ checkWindowLoadMoreRef.current = (fromUserScroll = false) => {
611
+ const win = reduxStore.getState().rooms.jumpWindow;
612
+ if (!win || win.roomJID !== roomJID) {
613
+ return;
614
+ }
615
+ const now = Date.now();
616
+ if (now - windowOpenedAtRef.current < WINDOW_SETTLE_MS) {
617
+ return;
618
+ }
619
+ // Not laid out yet: there is nothing to measure a distance against.
620
+ if (viewportHeightRef.current <= 0 || contentHeightRef.current <= 0) {
621
+ return;
622
+ }
623
+ const prefetch = prefetchDistance();
624
+ const olderDistance = distanceToOlderEdge();
625
+ const newerDistance = scrollOffsetRef.current;
626
+ if (win.hasOlder && olderDistance < prefetch && now - windowFailedAtRef.current.older > WINDOW_RETRY_AFTER_FAILURE_MS) {
627
+ loadWindowPage('older');
628
+ } else if (win.hasNewer && newerDistance < prefetch && now - windowFailedAtRef.current.newer > WINDOW_RETRY_AFTER_FAILURE_MS) {
629
+ loadWindowPage('newer');
630
+ } else if (!win.hasNewer && fromUserScroll && newerDistance <= 5 && now - windowOpenedAtRef.current > WINDOW_EXIT_GRACE_MS) {
631
+ // Nothing newer than this is left to load: that is the live tail.
632
+ exitWindow();
633
+ }
634
+ };
635
+ const handleStartReached = useCallback(() => {
636
+ checkWindowLoadMoreRef.current();
637
+ }, []);
638
+
639
+ // A jump whose target is a thread reply opens the parent's thread (the main
640
+ // list never shows replies); the thread list then highlights the reply.
641
+ const resolveReply = useCallback((jump, reply, nearby) => openThreadForJump({
642
+ client,
643
+ dispatch: dispatch,
644
+ roomJID,
645
+ at: jump.at,
646
+ reply,
647
+ live: messages ?? [],
648
+ nearby
649
+ }), [client, dispatch, roomJID, messages]);
650
+ const fetchWindow = useCallback(async jump => {
651
+ if (isReply || !client?.getHistoryWindow) {
652
+ return 'unavailable';
653
+ }
654
+ const result = await loadJumpWindow(client, roomJID, jump.ids, {
655
+ createdAt: jump.createdAt,
656
+ body: jump.body
657
+ });
658
+ if (result.status !== 'found') {
659
+ return result.status;
185
660
  }
186
- }, [memoizedMessages, loadMoreMessages, loading, isLoadingMore, isScrollBlocked, roomJID]);
661
+ const target = result.window.messages.find(message => String(message.id) === result.window.targetId);
662
+ if (target && replyParentId(target)) {
663
+ // Not a main-list message: the window is only used to learn what the
664
+ // reply belongs to; it is not put on screen.
665
+ const opened = await resolveReply(jump, target, result.window.messages).catch(() => false);
666
+ return opened ? 'found' : 'missing';
667
+ }
668
+ isUserAtBottomRef.current = false;
669
+ requestSendersOf(result.window.messages);
670
+ dispatch(setJumpWindow(result.window));
671
+ return 'found';
672
+ }, [client, roomJID, isReply, dispatch, resolveReply]);
673
+ const fetchOlderPage = useCallback(async (jid, before, max) => {
674
+ if (!client) {
675
+ return {
676
+ ok: false
677
+ };
678
+ }
679
+ const page = await client.getHistoryStanza(jid, max, before, undefined, {
680
+ source: 'active'
681
+ });
682
+ // The page is in the store by now, and so is the server's cursor for it
683
+ // (the <fin> handler writes it to messageStats).
684
+ const room = reduxStore.getState().rooms.rooms[jid];
685
+ return {
686
+ ok: page !== undefined,
687
+ cursor: room?.messageStats?.firstMessageTimestamp,
688
+ complete: Boolean(room?.historyComplete)
689
+ };
690
+ }, [client, reduxStore]);
691
+
692
+ // The own message that was just sent is on the live list: leave the window
693
+ // for it (the composer already clears it; this covers other send paths).
694
+ const lastNewestIdRef = useRef(messages[messages.length - 1]?.id);
695
+ useEffect(() => {
696
+ const newest = messages[messages.length - 1];
697
+ const changed = newest && String(newest.id) !== lastNewestIdRef.current;
698
+ lastNewestIdRef.current = newest ? String(newest.id) : undefined;
699
+ if (changed && windowActiveRef.current && isOwnMessage(newest, user.xmppUsername || user.walletAddress)) {
700
+ exitWindow();
701
+ }
702
+ }, [messages, user.xmppUsername, user.walletAddress, exitWindow]);
187
703
  const dataMessages = useMemo(() => {
188
704
  const base = memoizedMessages.slice().reverse();
189
705
  const boundaryTs = newestSeenTsRef.current;
190
- if (isUserAtBottomRef.current || boundaryTs === null || unreadWhileScrolledUp === 0) {
706
+ if (windowActive || isUserAtBottomRef.current || boundaryTs === null || unreadWhileScrolledUp === 0) {
191
707
  return base;
192
708
  }
193
709
  let lastNewIndex = -1;
@@ -215,7 +731,58 @@ const MessageList = ({
215
731
  roomJid: roomJID
216
732
  });
217
733
  return withDivider;
218
- }, [memoizedMessages, isUserAtBottom, roomJID, unreadWhileScrolledUp]);
734
+ // eslint-disable-next-line react-hooks/exhaustive-deps
735
+ }, [memoizedMessages, isUserAtBottom, roomJID, unreadWhileScrolledUp, windowActive]);
736
+
737
+ // Jump-to-message (search hit, notification): scroll to the row, then pulse
738
+ // the bubble. The retry budget is for scrolling to a row the virtualised
739
+ // list has not measured yet.
740
+ const scrollRetryRef = useRef(0);
741
+ const scrollToRow = useCallback(index => {
742
+ scrollRetryRef.current = 0;
743
+ flatListRef.current?.scrollToIndex({
744
+ index,
745
+ viewPosition: 0.5,
746
+ animated: false
747
+ });
748
+ }, []);
749
+ const handleScrollToIndexFailed = useCallback(info => {
750
+ const offset = scrollRetryOffset(info, scrollRetryRef.current);
751
+ if (offset === null) {
752
+ return;
753
+ }
754
+ scrollRetryRef.current += 1;
755
+ flatListRef.current?.scrollToOffset({
756
+ offset,
757
+ animated: false
758
+ });
759
+ setTimeout(() => {
760
+ flatListRef.current?.scrollToIndex({
761
+ index: info.index,
762
+ viewPosition: 0.5,
763
+ animated: false
764
+ });
765
+ }, 120);
766
+ }, []);
767
+ useJumpToMessage({
768
+ roomJID,
769
+ messages: memoizedMessages,
770
+ listData: dataMessages,
771
+ scrollToIndex: scrollToRow,
772
+ onHighlight: setBubbleHighlight,
773
+ loadMoreMessages,
774
+ fetchOlderPage: client ? fetchOlderPage : undefined,
775
+ historyComplete,
776
+ isUserAtBottomRef,
777
+ jumpWindowActive: windowActive,
778
+ fetchWindow: isReply || !client ? undefined : fetchWindow,
779
+ scope: isReply && activeMessage ? {
780
+ threadId: String(activeMessage.id)
781
+ } : 'main',
782
+ allMessages: sourceMessages,
783
+ resolveReply: isReply ? undefined : (jump, reply) => resolveReply(jump, reply),
784
+ roomOpening: !isReply && (Boolean(loading) || joiningRoomJID === roomJID || sourceMessages.length === 0 && !historyComplete)
785
+ });
219
786
  const dateLabelIds = useMemo(() => {
220
787
  const ids = new Set();
221
788
  let nextDay = null;
@@ -249,6 +816,9 @@ const MessageList = ({
249
816
  });
250
817
  }
251
818
  const showDateLabel = dateLabelIds.has(String(item.id));
819
+
820
+ // The jump highlight is drawn by the message bubble itself (see
821
+ // BubbleHighlight), so the row, its date label and its avatar stay put.
252
822
  return /*#__PURE__*/_jsx(MessageContainer, {
253
823
  CustomMessage: CustomMessage,
254
824
  CustomDaySeparator: CustomDaySeparator,
@@ -269,29 +839,58 @@ const MessageList = ({
269
839
  });
270
840
  }
271
841
  }, [flatListRef]);
272
- const handleContentSizeChange = useCallback(() => {
273
- if (!isLoadingMore && flatListRef.current && isUserAtBottomRef.current) {
842
+ const handleContentSizeChange = useCallback((_width, height) => {
843
+ if (typeof height === 'number' && height > 0) {
844
+ contentHeightRef.current = height;
845
+ }
846
+ if (windowActiveRef.current) {
847
+ // Reading a window of old history: never stick to the bottom.
848
+ checkWindowLoadMoreRef.current();
849
+ return;
850
+ }
851
+ const growing = isLoadingMoreRef.current || Date.now() < growthGuardUntilRef.current;
852
+ if (!growing && flatListRef.current && isUserAtBottomRef.current) {
274
853
  scrollToBottom();
275
- } else if (!isLoadingMore && hasUserScrolledRef.current) {
854
+ } else if (!growing && hasUserScrolledRef.current) {
276
855
  setShowNewMessageIndicator(true);
277
856
  }
278
- }, [scrollToBottom, isLoadingMore]);
857
+ scheduleAutoFill();
858
+ }, [scrollToBottom, scheduleAutoFill]);
279
859
  const handleScroll = useCallback(event => {
280
- const contentOffset = event.nativeEvent.contentOffset.y;
860
+ const {
861
+ contentOffset,
862
+ contentSize,
863
+ layoutMeasurement
864
+ } = event.nativeEvent;
865
+ const offsetY = contentOffset.y;
866
+ scrollOffsetRef.current = offsetY;
867
+ if (contentSize?.height > 0) {
868
+ contentHeightRef.current = contentSize.height;
869
+ }
870
+ if (layoutMeasurement?.height > 0) {
871
+ viewportHeightRef.current = layoutMeasurement.height;
872
+ }
873
+ if (windowActiveRef.current) {
874
+ // Reading a window of old history: never "at the bottom", always the
875
+ // way back to the latest messages, and page both ways.
876
+ isUserAtBottomRef.current = false;
877
+ checkWindowLoadMoreRef.current(true);
878
+ return;
879
+ }
281
880
 
282
- // Don't force-dismiss the keyboard on every scroll event — any
881
+ // Don't force-dismiss the keyboard on every scroll event, any
283
882
  // touch on the message list used to close it, which made replying
284
883
  // to a long thread feel broken. Drag-to-dismiss still works via
285
884
  // FlatList's keyboardDismissMode="interactive" below.
286
885
 
287
- if (contentOffset > 150 && !hasUserScrolledRef.current) {
886
+ if (offsetY > 150 && !hasUserScrolledRef.current) {
288
887
  hasUserScrolledRef.current = true;
289
888
  }
290
- if (contentOffset < 150) {
889
+ if (offsetY < 150) {
291
890
  isUserAtBottomRef.current = true;
292
891
  setShowNewMessageIndicator(false);
293
892
  setIsUserAtBottom(true);
294
- // Back at the bottom — clear the unread-while-scrolled-up
893
+ // Back at the bottom, clear the unread-while-scrolled-up
295
894
  // counter so the badge disappears with the arrow.
296
895
  setUnreadWhileScrolledUp(0);
297
896
  newestSeenTsRef.current = null;
@@ -307,8 +906,8 @@ const MessageList = ({
307
906
  // arrives later is genuinely "new"; older history loaded by
308
907
  // back-pagination is < this and never counts.
309
908
  newestSeenTsRef.current = memoizedMessages.reduce((mx, m) => {
310
- const t = msgSortableMs(m);
311
- return t > mx ? t : mx;
909
+ const ts = msgSortableMs(m);
910
+ return ts > mx ? ts : mx;
312
911
  }, 0);
313
912
  onReadBoundaryChange?.(newestSeenTsRef.current);
314
913
  }
@@ -318,8 +917,11 @@ const MessageList = ({
318
917
 
319
918
  // Keep the badge in sync with messages that arrive while the user is
320
919
  // scrolled up. Count ONLY messages newer than the newest-seen snapshot
321
- // — so loading older history (back-pagination) doesn't inflate it.
920
+ //, so loading older history (back-pagination) doesn't inflate it.
322
921
  useEffect(() => {
922
+ if (windowActiveRef.current) {
923
+ return;
924
+ }
323
925
  if (isUserAtBottomRef.current) {
324
926
  return;
325
927
  }
@@ -330,10 +932,17 @@ const MessageList = ({
330
932
  const count = memoizedMessages.reduce((n, m) => msgSortableMs(m) > since ? n + 1 : n, 0);
331
933
  setUnreadWhileScrolledUp(count);
332
934
  }, [memoizedMessages, isUserAtBottom]);
333
- const handleLayout = () => {
334
- isUserAtBottomRef.current = true;
335
- hasUserScrolledRef.current = false;
336
- setIsUserAtBottom(true);
935
+ const handleLayout = event => {
936
+ const height = event?.nativeEvent?.layout?.height;
937
+ if (typeof height === 'number' && height > 0) {
938
+ viewportHeightRef.current = height;
939
+ }
940
+ if (!windowActiveRef.current) {
941
+ isUserAtBottomRef.current = true;
942
+ hasUserScrolledRef.current = false;
943
+ setIsUserAtBottom(true);
944
+ scheduleAutoFill();
945
+ }
337
946
  };
338
947
  const handleNewMessageIndicatorPress = () => {
339
948
  isUserAtBottomRef.current = true;
@@ -394,20 +1003,69 @@ const MessageList = ({
394
1003
  initialNumToRender: 15,
395
1004
  maxToRenderPerBatch: 10,
396
1005
  windowSize: 7,
397
- onEndReached: handleLoadMore,
1006
+ onEndReached: windowActive ? handleStartReached : handleLoadMore,
1007
+ onStartReached: windowActive ? handleStartReached : undefined,
398
1008
  onScroll: handleScroll,
399
1009
  onContentSizeChange: handleContentSizeChange,
400
- onEndReachedThreshold: 0.1,
1010
+ onScrollToIndexFailed: handleScrollToIndexFailed
1011
+ // Prefetch well ahead of the viewport (screens, not pixels) so a
1012
+ // normal scroll finds older history already there.
1013
+ ,
1014
+ onEndReachedThreshold: END_REACHED_THRESHOLD,
1015
+ onStartReachedThreshold: END_REACHED_THRESHOLD
1016
+ // In a jump window newer messages arrive at the near (offset 0) end:
1017
+ // keep what the reader is looking at where it is instead of letting
1018
+ // the content slide. The live list stays unanchored on purpose, it
1019
+ // follows its own newest end (handleContentSizeChange).
1020
+ ,
1021
+ maintainVisibleContentPosition: windowActive ? {
1022
+ minIndexForVisible: 0
1023
+ } : undefined,
401
1024
  scrollEventThrottle: 16,
402
1025
  onLayout: handleLayout,
403
1026
  inverted: true,
404
1027
  keyboardShouldPersistTaps: "handled",
405
1028
  keyboardDismissMode: config?.keepKeyboardOpenOnScroll ? 'none' : 'interactive',
406
1029
  contentContainerStyle: styles.flatListContent,
407
- ListFooterComponent: loading && memoizedMessages.length > 15 ? /*#__PURE__*/_jsx(Loader, {
1030
+ ListFooterComponent: (loading || isLoadingMore) && !windowActive && memoizedMessages.length > 15 ? /*#__PURE__*/_jsx(Loader, {
408
1031
  color: config?.colors?.primary
409
1032
  }) : null
410
- }), showNewMessageIndicator && /*#__PURE__*/_jsxs(TouchableOpacity, {
1033
+ }), windowActive && windowLoading === 'older' && /*#__PURE__*/_jsx(View, {
1034
+ testID: "history-loader-older",
1035
+ pointerEvents: "none",
1036
+ style: styles.loaderTop,
1037
+ children: /*#__PURE__*/_jsx(Loader, {
1038
+ size: 24,
1039
+ color: config?.colors?.primary
1040
+ })
1041
+ }), windowActive && windowLoading === 'newer' && /*#__PURE__*/_jsx(View, {
1042
+ testID: "history-loader-newer",
1043
+ pointerEvents: "none",
1044
+ style: styles.loaderBottom,
1045
+ children: /*#__PURE__*/_jsx(Loader, {
1046
+ size: 24,
1047
+ color: config?.colors?.primary
1048
+ })
1049
+ }), windowActive ? /*#__PURE__*/_jsxs(TouchableOpacity, {
1050
+ testID: "jump-to-latest",
1051
+ accessibilityRole: "button",
1052
+ accessibilityLabel: t('action.jumpToLatest'),
1053
+ style: [styles.jumpToLatest, {
1054
+ backgroundColor: getIconColor(config),
1055
+ shadowColor: theme.shadow
1056
+ }],
1057
+ onPress: exitWindow,
1058
+ children: [/*#__PURE__*/_jsx(ArowDownIcon, {
1059
+ color: theme.textOnPrimary,
1060
+ width: 18,
1061
+ height: 18
1062
+ }), /*#__PURE__*/_jsx(Text, {
1063
+ style: [styles.jumpToLatestText, {
1064
+ color: theme.textOnPrimary
1065
+ }],
1066
+ children: t('action.jumpToLatest')
1067
+ })]
1068
+ }) : showNewMessageIndicator && /*#__PURE__*/_jsxs(TouchableOpacity, {
411
1069
  style: [styles.newMessageIndicator, {
412
1070
  backgroundColor: getIconColor(config),
413
1071
  shadowColor: theme.shadow
@@ -470,6 +1128,46 @@ const styles = StyleSheet.create({
470
1128
  width: '100%',
471
1129
  height: '100%'
472
1130
  },
1131
+ // Float over the list so showing or hiding them never moves the messages.
1132
+ loaderTop: {
1133
+ position: 'absolute',
1134
+ top: 8,
1135
+ left: 0,
1136
+ right: 0,
1137
+ alignItems: 'center',
1138
+ zIndex: 2
1139
+ },
1140
+ loaderBottom: {
1141
+ position: 'absolute',
1142
+ bottom: 8,
1143
+ left: 0,
1144
+ right: 0,
1145
+ alignItems: 'center',
1146
+ zIndex: 2
1147
+ },
1148
+ jumpToLatest: {
1149
+ position: 'absolute',
1150
+ bottom: 20,
1151
+ right: 20,
1152
+ height: 40,
1153
+ paddingHorizontal: 14,
1154
+ borderRadius: 20,
1155
+ flexDirection: 'row',
1156
+ alignItems: 'center',
1157
+ justifyContent: 'center',
1158
+ gap: 6,
1159
+ shadowOffset: {
1160
+ width: 0,
1161
+ height: 2
1162
+ },
1163
+ shadowOpacity: 0.2,
1164
+ shadowRadius: 3,
1165
+ elevation: 4
1166
+ },
1167
+ jumpToLatestText: {
1168
+ fontSize: 13,
1169
+ fontWeight: '600'
1170
+ },
473
1171
  newMessageIndicator: {
474
1172
  position: 'absolute',
475
1173
  width: 40,