@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
@@ -0,0 +1,18 @@
1
+ type KeyboardLayoutArgs = {
2
+ platform: string;
3
+ configuredOffset?: number;
4
+ bottomInset?: number;
5
+ };
6
+
7
+ export const getKeyboardVerticalOffset = ({
8
+ platform,
9
+ configuredOffset = 0,
10
+ bottomInset = 0,
11
+ }: KeyboardLayoutArgs): number =>
12
+ configuredOffset + (platform === 'ios' ? bottomInset : 0);
13
+
14
+ export const getInputDockPaddingBottom = ({
15
+ platform,
16
+ bottomInset = 0,
17
+ }: Omit<KeyboardLayoutArgs, 'configuredOffset'>): number =>
18
+ platform === 'ios' ? bottomInset : 0;
@@ -0,0 +1,47 @@
1
+ import { Dimensions } from 'react-native';
2
+
3
+ // Shared sizing for inline media bubbles (images + video posters).
4
+ // Goal: show the media UNCROPPED, just scaled, preserving aspect ratio —
5
+ // portrait stays portrait, landscape stays landscape.
6
+ const screenW = Dimensions.get('window').width;
7
+
8
+ // Box the media must fit inside. Width adapts to the screen so it never
9
+ // blows past the bubble's max-width on small phones, but caps on tablets.
10
+ export const MEDIA_MAX_W = Math.min(260, Math.round(screenW * 0.64));
11
+ export const MEDIA_MAX_H = 320;
12
+ // Keep thumbnails from collapsing into a sliver (very wide/tall media).
13
+ const MEDIA_MIN_W = 140;
14
+ const MEDIA_MIN_H = 120;
15
+
16
+ export interface MediaDims {
17
+ width: number;
18
+ height: number;
19
+ }
20
+
21
+ // Neutral 4:3 placeholder used before the natural size is known.
22
+ export const defaultMediaDims = (): MediaDims => ({
23
+ width: MEDIA_MAX_W,
24
+ height: Math.round(MEDIA_MAX_W * 0.75),
25
+ });
26
+
27
+ // Fit (natW × natH) inside the box, preserving aspect ratio. Never
28
+ // upscales beyond the source resolution (avoids blur), but bumps very
29
+ // small media up to a sensible minimum so it stays tappable.
30
+ export const fitMediaDimensions = (natW: number, natH: number): MediaDims => {
31
+ if (!natW || !natH) {
32
+ return defaultMediaDims();
33
+ }
34
+ let scale = Math.min(MEDIA_MAX_W / natW, MEDIA_MAX_H / natH);
35
+ if (scale > 1) {
36
+ scale = 1;
37
+ }
38
+ let width = Math.round(natW * scale);
39
+ let height = Math.round(natH * scale);
40
+
41
+ if (width < MEDIA_MIN_W || height < MEDIA_MIN_H) {
42
+ const up = Math.max(MEDIA_MIN_W / width, MEDIA_MIN_H / height);
43
+ width = Math.min(MEDIA_MAX_W, Math.round(width * up));
44
+ height = Math.min(MEDIA_MAX_H, Math.round(height * up));
45
+ }
46
+ return { width, height };
47
+ };
@@ -0,0 +1,135 @@
1
+ // MIME → file extension table for download/preview filename fallbacks.
2
+ // Centralized so FilePreviewModal, MediaMessage, and any future media
3
+ // surface share one source of truth. Keep additions sorted by category.
4
+
5
+ const MIME_TO_EXT: Record<string, string> = {
6
+ // images
7
+ 'image/jpeg': '.jpg',
8
+ 'image/jpg': '.jpg',
9
+ 'image/png': '.png',
10
+ 'image/gif': '.gif',
11
+ 'image/webp': '.webp',
12
+ 'image/heic': '.heic',
13
+ 'image/heif': '.heif',
14
+ 'image/bmp': '.bmp',
15
+ 'image/svg+xml': '.svg',
16
+ 'image/tiff': '.tiff',
17
+
18
+ // video
19
+ 'video/mp4': '.mp4',
20
+ 'video/quicktime': '.mov',
21
+ 'video/x-m4v': '.m4v',
22
+ 'video/webm': '.webm',
23
+ 'video/3gpp': '.3gp',
24
+ 'video/x-matroska': '.mkv',
25
+ 'video/mpeg': '.mpeg',
26
+
27
+ // audio
28
+ 'audio/mpeg': '.mp3',
29
+ 'audio/mp3': '.mp3',
30
+ 'audio/mp4': '.m4a',
31
+ 'audio/x-m4a': '.m4a',
32
+ 'audio/wav': '.wav',
33
+ 'audio/x-wav': '.wav',
34
+ 'audio/aac': '.aac',
35
+ 'audio/ogg': '.ogg',
36
+ 'audio/flac': '.flac',
37
+ 'audio/webm': '.weba',
38
+
39
+ // documents
40
+ 'application/pdf': '.pdf',
41
+ 'application/msword': '.doc',
42
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
43
+ '.docx',
44
+ 'application/vnd.ms-excel': '.xls',
45
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': '.xlsx',
46
+ 'application/vnd.ms-powerpoint': '.ppt',
47
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
48
+ '.pptx',
49
+ 'text/plain': '.txt',
50
+ 'text/csv': '.csv',
51
+ 'text/html': '.html',
52
+ 'application/json': '.json',
53
+ 'application/xml': '.xml',
54
+ 'text/xml': '.xml',
55
+ 'application/rtf': '.rtf',
56
+
57
+ // archives
58
+ 'application/zip': '.zip',
59
+ 'application/x-rar-compressed': '.rar',
60
+ 'application/x-7z-compressed': '.7z',
61
+ 'application/gzip': '.gz',
62
+ 'application/x-tar': '.tar',
63
+ };
64
+
65
+ const VALID_EXT_RE = /\.[a-z0-9]{1,8}$/i;
66
+
67
+ /**
68
+ * Returns the conventional extension (including the leading dot) for a
69
+ * MIME type, or `.bin` as a last-resort fallback so callers never end up
70
+ * with an extension-less filename.
71
+ */
72
+ export function getExtensionForMime(mime: string | undefined | null): string {
73
+ if (!mime) {return '.bin';}
74
+ const normalized = mime.toLowerCase().split(';')[0]!.trim();
75
+ return MIME_TO_EXT[normalized] || '.bin';
76
+ }
77
+
78
+ /**
79
+ * Parses a URL or file path and returns the last segment, URL-decoded,
80
+ * stripped of query/hash. Returns `''` when nothing usable is present.
81
+ */
82
+ export function filenameFromUrl(url: string | undefined | null): string {
83
+ if (!url) {return '';}
84
+ try {
85
+ // Strip query + hash without depending on a real URL parser (some RN
86
+ // URLs from CDNs do not parse cleanly).
87
+ const noQuery = url.split('?')[0]!.split('#')[0]!;
88
+ const last = noQuery.split('/').pop() || '';
89
+ return decodeURIComponent(last);
90
+ } catch {
91
+ return '';
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Ensures `fileName` ends with a valid extension. Strategy:
97
+ * 1. If the input already has an extension, return it unchanged.
98
+ * 2. Otherwise, derive the extension from `mime` and append it.
99
+ * 3. If `mime` is unknown, append `.bin`.
100
+ *
101
+ * Empty/falsy filenames get a `media_<timestamp>` base before the
102
+ * extension is appended so callers always get a usable filename.
103
+ */
104
+ export function ensureFilenameHasExtension(
105
+ fileName: string | undefined | null,
106
+ mime: string | undefined | null
107
+ ): string {
108
+ const base =
109
+ (fileName && fileName.trim()) || `media_${Date.now()}`;
110
+ if (VALID_EXT_RE.test(base)) {return base;}
111
+ return `${base}${getExtensionForMime(mime)}`;
112
+ }
113
+
114
+ /**
115
+ * Best-effort filename derivation for a message bubble:
116
+ * - server-provided `fileName`
117
+ * - server-provided `originalName`
118
+ * - URL pathname
119
+ * - generic `media_<timestamp>` so we never render an empty label
120
+ *
121
+ * Always returns a string with a valid extension.
122
+ */
123
+ export function deriveDisplayFilename(opts: {
124
+ fileName?: string | null;
125
+ originalName?: string | null;
126
+ url?: string | null;
127
+ mime?: string | null;
128
+ }): string {
129
+ const candidate =
130
+ (opts.fileName && opts.fileName.trim()) ||
131
+ (opts.originalName && opts.originalName.trim()) ||
132
+ filenameFromUrl(opts.url) ||
133
+ '';
134
+ return ensureFilenameHasExtension(candidate, opts.mime);
135
+ }
@@ -0,0 +1,6 @@
1
+ export const normalizeRoomJid = (roomJID: string, conference?: string) => {
2
+ if (!roomJID) {return roomJID;}
3
+ if (roomJID.includes('@')) {return roomJID;}
4
+ if (!conference) {return roomJID;}
5
+ return `${roomJID}@${conference}`;
6
+ };
@@ -237,4 +237,83 @@ export const applyResolvedUserToStore = (user?: User | null) => {
237
237
  store.dispatch(setUser(user));
238
238
  };
239
239
 
240
+ /**
241
+ * Re-mints a User with a fresh xmppPassword for the XMPP credentials-
242
+ * provider path (bug #17). Unlike `resolveInitBeforeLoadUser` this
243
+ * ALWAYS hydrates via the chosen auth path even if the cached user
244
+ * already had xmppCredentials — that's the whole point, the old ones
245
+ * are stale.
246
+ *
247
+ * Selection order:
248
+ * 1. `jwtLogin` — re-exchange the JWT via `/users/client`.
249
+ * 2. Anything else (`userLogin` / `customLogin` / store / persisted)
250
+ * — `tryHydrateViaMy` on the current user, which calls
251
+ * `/users/my` and, on 401, refreshes the access token via
252
+ * `/users/login/refresh` and retries.
253
+ *
254
+ * Returns `null` only when nothing usable is available — the caller
255
+ * (xmppProvider) falls back to the cached creds in that case.
256
+ */
257
+ export const refreshUserCredentialsForXmpp = async (
258
+ config?: IConfig,
259
+ signal?: AbortSignal
260
+ ): Promise<User | null> => {
261
+ if (signal?.aborted) {return null;}
262
+
263
+ // Priority 1: jwtLogin — same path as `resolveInitBeforeLoadUser`
264
+ // priority 3. We always re-exchange because the original JWT may
265
+ // itself have expired (and the consumer is expected to swap
266
+ // `config.jwtLogin.token` for a fresh one before then).
267
+ if (config?.jwtLogin?.enabled && config?.jwtLogin?.token) {
268
+ try {
269
+ const fresh = await loginViaJwt(config.jwtLogin.token);
270
+ const normalized = normalizeUserForXmpp(fresh);
271
+ if (normalized && hasXmppCredentials(normalized)) {return normalized;}
272
+ } catch (error) {
273
+ if (signal?.aborted || isAbortError(error)) {return null;}
274
+ // fall through to /users/my path
275
+ }
276
+ }
277
+
278
+ const myEndpoint = config?.initBeforeLoadAuth?.myEndpoint || '/users/my';
279
+
280
+ // Priority 2: redux store user (covers userLogin + customLogin +
281
+ // anything previously persisted into chatSettingStore.user). The
282
+ // user must have at least a refreshToken for the refresh chain to
283
+ // produce something useful.
284
+ const currentUser = store.getState().chatSettingStore.user;
285
+ if (currentUser?.token || currentUser?.refreshToken) {
286
+ const hydrated = await tryHydrateViaMy(
287
+ currentUser as User,
288
+ myEndpoint,
289
+ signal
290
+ ).catch(() => null);
291
+ if (hydrated && hasXmppCredentials(hydrated)) {return hydrated;}
292
+ }
293
+
294
+ // Priority 3: persisted user in AsyncStorage — defensive; the store
295
+ // should already mirror this, but if the redux state got cleared
296
+ // mid-session (logout race) this gives us one last shot.
297
+ try {
298
+ const storedUser = await asyncLocalStorage<User>(
299
+ localStorageConstants.ETHORA_USER
300
+ ).get();
301
+ if (
302
+ storedUser &&
303
+ (storedUser.token || storedUser.refreshToken || storedUser.xmppPassword)
304
+ ) {
305
+ const hydrated = await tryHydrateViaMy(
306
+ storedUser,
307
+ myEndpoint,
308
+ signal
309
+ ).catch(() => null);
310
+ if (hydrated && hasXmppCredentials(hydrated)) {return hydrated;}
311
+ }
312
+ } catch {
313
+ // ignore storage errors
314
+ }
315
+
316
+ return null;
317
+ };
318
+
240
319
  export default resolveInitBeforeLoadUser;
@@ -0,0 +1,95 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import { useDispatch } from 'react-redux';
3
+ import {
4
+ clearVisibleRoom,
5
+ setCurrentRoom,
6
+ setLastViewedTimestamp,
7
+ setVisibleRoom,
8
+ } from '../roomStore/roomsSlice';
9
+ import { store } from '../roomStore';
10
+
11
+ interface UseChatRoomFocusOptions {
12
+ /** The room JID that the consumer's tab/screen is currently showing. */
13
+ roomJID: string | null | undefined;
14
+ /**
15
+ * Whether the chat tab/screen is currently focused. In tab-based
16
+ * navigators where `<ChatRoom>` never unmounts, the SDK can't tell
17
+ * "user is looking at this tab" from "user is on a different tab"
18
+ * — pass this from `useFocusEffect` (React Navigation) or a similar
19
+ * focus signal so the unread counter clears on focus and starts
20
+ * counting again on blur.
21
+ */
22
+ isFocused: boolean;
23
+ }
24
+
25
+ /**
26
+ * Public hook for consumers embedding `<ChatRoom>` inside a tab-based
27
+ * navigator. Mirrors the mount/unmount unread-tracking that ChatRoom
28
+ * does internally — but driven by an explicit focus signal so the
29
+ * unread counter behaves correctly when the chat tab is not focused
30
+ * but the component is still mounted.
31
+ *
32
+ * Without this hook, `useUnread()` will always return 0 for the chat
33
+ * room because the SDK assumes "mounted == active". With this hook,
34
+ * focus marks the room visible (clearing the badge) and blur stamps
35
+ * `lastViewedTimestamp = Date.now()` so future messages count as unread.
36
+ *
37
+ * Usage with React Navigation:
38
+ *
39
+ * ```tsx
40
+ * import { useIsFocused } from '@react-navigation/native';
41
+ *
42
+ * function ChatTab() {
43
+ * const isFocused = useIsFocused();
44
+ * useChatRoomFocus({ roomJID: 'general@conference.host', isFocused });
45
+ * return <Chat config={...} />;
46
+ * }
47
+ * ```
48
+ *
49
+ * Bug #18-adjacent / unread-tracking bug from sdk-bug-tracker.md.
50
+ */
51
+ export const useChatRoomFocus = ({
52
+ roomJID,
53
+ isFocused,
54
+ }: UseChatRoomFocusOptions) => {
55
+ const dispatch = useDispatch();
56
+ const prevRef = useRef<{ roomJID: string | null; isFocused: boolean }>({
57
+ roomJID: null,
58
+ isFocused: false,
59
+ });
60
+
61
+ const leaveRoom = (jid: string) => {
62
+ const timestamp = Date.now();
63
+ dispatch(setLastViewedTimestamp({ chatJID: jid, timestamp }));
64
+ dispatch(clearVisibleRoom());
65
+ const rooms = store.getState().rooms?.rooms;
66
+ (store.getState().chatSettingStore as any)?.client
67
+ ?.flushLastViewedToPrivateStoreStanza(rooms, { visibleRoomJID: jid })
68
+ .catch(() => {});
69
+ };
70
+
71
+ useEffect(() => {
72
+ const prev = prevRef.current;
73
+ if (prev.isFocused && prev.roomJID && (prev.roomJID !== roomJID || !isFocused)) {
74
+ leaveRoom(prev.roomJID);
75
+ }
76
+
77
+ if (roomJID && isFocused) {
78
+ dispatch(setCurrentRoom({ roomJID }));
79
+ dispatch(setVisibleRoom({ roomJID }));
80
+ }
81
+
82
+ prevRef.current = { roomJID: roomJID || null, isFocused };
83
+ }, [roomJID, isFocused, dispatch]);
84
+
85
+ useEffect(() => {
86
+ return () => {
87
+ const prev = prevRef.current;
88
+ if (prev.isFocused && prev.roomJID) {
89
+ leaveRoom(prev.roomJID);
90
+ }
91
+ };
92
+ }, []);
93
+ };
94
+
95
+ export default useChatRoomFocus;
@@ -119,10 +119,8 @@ const useChatWrapperInit = ({
119
119
  user.xmppUsername || user?.defaultWallet?.walletAddress,
120
120
  user?.xmppPassword,
121
121
  config?.xmppSettings
122
- ).then((client) => {
123
- console.log(' Client initialized', client?.status);
124
- return client;
125
- });
122
+ );
123
+ console.log(' Client initialized', newClient?.status);
126
124
 
127
125
  if (roomsList && Object.keys(roomsList).length > 0) {
128
126
  console.log(
@@ -156,27 +154,29 @@ const useChatWrapperInit = ({
156
154
  await newClient.getRoomsStanza();
157
155
  }
158
156
  }
159
- await newClient
160
- .getChatsPrivateStoreRequestStanza()
161
- .then(
162
- async (
163
- roomTimestampObject: any
164
- ) => {
165
- console.log(
166
- 'Got chats private store',
167
- roomTimestampObject?.length || 0
168
- );
169
- updatedChatLastTimestamps(roomTimestampObject, dispatch);
170
- if (!hasSyncedHistoryRef.current) {
171
- console.log('Updating messages till last');
172
- await updateMessagesTillLast(rooms, newClient);
173
- hasSyncedHistoryRef.current = true;
174
- }
175
- setClient(newClient);
176
- setConnectionLost(false);
177
- console.log('Initialization complete');
178
- }
157
+ try {
158
+ const roomTimestampObject: any =
159
+ await newClient.getChatsPrivateStoreRequestStanza();
160
+ console.log(
161
+ 'Got chats private store',
162
+ roomTimestampObject?.length || 0
179
163
  );
164
+ if (roomTimestampObject) {
165
+ updatedChatLastTimestamps(roomTimestampObject, dispatch);
166
+ }
167
+ if (!hasSyncedHistoryRef.current) {
168
+ console.log('Updating messages till last');
169
+ await updateMessagesTillLast(rooms, newClient);
170
+ hasSyncedHistoryRef.current = true;
171
+ }
172
+ setClient(newClient);
173
+ setConnectionLost(false);
174
+ console.log('Initialization complete');
175
+ } catch (err) {
176
+ console.warn('privateStore/timestamps failed', err);
177
+ setClient(newClient);
178
+ setConnectionLost(false);
179
+ }
180
180
 
181
181
  {
182
182
  config?.refreshTokens?.enabled && refresh();
@@ -204,29 +204,34 @@ const useChatWrapperInit = ({
204
204
  }
205
205
  setInited(true);
206
206
  console.log(' Getting chats private store for existing client');
207
- await client
208
- .getChatsPrivateStoreRequestStanza()
209
- .then(
210
- async (
211
- roomTimestampObject: any
212
- ) => {
213
- console.log(
214
- 'Got chats private store for existing client',
215
- roomTimestampObject?.length || 0
216
- );
217
- updatedChatLastTimestamps(roomTimestampObject, dispatch);
218
- if (!hasSyncedHistoryRef.current) {
219
- console.log(
220
- 'Updating messages till last for existing client'
221
- );
222
- await updateMessagesTillLast(rooms, client);
223
- hasSyncedHistoryRef.current = true;
224
- }
225
- setClient(client);
226
- setConnectionLost(false);
227
- console.log(' Initialization complete for existing client');
228
- }
207
+ try {
208
+ const roomTimestampObject: any =
209
+ await client.getChatsPrivateStoreRequestStanza();
210
+ console.log(
211
+ 'Got chats private store for existing client',
212
+ roomTimestampObject?.length || 0
229
213
  );
214
+ if (roomTimestampObject) {
215
+ updatedChatLastTimestamps(roomTimestampObject, dispatch);
216
+ }
217
+ if (!hasSyncedHistoryRef.current) {
218
+ console.log(
219
+ 'Updating messages till last for existing client'
220
+ );
221
+ await updateMessagesTillLast(rooms, client);
222
+ hasSyncedHistoryRef.current = true;
223
+ }
224
+ setClient(client);
225
+ setConnectionLost(false);
226
+ console.log(' Initialization complete for existing client');
227
+ } catch (err) {
228
+ console.warn(
229
+ 'privateStore/timestamps failed (existing client)',
230
+ err
231
+ );
232
+ setClient(client);
233
+ setConnectionLost(false);
234
+ }
230
235
  {
231
236
  config?.refreshTokens?.enabled && refresh();
232
237
  }
@@ -23,10 +23,17 @@ interface MessageEditedEvent {
23
23
  user: any;
24
24
  }
25
25
 
26
+ interface MessageRetryEvent {
27
+ messageId: string;
28
+ roomJID: string;
29
+ messageType: 'text' | 'media';
30
+ }
31
+
26
32
  export interface EventHandlersHook {
27
33
  handleMessageSent: (event: MessageSentEvent) => Promise<void>;
28
34
  handleMessageFailed: (event: MessageFailedEvent) => void;
29
35
  handleMessageEdited: (event: MessageEditedEvent) => void;
36
+ handleMessageRetry: (event: MessageRetryEvent) => void;
30
37
  }
31
38
 
32
39
  export const useEventHandlers = (config?: IConfig): EventHandlersHook => {
@@ -77,5 +84,21 @@ export const useEventHandlers = (config?: IConfig): EventHandlersHook => {
77
84
  [config]
78
85
  );
79
86
 
80
- return { handleMessageSent, handleMessageFailed, handleMessageEdited };
87
+ const handleMessageRetry = useCallback(
88
+ (event: MessageRetryEvent) => {
89
+ try {
90
+ config?.eventHandlers?.onMessageRetry?.(event);
91
+ } catch (error) {
92
+ console.error('Error in message retry handler:', error);
93
+ }
94
+ },
95
+ [config]
96
+ );
97
+
98
+ return {
99
+ handleMessageSent,
100
+ handleMessageFailed,
101
+ handleMessageEdited,
102
+ handleMessageRetry,
103
+ };
81
104
  };
@@ -38,7 +38,8 @@ export const useHeapSender = (client: XmppClient | null) => {
38
38
  !!msg.isReply,
39
39
  msg.showInChannel === 'true',
40
40
  msg.mainMessage || '',
41
- msg.langSource
41
+ msg.langSource,
42
+ msg.id
42
43
  );
43
44
  } else {
44
45
  await client.sendMessage(
@@ -49,7 +49,7 @@ const logoutService = {
49
49
  performLogout: async (): Promise<void> => {
50
50
  // 0. Flush lastViewedTimestamp to the server's private store BEFORE
51
51
  // we close XMPP. Only rooms with no outstanding unread (plus the
52
- // active room) get their entries updated — rooms with unread
52
+ // visible room) get their entries updated — rooms with unread
53
53
  // keep their old marker so the next login still surfaces those
54
54
  // messages as unread. Without this, an active-room view at
55
55
  // logout would still appear unread on next login because we
@@ -58,11 +58,11 @@ const logoutService = {
58
58
  const state = store.getState();
59
59
  const client = (state.chatSettingStore as any)?.client;
60
60
  const rooms = state.rooms?.rooms;
61
- const activeRoomJID = state.rooms?.activeRoomJID || null;
61
+ const visibleRoomJID = state.rooms?.visibleRoomJID || null;
62
62
  if (client?.flushLastViewedToPrivateStoreStanza) {
63
63
  await Promise.race([
64
64
  client.flushLastViewedToPrivateStoreStanza(rooms, {
65
- activeRoomJID,
65
+ visibleRoomJID,
66
66
  onlyIfNoUnread: true,
67
67
  }),
68
68
  new Promise((res) => setTimeout(res, 2000)),
@@ -3,9 +3,13 @@ import { RootState } from '../roomStore';
3
3
 
4
4
  export const useMessageHeapState = () => {
5
5
  const queue = useSelector(
6
- (state: RootState) => state.roomHeapSlice.messageHeap
6
+ (state: RootState) => state.roomHeapSlice?.messageHeap
7
+ );
8
+ const failedMessages = useSelector(
9
+ (state: RootState) => state.roomHeapSlice?.failedMessages
7
10
  );
8
11
  const idSet = new Set(queue?.map((m) => m.id) ?? []);
12
+ const failedIdSet = new Set(Object.keys(failedMessages || {}));
9
13
 
10
- return { queue, idSet };
14
+ return { queue, idSet, failedMessages, failedIdSet };
11
15
  };
@@ -51,7 +51,13 @@ export function usePushNotifications() {
51
51
  };
52
52
 
53
53
  useEffect(() => {
54
- initToken();
54
+ // initToken is async — `initToken()` without `.catch` leaves any
55
+ // rejection unhandled (showed as `Uncaught (in promise, id: X)` red
56
+ // screen in dev). The inner try/catch covers most paths but any
57
+ // synchronous throw before the try-block ran would still leak.
58
+ initToken().catch((err) =>
59
+ console.warn('[usePushNotifications] initToken rejected', err)
60
+ );
55
61
 
56
62
  const unsubscribeForeground = onForegroundMessage((message) => {
57
63
  setNotification(message);
@@ -78,13 +84,20 @@ export function usePushNotifications() {
78
84
  handleNotificationPress(message);
79
85
  });
80
86
 
81
- getInitialNotification().then((message) => {
82
- if (message) {
83
- console.log('[usePushNotifications] Initial notification:', JSON.stringify(message, null, 2));
84
- setNotification(message);
85
- handleNotificationPress(message);
86
- }
87
- });
87
+ getInitialNotification()
88
+ .then((message) => {
89
+ if (message) {
90
+ console.log('[usePushNotifications] Initial notification:', JSON.stringify(message, null, 2));
91
+ setNotification(message);
92
+ handleNotificationPress(message);
93
+ }
94
+ })
95
+ .catch((err) =>
96
+ console.warn(
97
+ '[usePushNotifications] getInitialNotification failed',
98
+ err
99
+ )
100
+ );
88
101
 
89
102
  return () => {
90
103
  unsubscribeForeground();
@@ -12,6 +12,9 @@ export const useRoomState = (roomJID?: string) => {
12
12
  const activeRoomJID = useSelector(
13
13
  (state: RootState) => state.rooms.activeRoomJID
14
14
  );
15
+ const visibleRoomJID = useSelector(
16
+ (state: RootState) => state.rooms.visibleRoomJID
17
+ );
15
18
  const editAction = useSelector((state: RootState) => state.rooms.editAction);
16
19
  const globalLoading = useSelector(
17
20
  (state: RootState) => state.rooms.isLoading
@@ -37,6 +40,7 @@ export const useRoomState = (roomJID?: string) => {
37
40
  room,
38
41
  roomsList,
39
42
  activeRoomJID,
43
+ visibleRoomJID,
40
44
  editAction,
41
45
  globalLoading,
42
46
  loadingText,