@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
@@ -1,14 +1,30 @@
1
1
  import { useCallback } from 'react';
2
+ import { Platform } from 'react-native';
2
3
  import { v4 as uuidv4 } from 'uuid';
3
4
  import { useXmppClient } from '../context/xmppProvider';
4
- import { useDispatch, useSelector } from 'react-redux';
5
+ import { useDispatch, useSelector, useStore } from 'react-redux';
5
6
  import { addRoomMessage, setEditAction } from '../roomStore/roomsSlice';
6
- import { addMessageToHeap } from '../roomStore/roomHeapSlice';
7
- import { uploadFile } from '../networking/api-requests/auth.api';
8
- import { RootState } from '../roomStore';
7
+ import {
8
+ addMessageToHeap,
9
+ clearMessageFailure,
10
+ markMessageFailed,
11
+ } from '../roomStore/roomHeapSlice';
12
+ import { uploadFileViaFetch } from '../networking/api-requests/auth.api';
13
+ import { enqueueOutboundSend } from '../networking/outboundQueue';
14
+ import { RootState, store } from '../roomStore';
15
+ import { getGlobalXmppClient } from '../utils/clientRegistry';
9
16
  import { useEventHandlers } from './useEventHandlers';
10
17
  import type { IConfig, IMessage } from '../types/types';
11
18
 
19
+ // Watchdog: if a message stays "pending" (no server echo) for this
20
+ // long after the optimistic dispatch, mark it failed so the bubble
21
+ // flips out of the "sending..." state. Covers the silent-failure
22
+ // case (e.g. device offline before the message ever made it to the
23
+ // XMPP socket) — bug #18. Tuned generously so a brief network blip
24
+ // followed by reconnect within 30s still resolves naturally via the
25
+ // server echo.
26
+ const PENDING_WATCHDOG_MS = 30_000;
27
+
12
28
  // Monotonic counter for stanza ids. Bumped on each send within a single
13
29
  // process. Two rapid sends in the same millisecond used to collide on
14
30
  // `send-text-message-${Date.now()}`; that collision made
@@ -19,9 +35,18 @@ let __sendIdSeq = 0;
19
35
  const nextStanzaId = (prefix: string): string =>
20
36
  `${prefix}-${Date.now()}-${(__sendIdSeq = (__sendIdSeq + 1) >>> 0)}`;
21
37
 
38
+ // Hard client-side cap on a single upload. The backend rejects oversized
39
+ // bodies with HTTP 413; SendInput blocks oversized files at pick time,
40
+ // and this mirrors that limit on every send path (including retries).
41
+ const MAX_MEDIA_BYTES = 50 * 1024 * 1024;
42
+
22
43
  export const useSendMessage = (_configOverride?: IConfig) => {
23
44
  const { client } = useXmppClient();
24
45
  const dispatch = useDispatch();
46
+ // useStore gives us imperative getState() access for the watchdog —
47
+ // peek at the message's current pending/failed state from inside a
48
+ // setTimeout callback without re-rendering on every redux change.
49
+ const reduxStore = useStore<RootState>();
25
50
 
26
51
  // Split selectors so each returns a primitive/stable reference — the
27
52
  // single-object selector above caused a "returned a different result
@@ -38,8 +63,13 @@ export const useSendMessage = (_configOverride?: IConfig) => {
38
63
  handleMessageSent,
39
64
  handleMessageFailed,
40
65
  handleMessageEdited,
66
+ handleMessageRetry,
41
67
  } = useEventHandlers(config);
42
68
 
69
+ const failedMessages = useSelector(
70
+ (state: RootState) => state.roomHeapSlice?.failedMessages || {}
71
+ );
72
+
43
73
  const sendMessage = useCallback(
44
74
  async (
45
75
  message: string,
@@ -111,16 +141,85 @@ export const useSendMessage = (_configOverride?: IConfig) => {
111
141
  );
112
142
  }
113
143
 
144
+ // Silent-failure watchdog (bug #18). If no echo lands within
145
+ // PENDING_WATCHDOG_MS, flip the bubble to "failed → tap to
146
+ // retry". We check at fire-time whether the message is still
147
+ // pending and not already marked failed by the catch path.
148
+ const watchdogTimer = setTimeout(() => {
149
+ const state = reduxStore.getState();
150
+ const roomMsgs = state.rooms?.rooms?.[activeRoomJID]?.messages || [];
151
+ const stillPending = roomMsgs.find(
152
+ (m: any) => m.id === optimisticId && m.pending
153
+ );
154
+ const alreadyFailed = state.roomHeapSlice?.failedMessages?.[optimisticId];
155
+ if (stillPending && !alreadyFailed) {
156
+ console.warn(
157
+ `Send watchdog fired — message ${optimisticId} stuck pending > ${PENDING_WATCHDOG_MS}ms; marking failed`
158
+ );
159
+ dispatch(
160
+ markMessageFailed({
161
+ kind: 'text',
162
+ id: optimisticId,
163
+ roomJID: activeRoomJID,
164
+ body: message,
165
+ isReply,
166
+ isChecked,
167
+ mainMessage,
168
+ })
169
+ );
170
+ handleMessageFailed({
171
+ message,
172
+ roomJID: activeRoomJID,
173
+ error: new Error('Send timed out (no server echo)'),
174
+ messageType: 'text',
175
+ });
176
+ }
177
+ }, PENDING_WATCHDOG_MS);
178
+
114
179
  try {
115
- if (!client) {
116
- throw new Error('No XMPP client');
180
+
181
+ const effectiveClient: any =
182
+ client ||
183
+ [
184
+ getGlobalXmppClient(),
185
+ store.getState()?.chatSettingStore?.client,
186
+ ].find((c: any) => c && c.status === 'online');
187
+
188
+ if (!effectiveClient) {
189
+ const anyClient: any =
190
+ client ||
191
+ getGlobalXmppClient() ||
192
+ store.getState()?.chatSettingStore?.client;
193
+ try {
194
+ anyClient?.forceReconnect?.();
195
+ } catch {}
196
+ enqueueOutboundSend({
197
+ optimisticId,
198
+ roomJID: activeRoomJID,
199
+ enqueuedAt: Date.now(),
200
+ send: (c) =>
201
+ c.sendMessage(
202
+ activeRoomJID,
203
+ user.firstName,
204
+ user.lastName,
205
+ '',
206
+ user.walletAddress,
207
+ message,
208
+ '',
209
+ isReply || false,
210
+ isChecked || false,
211
+ mainMessage || '',
212
+ optimisticId
213
+ ),
214
+ });
215
+ return;
117
216
  }
118
217
  // Pass the optimistic id as the stanza id so the server echoes
119
218
  // it back with the same value — the reducer's dedupe lookup
120
219
  // matches and the bubble flips from pending → delivered in-place
121
220
  // (instead of rendering two copies, which is what happens when
122
221
  // ids don't match).
123
- client.sendMessage(
222
+ effectiveClient.sendMessage(
124
223
  activeRoomJID,
125
224
  user.firstName,
126
225
  user.lastName,
@@ -140,6 +239,18 @@ export const useSendMessage = (_configOverride?: IConfig) => {
140
239
  messageType: 'text',
141
240
  });
142
241
  } catch (error) {
242
+ clearTimeout(watchdogTimer);
243
+ dispatch(
244
+ markMessageFailed({
245
+ kind: 'text',
246
+ id: optimisticId,
247
+ roomJID: activeRoomJID,
248
+ body: message,
249
+ isReply,
250
+ isChecked,
251
+ mainMessage,
252
+ })
253
+ );
143
254
  handleMessageFailed({
144
255
  message,
145
256
  roomJID: activeRoomJID,
@@ -148,7 +259,7 @@ export const useSendMessage = (_configOverride?: IConfig) => {
148
259
  });
149
260
  }
150
261
  },
151
- [editAction, client, user, dispatch, handleMessageSent, handleMessageEdited, handleMessageFailed]
262
+ [editAction, client, user, dispatch, handleMessageSent, handleMessageEdited, handleMessageFailed, reduxStore]
152
263
  );
153
264
 
154
265
  const sendMedia = useCallback(
@@ -158,11 +269,42 @@ export const useSendMessage = (_configOverride?: IConfig) => {
158
269
  activeRoomJID: string,
159
270
  isReply: boolean = false,
160
271
  isChecked: boolean = false,
161
- mainMessage: string = ''
272
+ mainMessage: string = '',
273
+ existingId?: string
162
274
  ) => {
163
275
  client?.onCriticalSend?.(activeRoomJID);
164
276
 
165
- const id = `send-media-message:${uuidv4()}`;
277
+ // Defensive 50 MB guard on every send path (SendInput blocks it at
278
+ // pick time, but retries replay the saved payload straight here).
279
+ if (typeof data?.size === 'number' && data.size > MAX_MEDIA_BYTES) {
280
+ console.warn('media exceeds 50MB cap — not uploading', {
281
+ name: data?.name,
282
+ size: data?.size,
283
+ });
284
+ if (existingId) {
285
+ dispatch(
286
+ markMessageFailed({
287
+ kind: 'media',
288
+ id: existingId,
289
+ roomJID: activeRoomJID,
290
+ data,
291
+ type,
292
+ isReply,
293
+ isChecked,
294
+ mainMessage,
295
+ })
296
+ );
297
+ }
298
+ handleMessageFailed({
299
+ message: 'media',
300
+ roomJID: activeRoomJID,
301
+ error: new Error('File exceeds the 50 MB upload limit'),
302
+ messageType: 'media',
303
+ });
304
+ return;
305
+ }
306
+
307
+ const id = existingId || `send-media-message:${uuidv4()}`;
166
308
  const optimisticTimestamp = Date.now();
167
309
  const optimisticDate = new Date(optimisticTimestamp).toISOString();
168
310
  const selfId =
@@ -171,7 +313,7 @@ export const useSendMessage = (_configOverride?: IConfig) => {
171
313
  `${user?.firstName || ''} ${user?.lastName || ''}`.trim() || selfId;
172
314
  const fileSizeStr = data?.size != null ? String(data.size) : '';
173
315
 
174
- if (!config?.disableSentLogic) {
316
+ if (!config?.disableSentLogic && !existingId) {
175
317
  dispatch(
176
318
  addRoomMessage({
177
319
  roomJID: activeRoomJID,
@@ -224,11 +366,94 @@ export const useSendMessage = (_configOverride?: IConfig) => {
224
366
  );
225
367
  }
226
368
 
227
- try {
228
- const mediaData = new FormData();
229
- mediaData.append('files', data);
369
+ // Silent-failure watchdog for media-send (bug #18). Same shape
370
+ // as the text path: if the optimistic bubble never gets the echo
371
+ // (e.g. upload completed but XMPP send is buffered offline), flip
372
+ // to failed → tap to retry.
373
+ const mediaWatchdog = setTimeout(() => {
374
+ const state = reduxStore.getState();
375
+ const roomMsgs = state.rooms?.rooms?.[activeRoomJID]?.messages || [];
376
+ const stillPending = roomMsgs.find(
377
+ (m: any) => m.id === id && m.pending
378
+ );
379
+ const alreadyFailed = state.roomHeapSlice?.failedMessages?.[id];
380
+ if (stillPending && !alreadyFailed) {
381
+ console.warn(
382
+ `Media-send watchdog fired — ${id} stuck pending > ${PENDING_WATCHDOG_MS}ms; marking failed`
383
+ );
384
+ dispatch(
385
+ markMessageFailed({
386
+ kind: 'media',
387
+ id,
388
+ roomJID: activeRoomJID,
389
+ data,
390
+ type,
391
+ isReply,
392
+ isChecked,
393
+ mainMessage,
394
+ })
395
+ );
396
+ handleMessageFailed({
397
+ message: 'media',
398
+ roomJID: activeRoomJID,
399
+ error: new Error('Media send timed out (no server echo)'),
400
+ messageType: 'media',
401
+ });
402
+ }
403
+ }, PENDING_WATCHDOG_MS);
404
+
405
+ // Help RN's FormData polyfill build a proper multipart/file part
406
+ // (instead of serialising the JS object as JSON). On iOS the
407
+ // picker can return ph:// or assets-library:// URIs for some
408
+ // legacy library items — those can't be read as files. Coerce
409
+ // to a file:// scheme when possible so the body is a real blob.
410
+ const fileBlob = {
411
+ uri:
412
+ typeof data?.uri === 'string'
413
+ ? data.uri.replace(/^assets-library:\/\//, 'file://')
414
+ : data?.uri,
415
+ type:
416
+ data?.type ||
417
+ data?.mimeType ||
418
+ type ||
419
+ 'application/octet-stream',
420
+ name: data?.name || data?.fileName || `media_${Date.now()}`,
421
+ };
230
422
 
231
- const response = await uploadFile(mediaData);
423
+ // Mirror the web client: a single multipart POST to /files/ with
424
+ // the file under the plural "files" field. We send it via fetch
425
+ // (not axios) because RN's fetch sets the multipart boundary on the
426
+ // Content-Type header correctly. The previous axios fallback omitted
427
+ // a valid boundary, so the server mis-parsed one file as many and
428
+ // rejected the request with HTTP 413 "TOO_MANY_FILES". The only
429
+ // retry is the documented singular-"file" fallback for deployments
430
+ // that 500 on the plural field (bug #10).
431
+ const tryUpload = async () => {
432
+ const fd = new FormData();
433
+ fd.append('files', fileBlob as any);
434
+ try {
435
+ return await uploadFileViaFetch(fd);
436
+ } catch (err: any) {
437
+ if (err?.response?.status === 500) {
438
+ console.warn(
439
+ 'upload "files" field returned 500 — retrying with "file" (singular)',
440
+ { name: fileBlob.name, type: fileBlob.type }
441
+ );
442
+ const fd2 = new FormData();
443
+ fd2.append('file', fileBlob as any);
444
+ return await uploadFileViaFetch(fd2);
445
+ }
446
+ throw err;
447
+ }
448
+ };
449
+
450
+ try {
451
+ const response = await tryUpload();
452
+ // Upload succeeded — the stanza is now in flight. If the
453
+ // server echo arrives within the watchdog window the bubble
454
+ // flips normally; if not, the watchdog still fires and marks
455
+ // it failed. We don't clear the timer on upload success because
456
+ // the XMPP send could still fail silently.
232
457
 
233
458
  for (const item of response.data.results) {
234
459
  const messagePayload = {
@@ -260,7 +485,34 @@ export const useSendMessage = (_configOverride?: IConfig) => {
260
485
  // Stanza id == placeholder id so the MUC echo's outer
261
486
  // <message id="..."> matches xmppId on the placeholder and
262
487
  // insertMessageWithDelimiter merges in place + flips pending.
263
- client?.sendMediaMessageStanza(activeRoomJID, messagePayload, id);
488
+ const effectiveMediaClient: any =
489
+ client ||
490
+ [
491
+ getGlobalXmppClient(),
492
+ store.getState()?.chatSettingStore?.client,
493
+ ].find((c: any) => c && c.status === 'online');
494
+ if (effectiveMediaClient) {
495
+ effectiveMediaClient.sendMediaMessageStanza(
496
+ activeRoomJID,
497
+ messagePayload,
498
+ id
499
+ );
500
+ } else {
501
+ const anyMediaClient: any =
502
+ client ||
503
+ getGlobalXmppClient() ||
504
+ store.getState()?.chatSettingStore?.client;
505
+ try {
506
+ anyMediaClient?.forceReconnect?.();
507
+ } catch {}
508
+ enqueueOutboundSend({
509
+ optimisticId: id,
510
+ roomJID: activeRoomJID,
511
+ enqueuedAt: Date.now(),
512
+ send: (c) =>
513
+ c.sendMediaMessageStanza(activeRoomJID, messagePayload, id),
514
+ });
515
+ }
264
516
  }
265
517
 
266
518
  await handleMessageSent({
@@ -279,17 +531,43 @@ export const useSendMessage = (_configOverride?: IConfig) => {
279
531
  },
280
532
  });
281
533
  } catch (error: any) {
534
+ // Upload (or stanza send) threw an explicit error. Clear the
535
+ // watchdog so it doesn't double-fire `markMessageFailed`.
536
+ clearTimeout(mediaWatchdog);
282
537
  // Surface the real server payload so we can see why the upload
283
538
  // was rejected (axios collapses the message to "Request failed
284
539
  // with status code N"; the actual reason lives in
285
540
  // `error.response.data`).
541
+ const uriStr = typeof data?.uri === 'string' ? data.uri : '';
542
+ const uriScheme = uriStr.match(/^([a-z]+):/i)?.[1] ?? 'unknown';
286
543
  console.error('upload failed', {
287
544
  status: error?.response?.status,
288
545
  statusText: error?.response?.statusText,
289
546
  serverBody: error?.response?.data,
290
547
  requestUrl: error?.config?.url,
291
548
  axiosMessage: error?.message,
549
+ axiosCode: error?.code,
550
+ axiosCauseMessage: error?.cause?.message,
551
+ uri: uriStr,
552
+ uriScheme,
553
+ mime: fileBlob.type,
554
+ fileSizeBytes: data?.size ?? null,
555
+ fileName: fileBlob.name,
556
+ platformOS: Platform.OS,
557
+ platformVersion: Platform.Version,
292
558
  });
559
+ dispatch(
560
+ markMessageFailed({
561
+ kind: 'media',
562
+ id,
563
+ roomJID: activeRoomJID,
564
+ data,
565
+ type,
566
+ isReply,
567
+ isChecked,
568
+ mainMessage,
569
+ })
570
+ );
293
571
  handleMessageFailed({
294
572
  message: 'media',
295
573
  roomJID: activeRoomJID,
@@ -298,7 +576,44 @@ export const useSendMessage = (_configOverride?: IConfig) => {
298
576
  });
299
577
  }
300
578
  },
301
- [client, config, user, dispatch, handleMessageSent, handleMessageFailed]
579
+ [client, config, user, dispatch, handleMessageSent, handleMessageFailed, reduxStore]
580
+ );
581
+
582
+ // Retry a previously-failed send by replaying the saved payload from
583
+ // the heap. Clears the failure flag first so the bubble flips back to
584
+ // "sending..."; if the retry fails again `markMessageFailed` re-arms
585
+ // it. No-op when the id isn't in the failed map.
586
+ const retryMessage = useCallback(
587
+ async (failedId: string) => {
588
+ const payload = failedMessages[failedId];
589
+ if (!payload) {return;}
590
+ dispatch(clearMessageFailure(failedId));
591
+ handleMessageRetry({
592
+ messageId: failedId,
593
+ roomJID: payload.roomJID,
594
+ messageType: payload.kind,
595
+ });
596
+ if (payload.kind === 'text') {
597
+ await sendMessage(
598
+ payload.body,
599
+ payload.roomJID,
600
+ payload.isReply,
601
+ payload.isChecked,
602
+ payload.mainMessage
603
+ );
604
+ } else {
605
+ await sendMedia(
606
+ payload.data,
607
+ payload.type,
608
+ payload.roomJID,
609
+ payload.isReply,
610
+ payload.isChecked,
611
+ payload.mainMessage,
612
+ failedId
613
+ );
614
+ }
615
+ },
616
+ [failedMessages, dispatch, handleMessageRetry, sendMessage, sendMedia]
302
617
  );
303
618
 
304
619
  // ChatRoom/ThreadWrapper consume this as the "edit branch" of send.
@@ -355,6 +670,7 @@ export const useSendMessage = (_configOverride?: IConfig) => {
355
670
  sendMessage,
356
671
  sendMedia,
357
672
  sendEditMessage,
673
+ retryMessage,
358
674
  isLastMessageFromUserAndProcessing,
359
675
  };
360
676
  };
@@ -1,6 +1,6 @@
1
1
  import { useSyncExternalStore } from 'react';
2
2
  import { IRoom } from '../types/types';
3
- import { RootState, store } from '../roomStore';
3
+ import { store } from '../roomStore';
4
4
 
5
5
  interface UnreadMessagesMap {
6
6
  [roomJid: string]: number;
@@ -13,21 +13,18 @@ interface UnreadMessagesStats {
13
13
  }
14
14
 
15
15
  export const useUnreadMessagesCounter = (): UnreadMessagesStats => {
16
- const subscribe = (callback: () => void) => {
17
- const unsubscribe = store.subscribe(() => {
18
- const state: RootState = store.getState();
19
- const rooms = state.rooms?.rooms;
20
-
21
- if (rooms) {
22
- Object.entries(rooms).forEach(([roomJid, room]: [string, IRoom]) => {
23
- if (room.unreadMessages !== undefined) {
24
- callback();
25
- }
26
- });
27
- }
28
- });
29
- return unsubscribe;
30
- };
16
+ // Notify React on EVERY store change and let useSyncExternalStore's
17
+ // snapshot comparison (Object.is on the `rooms` reference) decide
18
+ // whether to re-render. The previous implementation only fired the
19
+ // callback while iterating rooms that already had `unreadMessages`
20
+ // defined — so when `rooms` became empty (logout, or the last room
21
+ // removed) the loop body never ran, the callback never fired, and the
22
+ // hook never re-rendered: a consumer's badge stayed stuck at its last
23
+ // non-zero count after logout. (It's also cheaper — one callback per
24
+ // change instead of one per unread room. Redux Toolkit keeps the
25
+ // `rooms` slice reference stable across actions that don't touch it,
26
+ // so non-rooms dispatches still don't trigger a re-render.)
27
+ const subscribe = (callback: () => void) => store.subscribe(callback);
31
28
 
32
29
  const rooms = useSyncExternalStore(
33
30
  subscribe,
package/src/main.ts CHANGED
@@ -28,6 +28,7 @@ export { XmppProvider } from './context/xmppProvider';
28
28
 
29
29
  // Hooks that consumers usually need (badge counts, logout, push, etc.).
30
30
  export { useUnread } from './hooks/useUnreadMessagesCounter';
31
+ export { useChatRoomFocus } from './hooks/useChatRoomFocus';
31
32
  export { logoutService } from './hooks/useLogout';
32
33
  export { useQRCodeChat, handleQRChatId } from './hooks/useQRCodeChatHandler';
33
34
  export { resendMessage } from './utils/resendMessage';
@@ -8,8 +8,9 @@ import { User } from '../../types/types';
8
8
  // } from 'firebase/auth';
9
9
  // import { app } from '../../../firebase-config';
10
10
 
11
- import http, { getCurrentAppToken } from '../apiClient';
11
+ import http, { getCurrentAppToken, getCurrentBaseURL } from '../apiClient';
12
12
  import { store } from '../../roomStore';
13
+ import { pushLog } from '../../utils/devLogger';
13
14
 
14
15
  // login functions
15
16
  export async function loginEmail(email: string, password: string) {
@@ -134,7 +135,64 @@ export function uploadFile(formData: FormData) {
134
135
  headers: {
135
136
  Authorization: token,
136
137
  Accept: '*/*',
137
- 'Content-Type': 'multipart/form-data',
138
+ 'Content-Type': null,
138
139
  },
140
+ transformRequest: (data: any, headers: any) => {
141
+ if (headers?.delete) {headers.delete('Content-Type');}
142
+ return data;
143
+ },
144
+ });
145
+ }
146
+
147
+ export async function uploadFileViaFetch(formData: FormData): Promise<{ data: any }> {
148
+ const token = store.getState().chatSettingStore?.user?.token ?? '';
149
+ const baseUrl = getCurrentBaseURL();
150
+ const url = `${baseUrl.replace(/\/$/, '')}/files/`;
151
+
152
+ pushLog('http', `→ POST ${url} (fetch)`, {
153
+ headers: { Authorization: token ? token.slice(0, 16) + '…' : '[empty]', Accept: '*/*' },
154
+ });
155
+
156
+ let res: Response;
157
+ try {
158
+ res = await fetch(url, {
159
+ method: 'POST',
160
+ headers: {
161
+ Authorization: token,
162
+ Accept: '*/*',
163
+ },
164
+ body: formData as any,
165
+ });
166
+ } catch (e: any) {
167
+ pushLog('http', `← ERR fetch /files/`, {
168
+ name: e?.name,
169
+ message: e?.message,
170
+ stack: e?.stack,
171
+ });
172
+ const err: any = new Error(e?.message || 'Network request failed');
173
+ err.code = 'ERR_NETWORK';
174
+ err.cause = e;
175
+ err.config = { url: '/files/' };
176
+ throw err;
177
+ }
178
+
179
+ if (!res.ok) {
180
+ let body: any = null;
181
+ try {
182
+ body = await res.json();
183
+ } catch {
184
+ try { body = await res.text(); } catch { body = null; }
185
+ }
186
+ pushLog('http', `← ${res.status} POST /files/ (fetch)`, body);
187
+ const err: any = new Error(`Upload failed: ${res.status}`);
188
+ err.response = { status: res.status, statusText: res.statusText, data: body };
189
+ err.config = { url: '/files/' };
190
+ throw err;
191
+ }
192
+
193
+ const data = await res.json();
194
+ pushLog('http', `← ${res.status} POST /files/ (fetch)`, {
195
+ resultsCount: Array.isArray(data?.results) ? data.results.length : 'n/a',
139
196
  });
197
+ return { data };
140
198
  }
@@ -171,7 +171,8 @@ http.interceptors.response.use(
171
171
  isRefreshing = true;
172
172
  try {
173
173
  const tokens = await refresh();
174
- console.log('tokens', tokens);
174
+ // (was: console.log('tokens', tokens) — dumped the full response
175
+ // incl. access/refresh/ws tokens on every refresh; removed.)
175
176
  isRefreshing = false;
176
177
  originalRequest.headers.Authorization = tokens.data.token;
177
178
  processQueue(tokens.data.token);