@ethora/chat-component-rn 26.5.5 → 26.5.10

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 (420) hide show
  1. package/CHANGELOG.md +177 -0
  2. package/README.md +67 -10
  3. package/index.js +24 -0
  4. package/lib/commonjs/assets/icons.js +5 -1
  5. package/lib/commonjs/assets/icons.js.map +1 -1
  6. package/lib/commonjs/components/ContextMenu/ContextMenuComponents.js +11 -5
  7. package/lib/commonjs/components/ContextMenu/ContextMenuComponents.js.map +1 -1
  8. package/lib/commonjs/components/MainComponents/ChatHeader.js +7 -1
  9. package/lib/commonjs/components/MainComponents/ChatHeader.js.map +1 -1
  10. package/lib/commonjs/components/MainComponents/ChatRoom.js +66 -34
  11. package/lib/commonjs/components/MainComponents/ChatRoom.js.map +1 -1
  12. package/lib/commonjs/components/MainComponents/ChatWrapper.js +59 -14
  13. package/lib/commonjs/components/MainComponents/ChatWrapper.js.map +1 -1
  14. package/lib/commonjs/components/MainComponents/MediaMessage.js +15 -7
  15. package/lib/commonjs/components/MainComponents/MediaMessage.js.map +1 -1
  16. package/lib/commonjs/components/MainComponents/MessageContainer.js +2 -2
  17. package/lib/commonjs/components/MainComponents/MessageContainer.js.map +1 -1
  18. package/lib/commonjs/components/MainComponents/MessageList.js +175 -30
  19. package/lib/commonjs/components/MainComponents/MessageList.js.map +1 -1
  20. package/lib/commonjs/components/MainComponents/ReduxWrapper.js +17 -8
  21. package/lib/commonjs/components/MainComponents/ReduxWrapper.js.map +1 -1
  22. package/lib/commonjs/components/MainComponents/SystemMessage.js +34 -22
  23. package/lib/commonjs/components/MainComponents/SystemMessage.js.map +1 -1
  24. package/lib/commonjs/components/MessageBubble/Message.js +117 -19
  25. package/lib/commonjs/components/MessageBubble/Message.js.map +1 -1
  26. package/lib/commonjs/components/MessageBubble/MessageInteractions.js +82 -22
  27. package/lib/commonjs/components/MessageBubble/MessageInteractions.js.map +1 -1
  28. package/lib/commonjs/components/Modals/AttachSheet/AttachSheet.js +16 -4
  29. package/lib/commonjs/components/Modals/AttachSheet/AttachSheet.js.map +1 -1
  30. package/lib/commonjs/components/Modals/FilePreviewModal/DocumentViewer.js +104 -0
  31. package/lib/commonjs/components/Modals/FilePreviewModal/DocumentViewer.js.map +1 -0
  32. package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js +175 -40
  33. package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
  34. package/lib/commonjs/components/Modals/FilePreviewModal/PdfView.js +29 -1
  35. package/lib/commonjs/components/Modals/FilePreviewModal/PdfView.js.map +1 -1
  36. package/lib/commonjs/components/Modals/Modal/Modal.js +5 -1
  37. package/lib/commonjs/components/Modals/Modal/Modal.js.map +1 -1
  38. package/lib/commonjs/components/Modals/ModalWrapper/ModalWrapper.js +78 -39
  39. package/lib/commonjs/components/Modals/ModalWrapper/ModalWrapper.js.map +1 -1
  40. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js +3 -3
  41. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  42. package/lib/commonjs/components/Modals/styledModalComponents.js +4 -9
  43. package/lib/commonjs/components/Modals/styledModalComponents.js.map +1 -1
  44. package/lib/commonjs/components/styled/MediaFilePreview.js +115 -5
  45. package/lib/commonjs/components/styled/MediaFilePreview.js.map +1 -1
  46. package/lib/commonjs/components/styled/MessageImage.js +49 -10
  47. package/lib/commonjs/components/styled/MessageImage.js.map +1 -1
  48. package/lib/commonjs/components/styled/NewMessageLabel.js +8 -6
  49. package/lib/commonjs/components/styled/NewMessageLabel.js.map +1 -1
  50. package/lib/commonjs/components/styled/SendInput.js +53 -29
  51. package/lib/commonjs/components/styled/SendInput.js.map +1 -1
  52. package/lib/commonjs/components/styled/StyledInputComponents/MediaComponents.js +14 -4
  53. package/lib/commonjs/components/styled/StyledInputComponents/MediaComponents.js.map +1 -1
  54. package/lib/commonjs/components/styled/StyledInputComponents/StyledInputComponents.js +7 -2
  55. package/lib/commonjs/components/styled/StyledInputComponents/StyledInputComponents.js.map +1 -1
  56. package/lib/commonjs/components/styled/UnsupportedType.js +5 -1
  57. package/lib/commonjs/components/styled/UnsupportedType.js.map +1 -1
  58. package/lib/commonjs/components/styled/VideoMessage.js +60 -22
  59. package/lib/commonjs/components/styled/VideoMessage.js.map +1 -1
  60. package/lib/commonjs/context/MessageNotificationContext.js +7 -7
  61. package/lib/commonjs/context/MessageNotificationContext.js.map +1 -1
  62. package/lib/commonjs/context/xmppProvider.js +315 -34
  63. package/lib/commonjs/context/xmppProvider.js.map +1 -1
  64. package/lib/commonjs/helpers/chatAutoEnterer.js +2 -7
  65. package/lib/commonjs/helpers/chatAutoEnterer.js.map +1 -1
  66. package/lib/commonjs/helpers/historyPreloadScheduler.js +4 -1
  67. package/lib/commonjs/helpers/historyPreloadScheduler.js.map +1 -1
  68. package/lib/commonjs/helpers/insertMessageWithDelimiter.js +15 -5
  69. package/lib/commonjs/helpers/insertMessageWithDelimiter.js.map +1 -1
  70. package/lib/commonjs/helpers/keyboardLayout.js +18 -0
  71. package/lib/commonjs/helpers/keyboardLayout.js.map +1 -0
  72. package/lib/commonjs/helpers/mediaDimensions.js +51 -0
  73. package/lib/commonjs/helpers/mediaDimensions.js.map +1 -0
  74. package/lib/commonjs/helpers/mimeToExtension.js +131 -0
  75. package/lib/commonjs/helpers/mimeToExtension.js.map +1 -0
  76. package/lib/commonjs/helpers/normalizeRoomJid.js +20 -0
  77. package/lib/commonjs/helpers/normalizeRoomJid.js.map +1 -0
  78. package/lib/commonjs/helpers/resolveInitBeforeLoadUser.js +73 -1
  79. package/lib/commonjs/helpers/resolveInitBeforeLoadUser.js.map +1 -1
  80. package/lib/commonjs/hooks/useChatRoomFocus.js +87 -0
  81. package/lib/commonjs/hooks/useChatRoomFocus.js.map +1 -0
  82. package/lib/commonjs/hooks/useChatWrapperInit.js +22 -10
  83. package/lib/commonjs/hooks/useChatWrapperInit.js.map +1 -1
  84. package/lib/commonjs/hooks/useEventHandlers.js +9 -1
  85. package/lib/commonjs/hooks/useEventHandlers.js.map +1 -1
  86. package/lib/commonjs/hooks/useHeapSender.js +1 -1
  87. package/lib/commonjs/hooks/useHeapSender.js.map +1 -1
  88. package/lib/commonjs/hooks/useLogout.js +3 -3
  89. package/lib/commonjs/hooks/useLogout.js.map +1 -1
  90. package/lib/commonjs/hooks/useMessageHeapState.js +6 -2
  91. package/lib/commonjs/hooks/useMessageHeapState.js.map +1 -1
  92. package/lib/commonjs/hooks/usePushNotifications.js +6 -2
  93. package/lib/commonjs/hooks/usePushNotifications.js.map +1 -1
  94. package/lib/commonjs/hooks/useRoomState.js +2 -0
  95. package/lib/commonjs/hooks/useRoomState.js.map +1 -1
  96. package/lib/commonjs/hooks/useSendMessage.js +249 -14
  97. package/lib/commonjs/hooks/useSendMessage.js.map +1 -1
  98. package/lib/commonjs/hooks/useUnreadMessagesCounter.js +12 -14
  99. package/lib/commonjs/hooks/useUnreadMessagesCounter.js.map +1 -1
  100. package/lib/commonjs/main.js +7 -0
  101. package/lib/commonjs/main.js.map +1 -1
  102. package/lib/commonjs/networking/api-requests/auth.api.js +74 -1
  103. package/lib/commonjs/networking/api-requests/auth.api.js.map +1 -1
  104. package/lib/commonjs/networking/apiClient.js +2 -1
  105. package/lib/commonjs/networking/apiClient.js.map +1 -1
  106. package/lib/commonjs/networking/outboundQueue.js +92 -0
  107. package/lib/commonjs/networking/outboundQueue.js.map +1 -0
  108. package/lib/commonjs/networking/xmpp/createTimeoutPromise.xmpp.js +4 -1
  109. package/lib/commonjs/networking/xmpp/createTimeoutPromise.xmpp.js.map +1 -1
  110. package/lib/commonjs/networking/xmpp/flushLastViewedToPrivateStore.js +7 -8
  111. package/lib/commonjs/networking/xmpp/flushLastViewedToPrivateStore.js.map +1 -1
  112. package/lib/commonjs/networking/xmpp/getRoomsPaged.xmpp.js +26 -3
  113. package/lib/commonjs/networking/xmpp/getRoomsPaged.xmpp.js.map +1 -1
  114. package/lib/commonjs/networking/xmpp/presenceInRoom.xmpp.js +26 -8
  115. package/lib/commonjs/networking/xmpp/presenceInRoom.xmpp.js.map +1 -1
  116. package/lib/commonjs/networking/xmpp/sendTextMessage.xmpp.js +2 -1
  117. package/lib/commonjs/networking/xmpp/sendTextMessage.xmpp.js.map +1 -1
  118. package/lib/commonjs/networking/xmppClient.js +371 -26
  119. package/lib/commonjs/networking/xmppClient.js.map +1 -1
  120. package/lib/commonjs/roomStore/Middleware/unreadMidlleware.js +22 -6
  121. package/lib/commonjs/roomStore/Middleware/unreadMidlleware.js.map +1 -1
  122. package/lib/commonjs/roomStore/roomHeapSlice.js +26 -8
  123. package/lib/commonjs/roomStore/roomHeapSlice.js.map +1 -1
  124. package/lib/commonjs/roomStore/roomsSlice.js +157 -23
  125. package/lib/commonjs/roomStore/roomsSlice.js.map +1 -1
  126. package/lib/commonjs/utils/installPromiseRejectionTracker.js +83 -0
  127. package/lib/commonjs/utils/installPromiseRejectionTracker.js.map +1 -0
  128. package/lib/commonjs/utils/resendMessage.js +1 -1
  129. package/lib/commonjs/utils/resendMessage.js.map +1 -1
  130. package/lib/module/assets/icons.js +5 -1
  131. package/lib/module/assets/icons.js.map +1 -1
  132. package/lib/module/components/ContextMenu/ContextMenuComponents.js +11 -5
  133. package/lib/module/components/ContextMenu/ContextMenuComponents.js.map +1 -1
  134. package/lib/module/components/MainComponents/ChatHeader.js +7 -1
  135. package/lib/module/components/MainComponents/ChatHeader.js.map +1 -1
  136. package/lib/module/components/MainComponents/ChatRoom.js +67 -35
  137. package/lib/module/components/MainComponents/ChatRoom.js.map +1 -1
  138. package/lib/module/components/MainComponents/ChatWrapper.js +59 -14
  139. package/lib/module/components/MainComponents/ChatWrapper.js.map +1 -1
  140. package/lib/module/components/MainComponents/MediaMessage.js +15 -7
  141. package/lib/module/components/MainComponents/MediaMessage.js.map +1 -1
  142. package/lib/module/components/MainComponents/MessageContainer.js +2 -2
  143. package/lib/module/components/MainComponents/MessageContainer.js.map +1 -1
  144. package/lib/module/components/MainComponents/MessageList.js +176 -31
  145. package/lib/module/components/MainComponents/MessageList.js.map +1 -1
  146. package/lib/module/components/MainComponents/ReduxWrapper.js +18 -8
  147. package/lib/module/components/MainComponents/ReduxWrapper.js.map +1 -1
  148. package/lib/module/components/MainComponents/SystemMessage.js +33 -21
  149. package/lib/module/components/MainComponents/SystemMessage.js.map +1 -1
  150. package/lib/module/components/MessageBubble/Message.js +118 -20
  151. package/lib/module/components/MessageBubble/Message.js.map +1 -1
  152. package/lib/module/components/MessageBubble/MessageInteractions.js +84 -24
  153. package/lib/module/components/MessageBubble/MessageInteractions.js.map +1 -1
  154. package/lib/module/components/Modals/AttachSheet/AttachSheet.js +15 -3
  155. package/lib/module/components/Modals/AttachSheet/AttachSheet.js.map +1 -1
  156. package/lib/module/components/Modals/FilePreviewModal/DocumentViewer.js +99 -0
  157. package/lib/module/components/Modals/FilePreviewModal/DocumentViewer.js.map +1 -0
  158. package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js +179 -42
  159. package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
  160. package/lib/module/components/Modals/FilePreviewModal/PdfView.js +31 -3
  161. package/lib/module/components/Modals/FilePreviewModal/PdfView.js.map +1 -1
  162. package/lib/module/components/Modals/Modal/Modal.js +5 -1
  163. package/lib/module/components/Modals/Modal/Modal.js.map +1 -1
  164. package/lib/module/components/Modals/ModalWrapper/ModalWrapper.js +80 -41
  165. package/lib/module/components/Modals/ModalWrapper/ModalWrapper.js.map +1 -1
  166. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js +3 -3
  167. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  168. package/lib/module/components/Modals/styledModalComponents.js +4 -9
  169. package/lib/module/components/Modals/styledModalComponents.js.map +1 -1
  170. package/lib/module/components/styled/MediaFilePreview.js +116 -6
  171. package/lib/module/components/styled/MediaFilePreview.js.map +1 -1
  172. package/lib/module/components/styled/MessageImage.js +51 -12
  173. package/lib/module/components/styled/MessageImage.js.map +1 -1
  174. package/lib/module/components/styled/NewMessageLabel.js +8 -6
  175. package/lib/module/components/styled/NewMessageLabel.js.map +1 -1
  176. package/lib/module/components/styled/SendInput.js +54 -30
  177. package/lib/module/components/styled/SendInput.js.map +1 -1
  178. package/lib/module/components/styled/StyledInputComponents/MediaComponents.js +14 -4
  179. package/lib/module/components/styled/StyledInputComponents/MediaComponents.js.map +1 -1
  180. package/lib/module/components/styled/StyledInputComponents/StyledInputComponents.js +7 -2
  181. package/lib/module/components/styled/StyledInputComponents/StyledInputComponents.js.map +1 -1
  182. package/lib/module/components/styled/UnsupportedType.js +5 -1
  183. package/lib/module/components/styled/UnsupportedType.js.map +1 -1
  184. package/lib/module/components/styled/VideoMessage.js +61 -23
  185. package/lib/module/components/styled/VideoMessage.js.map +1 -1
  186. package/lib/module/context/MessageNotificationContext.js +7 -7
  187. package/lib/module/context/MessageNotificationContext.js.map +1 -1
  188. package/lib/module/context/xmppProvider.js +319 -37
  189. package/lib/module/context/xmppProvider.js.map +1 -1
  190. package/lib/module/helpers/chatAutoEnterer.js +2 -7
  191. package/lib/module/helpers/chatAutoEnterer.js.map +1 -1
  192. package/lib/module/helpers/historyPreloadScheduler.js +4 -1
  193. package/lib/module/helpers/historyPreloadScheduler.js.map +1 -1
  194. package/lib/module/helpers/insertMessageWithDelimiter.js +15 -5
  195. package/lib/module/helpers/insertMessageWithDelimiter.js.map +1 -1
  196. package/lib/module/helpers/keyboardLayout.js +12 -0
  197. package/lib/module/helpers/keyboardLayout.js.map +1 -0
  198. package/lib/module/helpers/mediaDimensions.js +46 -0
  199. package/lib/module/helpers/mediaDimensions.js.map +1 -0
  200. package/lib/module/helpers/mimeToExtension.js +124 -0
  201. package/lib/module/helpers/mimeToExtension.js.map +1 -0
  202. package/lib/module/helpers/normalizeRoomJid.js +15 -0
  203. package/lib/module/helpers/normalizeRoomJid.js.map +1 -0
  204. package/lib/module/helpers/resolveInitBeforeLoadUser.js +71 -0
  205. package/lib/module/helpers/resolveInitBeforeLoadUser.js.map +1 -1
  206. package/lib/module/hooks/useChatRoomFocus.js +82 -0
  207. package/lib/module/hooks/useChatRoomFocus.js.map +1 -0
  208. package/lib/module/hooks/useChatWrapperInit.js +22 -10
  209. package/lib/module/hooks/useChatWrapperInit.js.map +1 -1
  210. package/lib/module/hooks/useEventHandlers.js +9 -1
  211. package/lib/module/hooks/useEventHandlers.js.map +1 -1
  212. package/lib/module/hooks/useHeapSender.js +1 -1
  213. package/lib/module/hooks/useHeapSender.js.map +1 -1
  214. package/lib/module/hooks/useLogout.js +3 -3
  215. package/lib/module/hooks/useLogout.js.map +1 -1
  216. package/lib/module/hooks/useMessageHeapState.js +6 -2
  217. package/lib/module/hooks/useMessageHeapState.js.map +1 -1
  218. package/lib/module/hooks/usePushNotifications.js +6 -2
  219. package/lib/module/hooks/usePushNotifications.js.map +1 -1
  220. package/lib/module/hooks/useRoomState.js +2 -0
  221. package/lib/module/hooks/useRoomState.js.map +1 -1
  222. package/lib/module/hooks/useSendMessage.js +252 -17
  223. package/lib/module/hooks/useSendMessage.js.map +1 -1
  224. package/lib/module/hooks/useUnreadMessagesCounter.js +12 -14
  225. package/lib/module/hooks/useUnreadMessagesCounter.js.map +1 -1
  226. package/lib/module/main.js +1 -0
  227. package/lib/module/main.js.map +1 -1
  228. package/lib/module/networking/api-requests/auth.api.js +74 -2
  229. package/lib/module/networking/api-requests/auth.api.js.map +1 -1
  230. package/lib/module/networking/apiClient.js +2 -1
  231. package/lib/module/networking/apiClient.js.map +1 -1
  232. package/lib/module/networking/outboundQueue.js +84 -0
  233. package/lib/module/networking/outboundQueue.js.map +1 -0
  234. package/lib/module/networking/xmpp/createTimeoutPromise.xmpp.js +4 -1
  235. package/lib/module/networking/xmpp/createTimeoutPromise.xmpp.js.map +1 -1
  236. package/lib/module/networking/xmpp/flushLastViewedToPrivateStore.js +7 -8
  237. package/lib/module/networking/xmpp/flushLastViewedToPrivateStore.js.map +1 -1
  238. package/lib/module/networking/xmpp/getRoomsPaged.xmpp.js +26 -3
  239. package/lib/module/networking/xmpp/getRoomsPaged.xmpp.js.map +1 -1
  240. package/lib/module/networking/xmpp/presenceInRoom.xmpp.js +26 -8
  241. package/lib/module/networking/xmpp/presenceInRoom.xmpp.js.map +1 -1
  242. package/lib/module/networking/xmpp/sendTextMessage.xmpp.js +2 -1
  243. package/lib/module/networking/xmpp/sendTextMessage.xmpp.js.map +1 -1
  244. package/lib/module/networking/xmppClient.js +371 -26
  245. package/lib/module/networking/xmppClient.js.map +1 -1
  246. package/lib/module/roomStore/Middleware/unreadMidlleware.js +22 -6
  247. package/lib/module/roomStore/Middleware/unreadMidlleware.js.map +1 -1
  248. package/lib/module/roomStore/roomHeapSlice.js +24 -7
  249. package/lib/module/roomStore/roomHeapSlice.js.map +1 -1
  250. package/lib/module/roomStore/roomsSlice.js +154 -22
  251. package/lib/module/roomStore/roomsSlice.js.map +1 -1
  252. package/lib/module/utils/installPromiseRejectionTracker.js +78 -0
  253. package/lib/module/utils/installPromiseRejectionTracker.js.map +1 -0
  254. package/lib/module/utils/resendMessage.js +1 -1
  255. package/lib/module/utils/resendMessage.js.map +1 -1
  256. package/lib/typescript/assets/icons.d.ts +8 -4
  257. package/lib/typescript/assets/icons.d.ts.map +1 -1
  258. package/lib/typescript/components/ContextMenu/ContextMenuComponents.d.ts.map +1 -1
  259. package/lib/typescript/components/MainComponents/ChatHeader.d.ts.map +1 -1
  260. package/lib/typescript/components/MainComponents/ChatRoom.d.ts.map +1 -1
  261. package/lib/typescript/components/MainComponents/ChatWrapper.d.ts.map +1 -1
  262. package/lib/typescript/components/MainComponents/MediaMessage.d.ts +1 -0
  263. package/lib/typescript/components/MainComponents/MediaMessage.d.ts.map +1 -1
  264. package/lib/typescript/components/MainComponents/MessageContainer.d.ts.map +1 -1
  265. package/lib/typescript/components/MainComponents/MessageList.d.ts.map +1 -1
  266. package/lib/typescript/components/MainComponents/ReduxWrapper.d.ts +1 -0
  267. package/lib/typescript/components/MainComponents/ReduxWrapper.d.ts.map +1 -1
  268. package/lib/typescript/components/MainComponents/SystemMessage.d.ts +0 -7
  269. package/lib/typescript/components/MainComponents/SystemMessage.d.ts.map +1 -1
  270. package/lib/typescript/components/MessageBubble/Message.d.ts.map +1 -1
  271. package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts +4 -2
  272. package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts.map +1 -1
  273. package/lib/typescript/components/Modals/AttachSheet/AttachSheet.d.ts.map +1 -1
  274. package/lib/typescript/components/Modals/FilePreviewModal/DocumentViewer.d.ts +29 -0
  275. package/lib/typescript/components/Modals/FilePreviewModal/DocumentViewer.d.ts.map +1 -0
  276. package/lib/typescript/components/Modals/FilePreviewModal/FilePreviewModal.d.ts +0 -1
  277. package/lib/typescript/components/Modals/FilePreviewModal/FilePreviewModal.d.ts.map +1 -1
  278. package/lib/typescript/components/Modals/FilePreviewModal/PdfView.d.ts.map +1 -1
  279. package/lib/typescript/components/Modals/Modal/Modal.d.ts.map +1 -1
  280. package/lib/typescript/components/Modals/ModalWrapper/ModalWrapper.d.ts +6 -0
  281. package/lib/typescript/components/Modals/ModalWrapper/ModalWrapper.d.ts.map +1 -1
  282. package/lib/typescript/components/Modals/UserProfileModal/UserProfileModal.d.ts.map +1 -1
  283. package/lib/typescript/components/Modals/styledModalComponents.d.ts.map +1 -1
  284. package/lib/typescript/components/styled/MediaFilePreview.d.ts.map +1 -1
  285. package/lib/typescript/components/styled/MessageImage.d.ts.map +1 -1
  286. package/lib/typescript/components/styled/NewMessageLabel.d.ts +1 -5
  287. package/lib/typescript/components/styled/NewMessageLabel.d.ts.map +1 -1
  288. package/lib/typescript/components/styled/SendInput.d.ts.map +1 -1
  289. package/lib/typescript/components/styled/StyledInputComponents/MediaComponents.d.ts +9 -1
  290. package/lib/typescript/components/styled/StyledInputComponents/MediaComponents.d.ts.map +1 -1
  291. package/lib/typescript/components/styled/StyledInputComponents/StyledInputComponents.d.ts.map +1 -1
  292. package/lib/typescript/components/styled/UnsupportedType.d.ts +1 -0
  293. package/lib/typescript/components/styled/UnsupportedType.d.ts.map +1 -1
  294. package/lib/typescript/components/styled/VideoMessage.d.ts.map +1 -1
  295. package/lib/typescript/context/xmppProvider.d.ts +1 -0
  296. package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
  297. package/lib/typescript/helpers/chatAutoEnterer.d.ts.map +1 -1
  298. package/lib/typescript/helpers/historyPreloadScheduler.d.ts.map +1 -1
  299. package/lib/typescript/helpers/insertMessageWithDelimiter.d.ts.map +1 -1
  300. package/lib/typescript/helpers/keyboardLayout.d.ts +9 -0
  301. package/lib/typescript/helpers/keyboardLayout.d.ts.map +1 -0
  302. package/lib/typescript/helpers/mediaDimensions.d.ts +9 -0
  303. package/lib/typescript/helpers/mediaDimensions.d.ts.map +1 -0
  304. package/lib/typescript/helpers/mimeToExtension.d.ts +37 -0
  305. package/lib/typescript/helpers/mimeToExtension.d.ts.map +1 -0
  306. package/lib/typescript/helpers/normalizeRoomJid.d.ts +2 -0
  307. package/lib/typescript/helpers/normalizeRoomJid.d.ts.map +1 -0
  308. package/lib/typescript/helpers/resolveInitBeforeLoadUser.d.ts +18 -0
  309. package/lib/typescript/helpers/resolveInitBeforeLoadUser.d.ts.map +1 -1
  310. package/lib/typescript/hooks/useChatRoomFocus.d.ts +42 -0
  311. package/lib/typescript/hooks/useChatRoomFocus.d.ts.map +1 -0
  312. package/lib/typescript/hooks/useChatWrapperInit.d.ts.map +1 -1
  313. package/lib/typescript/hooks/useEventHandlers.d.ts +6 -0
  314. package/lib/typescript/hooks/useEventHandlers.d.ts.map +1 -1
  315. package/lib/typescript/hooks/useHeapSender.d.ts.map +1 -1
  316. package/lib/typescript/hooks/useMessageHeapState.d.ts +2 -0
  317. package/lib/typescript/hooks/useMessageHeapState.d.ts.map +1 -1
  318. package/lib/typescript/hooks/usePushNotifications.d.ts.map +1 -1
  319. package/lib/typescript/hooks/useRoomState.d.ts +1 -0
  320. package/lib/typescript/hooks/useRoomState.d.ts.map +1 -1
  321. package/lib/typescript/hooks/useSendMessage.d.ts +2 -1
  322. package/lib/typescript/hooks/useSendMessage.d.ts.map +1 -1
  323. package/lib/typescript/hooks/useUnreadMessagesCounter.d.ts.map +1 -1
  324. package/lib/typescript/main.d.ts +1 -0
  325. package/lib/typescript/main.d.ts.map +1 -1
  326. package/lib/typescript/networking/api-requests/auth.api.d.ts +3 -0
  327. package/lib/typescript/networking/api-requests/auth.api.d.ts.map +1 -1
  328. package/lib/typescript/networking/apiClient.d.ts.map +1 -1
  329. package/lib/typescript/networking/outboundQueue.d.ts +57 -0
  330. package/lib/typescript/networking/outboundQueue.d.ts.map +1 -0
  331. package/lib/typescript/networking/xmpp/createTimeoutPromise.xmpp.d.ts.map +1 -1
  332. package/lib/typescript/networking/xmpp/flushLastViewedToPrivateStore.d.ts +1 -1
  333. package/lib/typescript/networking/xmpp/flushLastViewedToPrivateStore.d.ts.map +1 -1
  334. package/lib/typescript/networking/xmpp/getRoomsPaged.xmpp.d.ts +1 -1
  335. package/lib/typescript/networking/xmpp/getRoomsPaged.xmpp.d.ts.map +1 -1
  336. package/lib/typescript/networking/xmpp/presenceInRoom.xmpp.d.ts.map +1 -1
  337. package/lib/typescript/networking/xmpp/sendTextMessage.xmpp.d.ts.map +1 -1
  338. package/lib/typescript/networking/xmppClient.d.ts +58 -5
  339. package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
  340. package/lib/typescript/roomStore/Middleware/unreadMidlleware.d.ts.map +1 -1
  341. package/lib/typescript/roomStore/roomHeapSlice.d.ts +22 -1
  342. package/lib/typescript/roomStore/roomHeapSlice.d.ts.map +1 -1
  343. package/lib/typescript/roomStore/roomsSlice.d.ts +8 -1
  344. package/lib/typescript/roomStore/roomsSlice.d.ts.map +1 -1
  345. package/lib/typescript/types/models/media.model.d.ts +1 -0
  346. package/lib/typescript/types/models/media.model.d.ts.map +1 -1
  347. package/lib/typescript/types/models/xmpp.model.d.ts +1 -1
  348. package/lib/typescript/types/models/xmpp.model.d.ts.map +1 -1
  349. package/lib/typescript/types/types.d.ts +10 -0
  350. package/lib/typescript/types/types.d.ts.map +1 -1
  351. package/lib/typescript/utils/installPromiseRejectionTracker.d.ts +20 -0
  352. package/lib/typescript/utils/installPromiseRejectionTracker.d.ts.map +1 -0
  353. package/lib/typescript/utils/resendMessage.d.ts.map +1 -1
  354. package/package.json +18 -2
  355. package/src/assets/icons.tsx +23 -14
  356. package/src/components/ContextMenu/ContextMenuComponents.tsx +10 -5
  357. package/src/components/MainComponents/ChatHeader.tsx +5 -1
  358. package/src/components/MainComponents/ChatRoom.tsx +81 -61
  359. package/src/components/MainComponents/ChatWrapper.tsx +100 -21
  360. package/src/components/MainComponents/MediaMessage.tsx +18 -8
  361. package/src/components/MainComponents/MessageContainer.tsx +4 -2
  362. package/src/components/MainComponents/MessageList.tsx +185 -31
  363. package/src/components/MainComponents/ReduxWrapper.tsx +19 -9
  364. package/src/components/MainComponents/SystemMessage.tsx +38 -26
  365. package/src/components/MessageBubble/Message.tsx +110 -27
  366. package/src/components/MessageBubble/MessageInteractions.tsx +98 -29
  367. package/src/components/Modals/AttachSheet/AttachSheet.tsx +17 -3
  368. package/src/components/Modals/FilePreviewModal/DocumentViewer.tsx +93 -0
  369. package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +182 -37
  370. package/src/components/Modals/FilePreviewModal/PdfView.tsx +32 -4
  371. package/src/components/Modals/Modal/Modal.tsx +4 -1
  372. package/src/components/Modals/ModalWrapper/ModalWrapper.tsx +77 -44
  373. package/src/components/Modals/UserProfileModal/UserProfileModal.tsx +19 -15
  374. package/src/components/Modals/styledModalComponents.tsx +4 -9
  375. package/src/components/styled/MediaFilePreview.tsx +111 -5
  376. package/src/components/styled/MessageImage.tsx +58 -14
  377. package/src/components/styled/NewMessageLabel.tsx +8 -5
  378. package/src/components/styled/SendInput.tsx +67 -29
  379. package/src/components/styled/StyledInputComponents/MediaComponents.tsx +15 -5
  380. package/src/components/styled/StyledInputComponents/StyledInputComponents.tsx +7 -2
  381. package/src/components/styled/UnsupportedType.tsx +7 -1
  382. package/src/components/styled/VideoMessage.tsx +59 -18
  383. package/src/context/MessageNotificationContext.tsx +8 -8
  384. package/src/context/xmppProvider.tsx +337 -32
  385. package/src/helpers/chatAutoEnterer.ts +2 -8
  386. package/src/helpers/historyPreloadScheduler.ts +4 -1
  387. package/src/helpers/insertMessageWithDelimiter.ts +22 -15
  388. package/src/helpers/keyboardLayout.ts +18 -0
  389. package/src/helpers/mediaDimensions.ts +47 -0
  390. package/src/helpers/mimeToExtension.ts +135 -0
  391. package/src/helpers/normalizeRoomJid.ts +6 -0
  392. package/src/helpers/resolveInitBeforeLoadUser.ts +79 -0
  393. package/src/hooks/useChatRoomFocus.ts +95 -0
  394. package/src/hooks/useChatWrapperInit.ts +51 -46
  395. package/src/hooks/useEventHandlers.tsx +24 -1
  396. package/src/hooks/useHeapSender.tsx +2 -1
  397. package/src/hooks/useLogout.tsx +3 -3
  398. package/src/hooks/useMessageHeapState.tsx +6 -2
  399. package/src/hooks/usePushNotifications.ts +21 -8
  400. package/src/hooks/useRoomState.tsx +4 -0
  401. package/src/hooks/useSendMessage.tsx +333 -17
  402. package/src/hooks/useUnreadMessagesCounter.ts +13 -16
  403. package/src/main.ts +1 -0
  404. package/src/networking/api-requests/auth.api.ts +60 -2
  405. package/src/networking/apiClient.ts +2 -1
  406. package/src/networking/outboundQueue.ts +98 -0
  407. package/src/networking/xmpp/createTimeoutPromise.xmpp.ts +5 -1
  408. package/src/networking/xmpp/flushLastViewedToPrivateStore.ts +11 -13
  409. package/src/networking/xmpp/getRoomsPaged.xmpp.ts +27 -3
  410. package/src/networking/xmpp/presenceInRoom.xmpp.ts +24 -9
  411. package/src/networking/xmpp/sendTextMessage.xmpp.ts +2 -1
  412. package/src/networking/xmppClient.ts +399 -31
  413. package/src/roomStore/Middleware/unreadMidlleware.tsx +23 -5
  414. package/src/roomStore/roomHeapSlice.ts +47 -4
  415. package/src/roomStore/roomsSlice.ts +177 -27
  416. package/src/types/models/media.model.ts +3 -0
  417. package/src/types/models/xmpp.model.ts +2 -1
  418. package/src/types/types.ts +25 -0
  419. package/src/utils/installPromiseRejectionTracker.ts +91 -0
  420. package/src/utils/resendMessage.ts +2 -1
@@ -9,6 +9,7 @@ import {
9
9
  } from '../../roomStore/chatSettingsSlice';
10
10
  import {ChatWrapperBox} from '../styled/ChatWrapperBox';
11
11
  import {Overlay, StyledModal} from '../styled/MediaModal';
12
+ import ConnectionBanner from './ConnectionBanner';
12
13
  import {Message} from '../MessageBubble/Message';
13
14
  import {IConfig, IRoom, MessageProps, ModalType, User} from '../../types/types';
14
15
  import {useXmppClient} from '../../context/xmppProvider';
@@ -30,6 +31,7 @@ import {ModalWrapper} from '../Modals/ModalWrapper/ModalWrapper';
30
31
  import {useChatSettingState} from '../../hooks/useChatSettingState';
31
32
  import {DeviceEventEmitter, Pressable, Text, View} from 'react-native';
32
33
  import {pushLog as devPushLog} from '../../utils/devLogger';
34
+ import {normalizeRoomJid} from '../../helpers/normalizeRoomJid';
33
35
 
34
36
  interface ChatWrapperProps {
35
37
  token?: string;
@@ -126,7 +128,11 @@ const ChatWrapper: FC<ChatWrapperProps> = ({
126
128
 
127
129
  useEffect(() => {
128
130
  if (roomJID) {
129
- dispatch(setCurrentRoom({roomJID: roomJID}));
131
+ dispatch(
132
+ setCurrentRoom({
133
+ roomJID: normalizeRoomJid(roomJID, config?.xmppSettings?.conference),
134
+ }),
135
+ );
130
136
  }
131
137
 
132
138
  const initXmmpClient = async () => {
@@ -186,45 +192,79 @@ const ChatWrapper: FC<ChatWrapperProps> = ({
186
192
 
187
193
  if (!client && !storedClient) {
188
194
  devPushLog('rn', 'ChatWrapper: initing xmpp client (legacy path)');
189
- await initializeClient(
190
- user.xmppUsername || user.defaultWallet?.walletAddress,
191
- user.xmppPassword,
192
- config?.xmppSettings
193
- ).then(c => {
194
- c.getRoomsStanza()
195
- .then(() => {
196
- c.getChatsPrivateStoreRequestStanza();
197
- dispatch(setStoreClient(c));
198
- setClient(c);
199
- })
200
- .catch((err: unknown) => console.warn('getRoomsStanza failed', err));
201
- });
195
+ // The outer `.then(c => {...})` used to lack a `.catch()` —
196
+ // if initializeClient rejected (network / SASL), the outer
197
+ // try/catch couldn't see it because the rejection happens in
198
+ // the .then handler, not in the awaited promise itself.
199
+ // Convert to await + try/catch.
200
+ try {
201
+ const c = await initializeClient(
202
+ user.xmppUsername || user.defaultWallet?.walletAddress,
203
+ user.xmppPassword,
204
+ config?.xmppSettings
205
+ );
206
+ try {
207
+ await c.getRoomsStanza();
208
+ c.getChatsPrivateStoreRequestStanza().catch((err: unknown) =>
209
+ console.warn('getChatsPrivateStoreRequestStanza failed', err)
210
+ );
211
+ dispatch(setStoreClient(c));
212
+ setClient(c);
213
+ } catch (err) {
214
+ console.warn('getRoomsStanza failed', err);
215
+ }
216
+ } catch (err) {
217
+ devPushLog('warn', 'initializeClient failed (legacy path)', err);
218
+ }
202
219
  setInited(true);
203
- if (config?.refreshTokens?.enabled) {refresh();}
220
+ if (config?.refreshTokens?.enabled) {
221
+ // refresh() is fire-and-forget but rejects on bad
222
+ // refreshToken / network — surface via .catch instead of
223
+ // leaving the rejection unhandled (was bug #4, id:2).
224
+ refresh().catch((err) =>
225
+ console.warn('refresh failed', err)
226
+ );
227
+ }
204
228
  } else if (storedClient) {
205
229
  devPushLog('rn', 'ChatWrapper: reusing storedClient');
206
230
  setClient(storedClient);
207
231
  if (!activeRoomJID) {
208
232
  storedClient.getRoomsStanza()
209
233
  .then(() => {
210
- storedClient.getChatsPrivateStoreRequestStanza();
234
+ storedClient.getChatsPrivateStoreRequestStanza().catch(
235
+ (err: unknown) =>
236
+ console.warn('getChatsPrivateStoreRequestStanza failed', err)
237
+ );
211
238
  })
212
239
  .catch((err: unknown) => console.warn('getRoomsStanza failed', err));
213
240
  }
214
241
  setInited(true);
215
- if (config?.refreshTokens?.enabled) {refresh();}
242
+ if (config?.refreshTokens?.enabled) {
243
+ refresh().catch((err) =>
244
+ console.warn('refresh failed', err)
245
+ );
246
+ }
216
247
  } else if (client) {
217
248
  devPushLog('rn', 'ChatWrapper: reusing provider client');
218
249
  if (!activeRoomJID) {
219
250
  client.getRoomsStanza()
220
251
  .then(() => {
221
- client.getChatsPrivateStoreRequestStanza();
252
+ client.getChatsPrivateStoreRequestStanza().catch(
253
+ (err: unknown) =>
254
+ console.warn('getChatsPrivateStoreRequestStanza failed', err)
255
+ );
222
256
  })
223
257
  .catch((err: unknown) => console.warn('getRoomsStanza failed', err));
224
258
  }
225
- client.getChatsPrivateStoreRequestStanza();
259
+ client.getChatsPrivateStoreRequestStanza().catch((err: unknown) =>
260
+ console.warn('getChatsPrivateStoreRequestStanza failed', err)
261
+ );
226
262
  setInited(true);
227
- if (config?.refreshTokens?.enabled) {refresh();}
263
+ if (config?.refreshTokens?.enabled) {
264
+ refresh().catch((err) =>
265
+ console.warn('refresh failed', err)
266
+ );
267
+ }
228
268
  }
229
269
 
230
270
  dispatch(setIsLoading({loading: false}));
@@ -250,6 +290,38 @@ const ChatWrapper: FC<ChatWrapperProps> = ({
250
290
  config?.initBeforeLoad,
251
291
  ]);
252
292
 
293
+ // Cache-first display (initBeforeLoad). The main effect above only marks
294
+ // the chat `inited` once the provider bootstrap reaches 'ready' — which
295
+ // waits on the cold XMPP connect. On re-entry that left the user staring
296
+ // at a loader while fully-cached rooms/messages sat in redux-persist
297
+ // ("pulls a new connection for 100 years"). Here we render the cached
298
+ // content the instant rehydration lands; the live client gets wired by
299
+ // the main effect when the connect completes (send/loadMore activate
300
+ // then). Mirrors web showing cached rooms immediately + refreshing in
301
+ // the background.
302
+ useEffect(() => {
303
+ if (!config?.initBeforeLoad || initMode !== 'provider') {return;}
304
+ if (isInited || showModal) {return;}
305
+ if (providerBootstrapStatus === 'failed') {return;}
306
+ const haveCachedRooms = !!rooms && Object.keys(rooms).length > 0;
307
+ const haveUser =
308
+ !!user?.xmppUsername || !!user?.defaultWallet?.walletAddress;
309
+ if (haveCachedRooms && haveUser) {
310
+ devPushLog('rn', 'ChatWrapper: cache-first — showing persisted rooms while provider connects');
311
+ setShowModal(false);
312
+ setInited(true);
313
+ }
314
+ }, [
315
+ config?.initBeforeLoad,
316
+ initMode,
317
+ isInited,
318
+ showModal,
319
+ providerBootstrapStatus,
320
+ rooms,
321
+ user?.xmppUsername,
322
+ user?.defaultWallet?.walletAddress,
323
+ ]);
324
+
253
325
  // Skip the legacy email/password LoginForm entirely when XmppProvider
254
326
  // is driving auth via initBeforeLoad — the bootstrap effect will
255
327
  // populate the user shortly. Showing LoginForm here causes a flicker
@@ -270,7 +342,13 @@ const ChatWrapper: FC<ChatWrapperProps> = ({
270
342
 
271
343
  return (
272
344
  <>
273
- {showModal && (
345
+ {/* Connection/bootstrap error UI. For patient-facing apps the
346
+ full-screen dark overlay reads as a crash, so
347
+ `disableConnectionErrorOverlay` swaps it for a subtle inline
348
+ "Connection lost. Retrying…" banner (and reconnect + re-join now
349
+ recover automatically). */}
350
+ {showModal && config?.disableConnectionErrorOverlay && <ConnectionBanner />}
351
+ {showModal && !config?.disableConnectionErrorOverlay && (
274
352
  <Overlay>
275
353
  <View
276
354
  style={{
@@ -356,6 +434,7 @@ const ChatWrapper: FC<ChatWrapperProps> = ({
356
434
  backgroundColorButton="#E53935"
357
435
  handleClick={handleDeleteClick}
358
436
  handleCloseModal={handleCloseDeleteModal}
437
+ compact
359
438
  />
360
439
  )}
361
440
  </ChatWrapperBox>
@@ -5,12 +5,14 @@ import CustomMessageImage from '../styled/MessageImage';
5
5
  import CustomMessageVideo from '../styled/VideoMessage';
6
6
  import AudioMessage from '../styled/AudioMessage';
7
7
  import { Text } from 'react-native';
8
+ import { deriveDisplayFilename } from '../../helpers/mimeToExtension';
8
9
 
9
10
  interface MediaMessageProps {
10
11
  mimeType?: string;
11
12
  message?: IMessage;
12
13
  location?: string;
13
14
  messageText?: string;
15
+ isUser: boolean;
14
16
  }
15
17
 
16
18
  const MediaMessage: React.FC<MediaMessageProps> = ({
@@ -18,13 +20,20 @@ const MediaMessage: React.FC<MediaMessageProps> = ({
18
20
  location,
19
21
  messageText,
20
22
  message,
23
+ isUser,
21
24
  }) => {
22
- if (mimeType)
23
- {switch (true) {
25
+ if (mimeType) {
26
+ const displayName = deriveDisplayFilename({
27
+ fileName: message?.fileName,
28
+ originalName: (message as any)?.originalName,
29
+ url: location || messageText,
30
+ mime: mimeType,
31
+ });
32
+ switch (true) {
24
33
  case mimeType.startsWith('image/'):
25
34
  return (
26
35
  <CustomMessageImage
27
- fileName={message?.fileName || '000132001'}
36
+ fileName={displayName}
28
37
  fileURL={messageText || ''}
29
38
  mimetype={mimeType}
30
39
  />
@@ -32,7 +41,7 @@ const MediaMessage: React.FC<MediaMessageProps> = ({
32
41
  case mimeType.startsWith('video/'):
33
42
  return (
34
43
  <CustomMessageVideo
35
- fileName={message?.fileName || '000132001'}
44
+ fileName={displayName}
36
45
  fileURL={location || ''}
37
46
  mimetype={mimeType}
38
47
  />
@@ -44,21 +53,22 @@ const MediaMessage: React.FC<MediaMessageProps> = ({
44
53
  // Many backends tag binary payloads (PDFs, DOCX, archives) as
45
54
  // application/octet-stream; sniff the filename extension for the
46
55
  // legitimate audio case before falling through to FileDownload.
47
- const fileName = message?.fileName || location?.split('/')?.pop() || 'MediaFile';
48
56
  if (mimeType.includes('application/octet-stream')) {
49
- if (/\.(mp3|m4a|wav|aac|ogg|flac)$/i.test(fileName)) {
57
+ if (/\.(mp3|m4a|wav|aac|ogg|flac)$/i.test(displayName)) {
50
58
  return <AudioMessage src={location || ''} />;
51
59
  }
52
60
  }
53
61
  return (
54
62
  <FileDownload
55
63
  fileURL={location ? location : ''}
56
- fileName={fileName}
64
+ fileName={displayName}
57
65
  mimetype={mimeType}
66
+ isUser={isUser}
58
67
  />
59
68
  );
60
69
  }
61
- }}
70
+ }
71
+ }
62
72
  return <Text>Unsupported media type</Text>;
63
73
  };
64
74
 
@@ -73,7 +73,7 @@ export const MessageContainer: FC<MessageContainerProps> = ({
73
73
  );
74
74
  }
75
75
 
76
- if (message?.id === 'delimiter-new') {
76
+ if (String(message?.id || '').startsWith('delimiter-new')) {
77
77
  return CustomNewMessageLabel ? (
78
78
  <CustomNewMessageLabel color={config?.colors?.primary} />
79
79
  ) : (
@@ -85,7 +85,9 @@ export const MessageContainer: FC<MessageContainerProps> = ({
85
85
 
86
86
  return (
87
87
  <View key={message.id}>
88
- {showDateLabel && !activeMessage && message.id !== 'delimiter-new' ? (
88
+ {showDateLabel &&
89
+ !activeMessage &&
90
+ !String(message.id || '').startsWith('delimiter-new') ? (
89
91
  CustomDaySeparator ? (
90
92
  <CustomDaySeparator
91
93
  date={messageDate}
@@ -9,16 +9,17 @@ import React, {
9
9
  } from 'react';
10
10
  import {
11
11
  View,
12
+ Text,
12
13
  StyleSheet,
13
14
  FlatList,
14
15
  Image,
15
16
  NativeSyntheticEvent,
16
17
  NativeScrollEvent,
17
18
  TouchableOpacity,
18
- Keyboard,
19
19
  ImageSourcePropType,
20
20
  } from 'react-native';
21
21
  import { IMessage, User, IConfig, IRoom } from '../../types/types';
22
+ import { msgSortableMs } from '../../roomStore/roomsSlice';
22
23
  import Composing from '../styled/StyledInputComponents/Composing';
23
24
  import TreadLabel from '../styled/TreadLabel';
24
25
  import { MessageContainer } from './MessageContainer';
@@ -69,10 +70,24 @@ const MessageList = <TMessage extends IMessage>({
69
70
  .room! as IRoom;
70
71
  const [isUserAtBottom, setIsUserAtBottom] = useState(true);
71
72
  const [showNewMessageIndicator, setShowNewMessageIndicator] = useState(false);
72
- const [isContentOffset, setIsContentOffset] = useState(false);
73
73
  const [isLoadingMore, setIsLoadingMore] = useState(false);
74
74
  const [isScrollBlocked, setIsScrollBlocked] = useState(false);
75
- const [hasUserScrolled, setHasUserScrolled] = useState(false);
75
+ // Count of messages that arrived while the user was scrolled up.
76
+ // Renders as a badge on the scroll-to-bottom arrow so they know how
77
+ // much they're missing without having to scroll to find out.
78
+ const [unreadWhileScrolledUp, setUnreadWhileScrolledUp] = useState(0);
79
+ // Newest message timestamp at the moment the user scrolled away from
80
+ // the bottom. The badge counts only messages NEWER than this, so
81
+ // back-pagination (loading OLDER history while scrolled up) never
82
+ // inflates the count. Was previously a message-COUNT snapshot, which
83
+ // wrongly counted back-paginated old messages as "new" (Android repro).
84
+ const newestSeenTsRef = useRef<number | null>(null);
85
+ // Refs mirror the viewport state synchronously. The auto-follow logic
86
+ // runs from FlatList callbacks where setState hasn't necessarily
87
+ // committed yet; relying on stale React state is what caused the chat
88
+ // to jump back to the bottom when a new message landed mid-scroll.
89
+ const isUserAtBottomRef = useRef(true);
90
+ const hasUserScrolledRef = useRef(false);
76
91
 
77
92
  const flatListRef = useRef<FlatList<IMessage>>(null);
78
93
 
@@ -129,7 +144,10 @@ const MessageList = <TMessage extends IMessage>({
129
144
  const bNum = new Date(b?.date as any).getTime() || 0;
130
145
  return aNum - bNum;
131
146
  });
132
- return sorted;
147
+ const deduped = Array.from(
148
+ new Map(sorted.map((m) => [m.id, m])).values()
149
+ );
150
+ return deduped;
133
151
  }, [addReplyMessages, isReply, roomJID, activeMessage]);
134
152
 
135
153
  const handleLoadMore = useCallback(async () => {
@@ -174,16 +192,72 @@ const MessageList = <TMessage extends IMessage>({
174
192
  ]);
175
193
 
176
194
  const dataMessages = useMemo(() => {
177
- return memoizedMessages.slice().reverse();
178
- }, [memoizedMessages]);
195
+ const base = memoizedMessages.slice().reverse();
196
+ const boundaryTs = newestSeenTsRef.current;
197
+ if (
198
+ isUserAtBottomRef.current ||
199
+ boundaryTs === null ||
200
+ unreadWhileScrolledUp === 0
201
+ ) {
202
+ return base;
203
+ }
204
+
205
+ let lastNewIndex = -1;
206
+ for (let i = 0; i < base.length; i++) {
207
+ if (msgSortableMs(base[i]) > boundaryTs) {
208
+ lastNewIndex = i;
209
+ } else {
210
+ break;
211
+ }
212
+ }
213
+
214
+ if (lastNewIndex === -1) {return base;}
215
+
216
+ const withDivider = base.slice();
217
+ withDivider.splice(lastNewIndex + 1, 0, {
218
+ id: 'delimiter-new-local',
219
+ user: {
220
+ id: 'system',
221
+ name: undefined,
222
+ token: '',
223
+ refreshToken: '',
224
+ } as any,
225
+ date: new Date(boundaryTs).toISOString(),
226
+ body: 'New Messages',
227
+ roomJid: roomJID,
228
+ } as IMessage);
229
+ return withDivider;
230
+ }, [memoizedMessages, isUserAtBottom, roomJID, unreadWhileScrolledUp]);
179
231
 
180
232
  const renderMessage = useCallback(
181
233
  ({ item, index }: { item: IMessage; index: number }) => {
234
+ if (String(item.id).startsWith('delimiter-new')) {
235
+ return (
236
+ <MessageContainer
237
+ CustomMessage={CustomMessage}
238
+ CustomDaySeparator={CustomDaySeparator}
239
+ CustomNewMessageLabel={CustomNewMessageLabel}
240
+ message={item}
241
+ activeMessage={activeMessage}
242
+ config={config}
243
+ walletAddress={user.xmppUsername || user.walletAddress}
244
+ isReply={isReply}
245
+ showDateLabel={false}
246
+ />
247
+ );
248
+ }
249
+
182
250
  const messageDate = new Date(item.date).toDateString();
183
251
  let showDateLabel = false;
184
252
 
185
- const nextMessage =
186
- index < dataMessages.length - 1 ? dataMessages[index + 1] : null;
253
+ let nextMessage = null;
254
+ for (let i = index + 1; i < dataMessages.length; i++) {
255
+ const candidate = dataMessages[i];
256
+ if (!String(candidate.id).startsWith('delimiter-new')) {
257
+ nextMessage = candidate;
258
+ break;
259
+ }
260
+ }
187
261
  const nextMessageDate = nextMessage
188
262
  ? new Date(nextMessage.date).toDateString()
189
263
  : null;
@@ -209,10 +283,13 @@ const MessageList = <TMessage extends IMessage>({
209
283
  [
210
284
  activeMessage,
211
285
  config,
286
+ CustomDaySeparator,
287
+ CustomMessage,
288
+ CustomNewMessageLabel,
289
+ dataMessages,
290
+ isReply,
212
291
  user.xmppUsername,
213
292
  user.walletAddress,
214
- isReply,
215
- memoizedMessages.length,
216
293
  ]
217
294
  );
218
295
 
@@ -226,57 +303,94 @@ const MessageList = <TMessage extends IMessage>({
226
303
  }, [flatListRef]);
227
304
 
228
305
  const handleContentSizeChange = useCallback(() => {
229
- if (isContentOffset) {
230
- scrollToBottom();
231
- setIsContentOffset(false);
232
- }
233
- if (!isLoadingMore && flatListRef.current && isUserAtBottom) {
306
+ if (!isLoadingMore && flatListRef.current && isUserAtBottomRef.current) {
234
307
  scrollToBottom();
235
- } else if (!isLoadingMore && hasUserScrolled) {
308
+ } else if (!isLoadingMore && hasUserScrolledRef.current) {
236
309
  setShowNewMessageIndicator(true);
237
310
  }
238
- }, [isUserAtBottom, scrollToBottom, isLoadingMore, hasUserScrolled]);
311
+ }, [scrollToBottom, isLoadingMore]);
239
312
 
240
313
  const handleScroll = useCallback(
241
314
  (event: NativeSyntheticEvent<NativeScrollEvent>) => {
242
315
  const contentOffset = event.nativeEvent.contentOffset.y;
243
316
 
244
- Keyboard.dismiss();
317
+ // Don't force-dismiss the keyboard on every scroll event — any
318
+ // touch on the message list used to close it, which made replying
319
+ // to a long thread feel broken. Drag-to-dismiss still works via
320
+ // FlatList's keyboardDismissMode="interactive" below.
245
321
 
246
- if (contentOffset > 150 && !hasUserScrolled) {
247
- setHasUserScrolled(true);
322
+ if (contentOffset > 150 && !hasUserScrolledRef.current) {
323
+ hasUserScrolledRef.current = true;
248
324
  }
249
325
 
250
326
  if (contentOffset < 150) {
251
- setIsContentOffset(true);
327
+ isUserAtBottomRef.current = true;
252
328
  setShowNewMessageIndicator(false);
253
329
  setIsUserAtBottom(true);
330
+ // Back at the bottom — clear the unread-while-scrolled-up
331
+ // counter so the badge disappears with the arrow.
332
+ setUnreadWhileScrolledUp(0);
333
+ newestSeenTsRef.current = null;
254
334
  } else {
255
- setIsContentOffset(false);
256
- if (hasUserScrolled) {
335
+ isUserAtBottomRef.current = false;
336
+ if (hasUserScrolledRef.current) {
257
337
  setShowNewMessageIndicator(true);
258
338
  }
339
+ if (newestSeenTsRef.current === null) {
340
+ // Snapshot the NEWEST message's timestamp the moment the user
341
+ // first leaves the bottom. Anything newer than this that
342
+ // arrives later is genuinely "new"; older history loaded by
343
+ // back-pagination is < this and never counts.
344
+ newestSeenTsRef.current = memoizedMessages.reduce(
345
+ (mx: number, m: IMessage) => {
346
+ const t = msgSortableMs(m);
347
+ return t > mx ? t : mx;
348
+ },
349
+ 0
350
+ );
351
+ }
259
352
  setIsUserAtBottom(false);
260
353
  }
261
354
  },
262
- [hasUserScrolled]
355
+ [memoizedMessages]
263
356
  );
264
357
 
358
+ // Keep the badge in sync with messages that arrive while the user is
359
+ // scrolled up. Count ONLY messages newer than the newest-seen snapshot
360
+ // — so loading older history (back-pagination) doesn't inflate it.
361
+ useEffect(() => {
362
+ if (isUserAtBottomRef.current) {return;}
363
+ if (newestSeenTsRef.current === null) {return;}
364
+ const since = newestSeenTsRef.current;
365
+ const count = memoizedMessages.reduce(
366
+ (n: number, m: IMessage) => (msgSortableMs(m) > since ? n + 1 : n),
367
+ 0
368
+ );
369
+ setUnreadWhileScrolledUp(count);
370
+ }, [memoizedMessages, isUserAtBottom]);
371
+
265
372
  const handleLayout = () => {
266
- setIsContentOffset(true);
373
+ isUserAtBottomRef.current = true;
374
+ hasUserScrolledRef.current = false;
267
375
  setIsUserAtBottom(true);
268
376
  };
269
377
 
270
378
  const handleNewMessageIndicatorPress = () => {
379
+ isUserAtBottomRef.current = true;
271
380
  setShowNewMessageIndicator(false);
272
381
  setIsUserAtBottom(true);
382
+ setUnreadWhileScrolledUp(0);
383
+ newestSeenTsRef.current = null;
273
384
  scrollToBottom();
274
385
  };
275
386
 
276
387
  useEffect(() => {
277
- setHasUserScrolled(false);
388
+ hasUserScrolledRef.current = false;
389
+ isUserAtBottomRef.current = true;
278
390
  setShowNewMessageIndicator(false);
279
391
  setIsUserAtBottom(true);
392
+ setUnreadWhileScrolledUp(0);
393
+ newestSeenTsRef.current = null;
280
394
  }, [roomJID]);
281
395
 
282
396
  const BackgroundImage = useMemo(() => {
@@ -316,7 +430,7 @@ const MessageList = <TMessage extends IMessage>({
316
430
  )}
317
431
  <FlatList
318
432
  ref={flatListRef}
319
- data={memoizedMessages.slice().reverse()}
433
+ data={dataMessages}
320
434
  renderItem={renderMessage}
321
435
  keyExtractor={(item) => item.id.toString()}
322
436
  onEndReached={handleLoadMore}
@@ -326,6 +440,8 @@ const MessageList = <TMessage extends IMessage>({
326
440
  scrollEventThrottle={16}
327
441
  onLayout={handleLayout}
328
442
  inverted={true}
443
+ keyboardShouldPersistTaps="handled"
444
+ keyboardDismissMode="interactive"
329
445
  contentContainerStyle={styles.flatListContent}
330
446
  ListFooterComponent={
331
447
  loading && memoizedMessages.length > 15 ? (
@@ -337,11 +453,21 @@ const MessageList = <TMessage extends IMessage>({
337
453
  <TouchableOpacity
338
454
  style={[
339
455
  styles.newMessageIndicator,
340
- { backgroundColor: config?.colors?.secondary },
456
+ { backgroundColor: config?.colors?.primary || '#0052CD' },
341
457
  ]}
342
458
  onPress={handleNewMessageIndicatorPress}
343
459
  >
344
- <ArowDownIcon />
460
+ {/* White chevron on the saturated FAB. The icon's default
461
+ color is white, which was invisible against the previous
462
+ light `secondary` background — hence "no icon". */}
463
+ <ArowDownIcon color="#FFFFFF" width={22} height={22} />
464
+ {unreadWhileScrolledUp > 0 && (
465
+ <View style={styles.newMessageBadge}>
466
+ <Text style={styles.newMessageBadgeText}>
467
+ {unreadWhileScrolledUp > 99 ? '99+' : unreadWhileScrolledUp}
468
+ </Text>
469
+ </View>
470
+ )}
345
471
  </TouchableOpacity>
346
472
  )}
347
473
  {config?.customTypingIndicator?.enabled && composing && (
@@ -400,12 +526,40 @@ const styles = StyleSheet.create({
400
526
  height: 40,
401
527
  bottom: 20,
402
528
  right: 20,
403
- backgroundColor: '#007AFF',
404
- padding: 10,
529
+ backgroundColor: '#0052CD',
405
530
  borderRadius: 20,
531
+ justifyContent: 'center',
532
+ alignItems: 'center',
533
+ // Lift the FAB off the chat background so it reads as a button.
534
+ shadowColor: '#000',
535
+ shadowOffset: { width: 0, height: 2 },
536
+ shadowOpacity: 0.2,
537
+ shadowRadius: 3,
538
+ elevation: 4,
406
539
  },
407
540
  newMessageText: {
408
541
  color: '#fff',
409
542
  fontWeight: 'bold',
410
543
  },
544
+ newMessageBadge: {
545
+ position: 'absolute',
546
+ top: -6,
547
+ right: -6,
548
+ minWidth: 20,
549
+ height: 20,
550
+ paddingHorizontal: 6,
551
+ borderRadius: 10,
552
+ justifyContent: 'center',
553
+ alignItems: 'center',
554
+ // Fixed red so it stays distinct from the (primary-coloured) FAB,
555
+ // with a white ring to separate the two.
556
+ backgroundColor: '#E53935',
557
+ borderWidth: 2,
558
+ borderColor: '#fff',
559
+ },
560
+ newMessageBadgeText: {
561
+ color: '#fff',
562
+ fontWeight: 'bold',
563
+ fontSize: 11,
564
+ },
411
565
  });
@@ -1,6 +1,7 @@
1
1
  import React, {useMemo} from 'react';
2
2
  import {Provider} from 'react-redux';
3
3
  import {KeyboardProvider} from 'react-native-keyboard-controller';
4
+ import {SafeAreaProvider} from 'react-native-safe-area-context';
4
5
  import {store} from '../../roomStore';
5
6
  import {ConfigUser, IConfig, MessageProps} from '../../types/types';
6
7
  import {XmppProvider} from '../../context/xmppProvider';
@@ -8,6 +9,12 @@ import {MessageNotificationProvider} from '../../context/MessageNotificationCont
8
9
  import {ToastProvider} from '../../context/ToastContext';
9
10
  import LoginWrapper from './LoginWrapper';
10
11
  import '../../helpers/storeConsole';
12
+ import {installPromiseRejectionTracker} from '../../utils/installPromiseRejectionTracker';
13
+
14
+ // Mount-time, dev-only — wire a global unhandled-promise-rejection
15
+ // tracker so any future leak surfaces with a real stack trace in Metro
16
+ // logs (bug #4 follow-up). No-op in production.
17
+ installPromiseRejectionTracker();
11
18
 
12
19
  interface ChatWrapperProps {
13
20
  token?: string;
@@ -17,6 +24,7 @@ interface ChatWrapperProps {
17
24
  MainComponentStyles?: React.CSSProperties;
18
25
  CustomMessageComponent?: React.ComponentType<MessageProps>;
19
26
  config?: IConfig;
27
+ isVisible?: boolean;
20
28
  }
21
29
 
22
30
  export const ReduxWrapper: React.FC<ChatWrapperProps> = React.memo(
@@ -27,15 +35,17 @@ export const ReduxWrapper: React.FC<ChatWrapperProps> = React.memo(
27
35
 
28
36
  return (
29
37
  <Provider store={store}>
30
- <KeyboardProvider>
31
- <XmppProvider config={memoizedConfig}>
32
- <ToastProvider>
33
- <MessageNotificationProvider config={memoizedConfig}>
34
- <LoginWrapper config={memoizedConfig} {...props} />
35
- </MessageNotificationProvider>
36
- </ToastProvider>
37
- </XmppProvider>
38
- </KeyboardProvider>
38
+ <SafeAreaProvider>
39
+ <KeyboardProvider>
40
+ <XmppProvider config={memoizedConfig} isVisible={props.isVisible}>
41
+ <ToastProvider>
42
+ <MessageNotificationProvider config={memoizedConfig}>
43
+ <LoginWrapper config={memoizedConfig} {...props} />
44
+ </MessageNotificationProvider>
45
+ </ToastProvider>
46
+ </XmppProvider>
47
+ </KeyboardProvider>
48
+ </SafeAreaProvider>
39
49
  </Provider>
40
50
  );
41
51
  },