@ethora/chat-component-rn 26.5.7 → 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 (371) hide show
  1. package/CHANGELOG.md +133 -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/ChatRoom.js +64 -38
  9. package/lib/commonjs/components/MainComponents/ChatRoom.js.map +1 -1
  10. package/lib/commonjs/components/MainComponents/ChatWrapper.js +57 -13
  11. package/lib/commonjs/components/MainComponents/ChatWrapper.js.map +1 -1
  12. package/lib/commonjs/components/MainComponents/MediaMessage.js +4 -2
  13. package/lib/commonjs/components/MainComponents/MediaMessage.js.map +1 -1
  14. package/lib/commonjs/components/MainComponents/MessageContainer.js +2 -2
  15. package/lib/commonjs/components/MainComponents/MessageContainer.js.map +1 -1
  16. package/lib/commonjs/components/MainComponents/MessageList.js +165 -28
  17. package/lib/commonjs/components/MainComponents/MessageList.js.map +1 -1
  18. package/lib/commonjs/components/MainComponents/ReduxWrapper.js +17 -8
  19. package/lib/commonjs/components/MainComponents/ReduxWrapper.js.map +1 -1
  20. package/lib/commonjs/components/MainComponents/SystemMessage.js +34 -22
  21. package/lib/commonjs/components/MainComponents/SystemMessage.js.map +1 -1
  22. package/lib/commonjs/components/MessageBubble/Message.js +84 -39
  23. package/lib/commonjs/components/MessageBubble/Message.js.map +1 -1
  24. package/lib/commonjs/components/MessageBubble/MessageInteractions.js +82 -22
  25. package/lib/commonjs/components/MessageBubble/MessageInteractions.js.map +1 -1
  26. package/lib/commonjs/components/Modals/AttachSheet/AttachSheet.js +16 -4
  27. package/lib/commonjs/components/Modals/AttachSheet/AttachSheet.js.map +1 -1
  28. package/lib/commonjs/components/Modals/FilePreviewModal/DocumentViewer.js +104 -0
  29. package/lib/commonjs/components/Modals/FilePreviewModal/DocumentViewer.js.map +1 -0
  30. package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js +140 -24
  31. package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
  32. package/lib/commonjs/components/Modals/FilePreviewModal/PdfView.js +29 -1
  33. package/lib/commonjs/components/Modals/FilePreviewModal/PdfView.js.map +1 -1
  34. package/lib/commonjs/components/Modals/Modal/Modal.js +5 -1
  35. package/lib/commonjs/components/Modals/Modal/Modal.js.map +1 -1
  36. package/lib/commonjs/components/Modals/ModalWrapper/ModalWrapper.js +77 -47
  37. package/lib/commonjs/components/Modals/ModalWrapper/ModalWrapper.js.map +1 -1
  38. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js +3 -3
  39. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  40. package/lib/commonjs/components/Modals/styledModalComponents.js +4 -9
  41. package/lib/commonjs/components/Modals/styledModalComponents.js.map +1 -1
  42. package/lib/commonjs/components/styled/MediaFilePreview.js +47 -28
  43. package/lib/commonjs/components/styled/MediaFilePreview.js.map +1 -1
  44. package/lib/commonjs/components/styled/MessageImage.js +49 -9
  45. package/lib/commonjs/components/styled/MessageImage.js.map +1 -1
  46. package/lib/commonjs/components/styled/NewMessageLabel.js +8 -6
  47. package/lib/commonjs/components/styled/NewMessageLabel.js.map +1 -1
  48. package/lib/commonjs/components/styled/SendInput.js +49 -27
  49. package/lib/commonjs/components/styled/SendInput.js.map +1 -1
  50. package/lib/commonjs/components/styled/StyledInputComponents/MediaComponents.js +14 -4
  51. package/lib/commonjs/components/styled/StyledInputComponents/MediaComponents.js.map +1 -1
  52. package/lib/commonjs/components/styled/StyledInputComponents/StyledInputComponents.js +7 -2
  53. package/lib/commonjs/components/styled/StyledInputComponents/StyledInputComponents.js.map +1 -1
  54. package/lib/commonjs/components/styled/UnsupportedType.js +5 -1
  55. package/lib/commonjs/components/styled/UnsupportedType.js.map +1 -1
  56. package/lib/commonjs/components/styled/VideoMessage.js +60 -14
  57. package/lib/commonjs/components/styled/VideoMessage.js.map +1 -1
  58. package/lib/commonjs/context/MessageNotificationContext.js +7 -7
  59. package/lib/commonjs/context/MessageNotificationContext.js.map +1 -1
  60. package/lib/commonjs/context/xmppProvider.js +244 -32
  61. package/lib/commonjs/context/xmppProvider.js.map +1 -1
  62. package/lib/commonjs/helpers/chatAutoEnterer.js +2 -7
  63. package/lib/commonjs/helpers/chatAutoEnterer.js.map +1 -1
  64. package/lib/commonjs/helpers/historyPreloadScheduler.js +4 -1
  65. package/lib/commonjs/helpers/historyPreloadScheduler.js.map +1 -1
  66. package/lib/commonjs/helpers/insertMessageWithDelimiter.js +15 -5
  67. package/lib/commonjs/helpers/insertMessageWithDelimiter.js.map +1 -1
  68. package/lib/commonjs/helpers/keyboardLayout.js +18 -0
  69. package/lib/commonjs/helpers/keyboardLayout.js.map +1 -0
  70. package/lib/commonjs/helpers/mediaDimensions.js +51 -0
  71. package/lib/commonjs/helpers/mediaDimensions.js.map +1 -0
  72. package/lib/commonjs/helpers/normalizeRoomJid.js +20 -0
  73. package/lib/commonjs/helpers/normalizeRoomJid.js.map +1 -0
  74. package/lib/commonjs/hooks/useChatRoomFocus.js +87 -0
  75. package/lib/commonjs/hooks/useChatRoomFocus.js.map +1 -0
  76. package/lib/commonjs/hooks/useHeapSender.js +1 -1
  77. package/lib/commonjs/hooks/useHeapSender.js.map +1 -1
  78. package/lib/commonjs/hooks/useLogout.js +3 -3
  79. package/lib/commonjs/hooks/useLogout.js.map +1 -1
  80. package/lib/commonjs/hooks/usePushNotifications.js +6 -2
  81. package/lib/commonjs/hooks/usePushNotifications.js.map +1 -1
  82. package/lib/commonjs/hooks/useRoomState.js +2 -0
  83. package/lib/commonjs/hooks/useRoomState.js.map +1 -1
  84. package/lib/commonjs/hooks/useSendMessage.js +201 -10
  85. package/lib/commonjs/hooks/useSendMessage.js.map +1 -1
  86. package/lib/commonjs/hooks/useUnreadMessagesCounter.js +12 -14
  87. package/lib/commonjs/hooks/useUnreadMessagesCounter.js.map +1 -1
  88. package/lib/commonjs/main.js +7 -0
  89. package/lib/commonjs/main.js.map +1 -1
  90. package/lib/commonjs/networking/api-requests/auth.api.js +67 -0
  91. package/lib/commonjs/networking/api-requests/auth.api.js.map +1 -1
  92. package/lib/commonjs/networking/apiClient.js +2 -1
  93. package/lib/commonjs/networking/apiClient.js.map +1 -1
  94. package/lib/commonjs/networking/outboundQueue.js +92 -0
  95. package/lib/commonjs/networking/outboundQueue.js.map +1 -0
  96. package/lib/commonjs/networking/xmpp/createTimeoutPromise.xmpp.js +4 -1
  97. package/lib/commonjs/networking/xmpp/createTimeoutPromise.xmpp.js.map +1 -1
  98. package/lib/commonjs/networking/xmpp/flushLastViewedToPrivateStore.js +7 -8
  99. package/lib/commonjs/networking/xmpp/flushLastViewedToPrivateStore.js.map +1 -1
  100. package/lib/commonjs/networking/xmpp/getRoomsPaged.xmpp.js +26 -3
  101. package/lib/commonjs/networking/xmpp/getRoomsPaged.xmpp.js.map +1 -1
  102. package/lib/commonjs/networking/xmpp/presenceInRoom.xmpp.js +26 -8
  103. package/lib/commonjs/networking/xmpp/presenceInRoom.xmpp.js.map +1 -1
  104. package/lib/commonjs/networking/xmpp/sendTextMessage.xmpp.js +2 -1
  105. package/lib/commonjs/networking/xmpp/sendTextMessage.xmpp.js.map +1 -1
  106. package/lib/commonjs/networking/xmppClient.js +294 -35
  107. package/lib/commonjs/networking/xmppClient.js.map +1 -1
  108. package/lib/commonjs/roomStore/Middleware/unreadMidlleware.js +22 -6
  109. package/lib/commonjs/roomStore/Middleware/unreadMidlleware.js.map +1 -1
  110. package/lib/commonjs/roomStore/roomsSlice.js +157 -23
  111. package/lib/commonjs/roomStore/roomsSlice.js.map +1 -1
  112. package/lib/commonjs/utils/installPromiseRejectionTracker.js +83 -0
  113. package/lib/commonjs/utils/installPromiseRejectionTracker.js.map +1 -0
  114. package/lib/commonjs/utils/resendMessage.js +1 -1
  115. package/lib/commonjs/utils/resendMessage.js.map +1 -1
  116. package/lib/module/assets/icons.js +5 -1
  117. package/lib/module/assets/icons.js.map +1 -1
  118. package/lib/module/components/ContextMenu/ContextMenuComponents.js +11 -5
  119. package/lib/module/components/ContextMenu/ContextMenuComponents.js.map +1 -1
  120. package/lib/module/components/MainComponents/ChatRoom.js +65 -39
  121. package/lib/module/components/MainComponents/ChatRoom.js.map +1 -1
  122. package/lib/module/components/MainComponents/ChatWrapper.js +57 -13
  123. package/lib/module/components/MainComponents/ChatWrapper.js.map +1 -1
  124. package/lib/module/components/MainComponents/MediaMessage.js +4 -2
  125. package/lib/module/components/MainComponents/MediaMessage.js.map +1 -1
  126. package/lib/module/components/MainComponents/MessageContainer.js +2 -2
  127. package/lib/module/components/MainComponents/MessageContainer.js.map +1 -1
  128. package/lib/module/components/MainComponents/MessageList.js +166 -29
  129. package/lib/module/components/MainComponents/MessageList.js.map +1 -1
  130. package/lib/module/components/MainComponents/ReduxWrapper.js +18 -8
  131. package/lib/module/components/MainComponents/ReduxWrapper.js.map +1 -1
  132. package/lib/module/components/MainComponents/SystemMessage.js +33 -21
  133. package/lib/module/components/MainComponents/SystemMessage.js.map +1 -1
  134. package/lib/module/components/MessageBubble/Message.js +85 -40
  135. package/lib/module/components/MessageBubble/Message.js.map +1 -1
  136. package/lib/module/components/MessageBubble/MessageInteractions.js +84 -24
  137. package/lib/module/components/MessageBubble/MessageInteractions.js.map +1 -1
  138. package/lib/module/components/Modals/AttachSheet/AttachSheet.js +15 -3
  139. package/lib/module/components/Modals/AttachSheet/AttachSheet.js.map +1 -1
  140. package/lib/module/components/Modals/FilePreviewModal/DocumentViewer.js +99 -0
  141. package/lib/module/components/Modals/FilePreviewModal/DocumentViewer.js.map +1 -0
  142. package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js +143 -25
  143. package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
  144. package/lib/module/components/Modals/FilePreviewModal/PdfView.js +31 -3
  145. package/lib/module/components/Modals/FilePreviewModal/PdfView.js.map +1 -1
  146. package/lib/module/components/Modals/Modal/Modal.js +5 -1
  147. package/lib/module/components/Modals/Modal/Modal.js.map +1 -1
  148. package/lib/module/components/Modals/ModalWrapper/ModalWrapper.js +79 -49
  149. package/lib/module/components/Modals/ModalWrapper/ModalWrapper.js.map +1 -1
  150. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js +3 -3
  151. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  152. package/lib/module/components/Modals/styledModalComponents.js +4 -9
  153. package/lib/module/components/Modals/styledModalComponents.js.map +1 -1
  154. package/lib/module/components/styled/MediaFilePreview.js +47 -28
  155. package/lib/module/components/styled/MediaFilePreview.js.map +1 -1
  156. package/lib/module/components/styled/MessageImage.js +51 -11
  157. package/lib/module/components/styled/MessageImage.js.map +1 -1
  158. package/lib/module/components/styled/NewMessageLabel.js +8 -6
  159. package/lib/module/components/styled/NewMessageLabel.js.map +1 -1
  160. package/lib/module/components/styled/SendInput.js +50 -28
  161. package/lib/module/components/styled/SendInput.js.map +1 -1
  162. package/lib/module/components/styled/StyledInputComponents/MediaComponents.js +14 -4
  163. package/lib/module/components/styled/StyledInputComponents/MediaComponents.js.map +1 -1
  164. package/lib/module/components/styled/StyledInputComponents/StyledInputComponents.js +7 -2
  165. package/lib/module/components/styled/StyledInputComponents/StyledInputComponents.js.map +1 -1
  166. package/lib/module/components/styled/UnsupportedType.js +5 -1
  167. package/lib/module/components/styled/UnsupportedType.js.map +1 -1
  168. package/lib/module/components/styled/VideoMessage.js +61 -15
  169. package/lib/module/components/styled/VideoMessage.js.map +1 -1
  170. package/lib/module/context/MessageNotificationContext.js +7 -7
  171. package/lib/module/context/MessageNotificationContext.js.map +1 -1
  172. package/lib/module/context/xmppProvider.js +246 -34
  173. package/lib/module/context/xmppProvider.js.map +1 -1
  174. package/lib/module/helpers/chatAutoEnterer.js +2 -7
  175. package/lib/module/helpers/chatAutoEnterer.js.map +1 -1
  176. package/lib/module/helpers/historyPreloadScheduler.js +4 -1
  177. package/lib/module/helpers/historyPreloadScheduler.js.map +1 -1
  178. package/lib/module/helpers/insertMessageWithDelimiter.js +15 -5
  179. package/lib/module/helpers/insertMessageWithDelimiter.js.map +1 -1
  180. package/lib/module/helpers/keyboardLayout.js +12 -0
  181. package/lib/module/helpers/keyboardLayout.js.map +1 -0
  182. package/lib/module/helpers/mediaDimensions.js +46 -0
  183. package/lib/module/helpers/mediaDimensions.js.map +1 -0
  184. package/lib/module/helpers/normalizeRoomJid.js +15 -0
  185. package/lib/module/helpers/normalizeRoomJid.js.map +1 -0
  186. package/lib/module/hooks/useChatRoomFocus.js +82 -0
  187. package/lib/module/hooks/useChatRoomFocus.js.map +1 -0
  188. package/lib/module/hooks/useHeapSender.js +1 -1
  189. package/lib/module/hooks/useHeapSender.js.map +1 -1
  190. package/lib/module/hooks/useLogout.js +3 -3
  191. package/lib/module/hooks/useLogout.js.map +1 -1
  192. package/lib/module/hooks/usePushNotifications.js +6 -2
  193. package/lib/module/hooks/usePushNotifications.js.map +1 -1
  194. package/lib/module/hooks/useRoomState.js +2 -0
  195. package/lib/module/hooks/useRoomState.js.map +1 -1
  196. package/lib/module/hooks/useSendMessage.js +203 -12
  197. package/lib/module/hooks/useSendMessage.js.map +1 -1
  198. package/lib/module/hooks/useUnreadMessagesCounter.js +12 -14
  199. package/lib/module/hooks/useUnreadMessagesCounter.js.map +1 -1
  200. package/lib/module/main.js +1 -0
  201. package/lib/module/main.js.map +1 -1
  202. package/lib/module/networking/api-requests/auth.api.js +67 -1
  203. package/lib/module/networking/api-requests/auth.api.js.map +1 -1
  204. package/lib/module/networking/apiClient.js +2 -1
  205. package/lib/module/networking/apiClient.js.map +1 -1
  206. package/lib/module/networking/outboundQueue.js +84 -0
  207. package/lib/module/networking/outboundQueue.js.map +1 -0
  208. package/lib/module/networking/xmpp/createTimeoutPromise.xmpp.js +4 -1
  209. package/lib/module/networking/xmpp/createTimeoutPromise.xmpp.js.map +1 -1
  210. package/lib/module/networking/xmpp/flushLastViewedToPrivateStore.js +7 -8
  211. package/lib/module/networking/xmpp/flushLastViewedToPrivateStore.js.map +1 -1
  212. package/lib/module/networking/xmpp/getRoomsPaged.xmpp.js +26 -3
  213. package/lib/module/networking/xmpp/getRoomsPaged.xmpp.js.map +1 -1
  214. package/lib/module/networking/xmpp/presenceInRoom.xmpp.js +26 -8
  215. package/lib/module/networking/xmpp/presenceInRoom.xmpp.js.map +1 -1
  216. package/lib/module/networking/xmpp/sendTextMessage.xmpp.js +2 -1
  217. package/lib/module/networking/xmpp/sendTextMessage.xmpp.js.map +1 -1
  218. package/lib/module/networking/xmppClient.js +294 -35
  219. package/lib/module/networking/xmppClient.js.map +1 -1
  220. package/lib/module/roomStore/Middleware/unreadMidlleware.js +22 -6
  221. package/lib/module/roomStore/Middleware/unreadMidlleware.js.map +1 -1
  222. package/lib/module/roomStore/roomsSlice.js +154 -22
  223. package/lib/module/roomStore/roomsSlice.js.map +1 -1
  224. package/lib/module/utils/installPromiseRejectionTracker.js +78 -0
  225. package/lib/module/utils/installPromiseRejectionTracker.js.map +1 -0
  226. package/lib/module/utils/resendMessage.js +1 -1
  227. package/lib/module/utils/resendMessage.js.map +1 -1
  228. package/lib/typescript/assets/icons.d.ts +8 -4
  229. package/lib/typescript/assets/icons.d.ts.map +1 -1
  230. package/lib/typescript/components/ContextMenu/ContextMenuComponents.d.ts.map +1 -1
  231. package/lib/typescript/components/MainComponents/ChatRoom.d.ts.map +1 -1
  232. package/lib/typescript/components/MainComponents/ChatWrapper.d.ts.map +1 -1
  233. package/lib/typescript/components/MainComponents/MediaMessage.d.ts +1 -0
  234. package/lib/typescript/components/MainComponents/MediaMessage.d.ts.map +1 -1
  235. package/lib/typescript/components/MainComponents/MessageContainer.d.ts.map +1 -1
  236. package/lib/typescript/components/MainComponents/MessageList.d.ts.map +1 -1
  237. package/lib/typescript/components/MainComponents/ReduxWrapper.d.ts +1 -0
  238. package/lib/typescript/components/MainComponents/ReduxWrapper.d.ts.map +1 -1
  239. package/lib/typescript/components/MainComponents/SystemMessage.d.ts +0 -7
  240. package/lib/typescript/components/MainComponents/SystemMessage.d.ts.map +1 -1
  241. package/lib/typescript/components/MessageBubble/Message.d.ts.map +1 -1
  242. package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts +4 -2
  243. package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts.map +1 -1
  244. package/lib/typescript/components/Modals/AttachSheet/AttachSheet.d.ts.map +1 -1
  245. package/lib/typescript/components/Modals/FilePreviewModal/DocumentViewer.d.ts +29 -0
  246. package/lib/typescript/components/Modals/FilePreviewModal/DocumentViewer.d.ts.map +1 -0
  247. package/lib/typescript/components/Modals/FilePreviewModal/FilePreviewModal.d.ts +0 -1
  248. package/lib/typescript/components/Modals/FilePreviewModal/FilePreviewModal.d.ts.map +1 -1
  249. package/lib/typescript/components/Modals/FilePreviewModal/PdfView.d.ts.map +1 -1
  250. package/lib/typescript/components/Modals/Modal/Modal.d.ts.map +1 -1
  251. package/lib/typescript/components/Modals/ModalWrapper/ModalWrapper.d.ts.map +1 -1
  252. package/lib/typescript/components/Modals/UserProfileModal/UserProfileModal.d.ts.map +1 -1
  253. package/lib/typescript/components/Modals/styledModalComponents.d.ts.map +1 -1
  254. package/lib/typescript/components/styled/MediaFilePreview.d.ts.map +1 -1
  255. package/lib/typescript/components/styled/MessageImage.d.ts.map +1 -1
  256. package/lib/typescript/components/styled/NewMessageLabel.d.ts +1 -5
  257. package/lib/typescript/components/styled/NewMessageLabel.d.ts.map +1 -1
  258. package/lib/typescript/components/styled/SendInput.d.ts.map +1 -1
  259. package/lib/typescript/components/styled/StyledInputComponents/MediaComponents.d.ts +9 -1
  260. package/lib/typescript/components/styled/StyledInputComponents/MediaComponents.d.ts.map +1 -1
  261. package/lib/typescript/components/styled/StyledInputComponents/StyledInputComponents.d.ts.map +1 -1
  262. package/lib/typescript/components/styled/UnsupportedType.d.ts +1 -0
  263. package/lib/typescript/components/styled/UnsupportedType.d.ts.map +1 -1
  264. package/lib/typescript/components/styled/VideoMessage.d.ts.map +1 -1
  265. package/lib/typescript/context/xmppProvider.d.ts +1 -0
  266. package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
  267. package/lib/typescript/helpers/chatAutoEnterer.d.ts.map +1 -1
  268. package/lib/typescript/helpers/historyPreloadScheduler.d.ts.map +1 -1
  269. package/lib/typescript/helpers/insertMessageWithDelimiter.d.ts.map +1 -1
  270. package/lib/typescript/helpers/keyboardLayout.d.ts +9 -0
  271. package/lib/typescript/helpers/keyboardLayout.d.ts.map +1 -0
  272. package/lib/typescript/helpers/mediaDimensions.d.ts +9 -0
  273. package/lib/typescript/helpers/mediaDimensions.d.ts.map +1 -0
  274. package/lib/typescript/helpers/normalizeRoomJid.d.ts +2 -0
  275. package/lib/typescript/helpers/normalizeRoomJid.d.ts.map +1 -0
  276. package/lib/typescript/hooks/useChatRoomFocus.d.ts +42 -0
  277. package/lib/typescript/hooks/useChatRoomFocus.d.ts.map +1 -0
  278. package/lib/typescript/hooks/useHeapSender.d.ts.map +1 -1
  279. package/lib/typescript/hooks/usePushNotifications.d.ts.map +1 -1
  280. package/lib/typescript/hooks/useRoomState.d.ts +1 -0
  281. package/lib/typescript/hooks/useRoomState.d.ts.map +1 -1
  282. package/lib/typescript/hooks/useSendMessage.d.ts.map +1 -1
  283. package/lib/typescript/hooks/useUnreadMessagesCounter.d.ts.map +1 -1
  284. package/lib/typescript/main.d.ts +1 -0
  285. package/lib/typescript/main.d.ts.map +1 -1
  286. package/lib/typescript/networking/api-requests/auth.api.d.ts +3 -0
  287. package/lib/typescript/networking/api-requests/auth.api.d.ts.map +1 -1
  288. package/lib/typescript/networking/apiClient.d.ts.map +1 -1
  289. package/lib/typescript/networking/outboundQueue.d.ts +57 -0
  290. package/lib/typescript/networking/outboundQueue.d.ts.map +1 -0
  291. package/lib/typescript/networking/xmpp/createTimeoutPromise.xmpp.d.ts.map +1 -1
  292. package/lib/typescript/networking/xmpp/flushLastViewedToPrivateStore.d.ts +1 -1
  293. package/lib/typescript/networking/xmpp/flushLastViewedToPrivateStore.d.ts.map +1 -1
  294. package/lib/typescript/networking/xmpp/getRoomsPaged.xmpp.d.ts +1 -1
  295. package/lib/typescript/networking/xmpp/getRoomsPaged.xmpp.d.ts.map +1 -1
  296. package/lib/typescript/networking/xmpp/presenceInRoom.xmpp.d.ts.map +1 -1
  297. package/lib/typescript/networking/xmpp/sendTextMessage.xmpp.d.ts.map +1 -1
  298. package/lib/typescript/networking/xmppClient.d.ts +33 -4
  299. package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
  300. package/lib/typescript/roomStore/Middleware/unreadMidlleware.d.ts.map +1 -1
  301. package/lib/typescript/roomStore/roomsSlice.d.ts +8 -1
  302. package/lib/typescript/roomStore/roomsSlice.d.ts.map +1 -1
  303. package/lib/typescript/types/models/media.model.d.ts +1 -0
  304. package/lib/typescript/types/models/media.model.d.ts.map +1 -1
  305. package/lib/typescript/types/models/xmpp.model.d.ts +1 -1
  306. package/lib/typescript/types/models/xmpp.model.d.ts.map +1 -1
  307. package/lib/typescript/types/types.d.ts +4 -0
  308. package/lib/typescript/types/types.d.ts.map +1 -1
  309. package/lib/typescript/utils/installPromiseRejectionTracker.d.ts +20 -0
  310. package/lib/typescript/utils/installPromiseRejectionTracker.d.ts.map +1 -0
  311. package/lib/typescript/utils/resendMessage.d.ts.map +1 -1
  312. package/package.json +18 -2
  313. package/src/assets/icons.tsx +23 -14
  314. package/src/components/ContextMenu/ContextMenuComponents.tsx +10 -5
  315. package/src/components/MainComponents/ChatRoom.tsx +81 -66
  316. package/src/components/MainComponents/ChatWrapper.tsx +99 -21
  317. package/src/components/MainComponents/MediaMessage.tsx +3 -0
  318. package/src/components/MainComponents/MessageContainer.tsx +4 -2
  319. package/src/components/MainComponents/MessageList.tsx +175 -28
  320. package/src/components/MainComponents/ReduxWrapper.tsx +19 -9
  321. package/src/components/MainComponents/SystemMessage.tsx +38 -26
  322. package/src/components/MessageBubble/Message.tsx +91 -62
  323. package/src/components/MessageBubble/MessageInteractions.tsx +98 -29
  324. package/src/components/Modals/AttachSheet/AttachSheet.tsx +17 -3
  325. package/src/components/Modals/FilePreviewModal/DocumentViewer.tsx +93 -0
  326. package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +152 -25
  327. package/src/components/Modals/FilePreviewModal/PdfView.tsx +32 -4
  328. package/src/components/Modals/Modal/Modal.tsx +4 -1
  329. package/src/components/Modals/ModalWrapper/ModalWrapper.tsx +70 -58
  330. package/src/components/Modals/UserProfileModal/UserProfileModal.tsx +19 -15
  331. package/src/components/Modals/styledModalComponents.tsx +4 -9
  332. package/src/components/styled/MediaFilePreview.tsx +46 -26
  333. package/src/components/styled/MessageImage.tsx +58 -13
  334. package/src/components/styled/NewMessageLabel.tsx +8 -5
  335. package/src/components/styled/SendInput.tsx +64 -29
  336. package/src/components/styled/StyledInputComponents/MediaComponents.tsx +15 -5
  337. package/src/components/styled/StyledInputComponents/StyledInputComponents.tsx +7 -2
  338. package/src/components/styled/UnsupportedType.tsx +7 -1
  339. package/src/components/styled/VideoMessage.tsx +59 -11
  340. package/src/context/MessageNotificationContext.tsx +8 -8
  341. package/src/context/xmppProvider.tsx +247 -30
  342. package/src/helpers/chatAutoEnterer.ts +2 -8
  343. package/src/helpers/historyPreloadScheduler.ts +4 -1
  344. package/src/helpers/insertMessageWithDelimiter.ts +22 -15
  345. package/src/helpers/keyboardLayout.ts +18 -0
  346. package/src/helpers/mediaDimensions.ts +47 -0
  347. package/src/helpers/normalizeRoomJid.ts +6 -0
  348. package/src/hooks/useChatRoomFocus.ts +95 -0
  349. package/src/hooks/useHeapSender.tsx +2 -1
  350. package/src/hooks/useLogout.tsx +3 -3
  351. package/src/hooks/usePushNotifications.ts +21 -8
  352. package/src/hooks/useRoomState.tsx +4 -0
  353. package/src/hooks/useSendMessage.tsx +258 -13
  354. package/src/hooks/useUnreadMessagesCounter.ts +13 -16
  355. package/src/main.ts +1 -0
  356. package/src/networking/api-requests/auth.api.ts +55 -1
  357. package/src/networking/apiClient.ts +2 -1
  358. package/src/networking/outboundQueue.ts +98 -0
  359. package/src/networking/xmpp/createTimeoutPromise.xmpp.ts +5 -1
  360. package/src/networking/xmpp/flushLastViewedToPrivateStore.ts +11 -13
  361. package/src/networking/xmpp/getRoomsPaged.xmpp.ts +27 -3
  362. package/src/networking/xmpp/presenceInRoom.xmpp.ts +24 -9
  363. package/src/networking/xmpp/sendTextMessage.xmpp.ts +2 -1
  364. package/src/networking/xmppClient.ts +317 -43
  365. package/src/roomStore/Middleware/unreadMidlleware.tsx +23 -5
  366. package/src/roomStore/roomsSlice.ts +177 -27
  367. package/src/types/models/media.model.ts +3 -0
  368. package/src/types/models/xmpp.model.ts +2 -1
  369. package/src/types/types.ts +15 -0
  370. package/src/utils/installPromiseRejectionTracker.ts +91 -0
  371. package/src/utils/resendMessage.ts +2 -1
@@ -5,7 +5,6 @@ import {
5
5
  StyleSheet,
6
6
  findNodeHandle,
7
7
  UIManager,
8
- Dimensions,
9
8
  Text,
10
9
  } from 'react-native';
11
10
  import { useDispatch, useSelector } from 'react-redux';
@@ -52,7 +51,9 @@ const CustomMessageBubble = styled.View<{
52
51
  }>`
53
52
  position: relative;
54
53
  max-width: 85%;
55
- min-width: 30%;
54
+ min-width: ${({ isMedia }) => (isMedia ? '0' : '30%')};
55
+ align-items: ${({ isMedia, isUser }) =>
56
+ isMedia ? (isUser ? 'flex-end' : 'flex-start') : 'stretch'};
56
57
  padding: ${({isMedia}) => isMedia ? '0' : '10'}px;
57
58
  padding-bottom: ${({isMedia}) => isMedia ? '6' : '10'}px;
58
59
  margin-right: ${({isUser}) => isUser ? '0' : '10px'};
@@ -94,9 +95,12 @@ const CustomMessagePhotoContainer = styled.TouchableOpacity`
94
95
  margin-right: 10px;
95
96
  `;
96
97
 
97
- const CustomUserName = styled.Text<{ color?: string }>`
98
+ const CustomUserName = styled.Text<{ color?: string; media: boolean }>`
98
99
  font-size: 14px;
99
100
  font-weight: 500;
101
+ padding-bottom: 8px;
102
+ padding-left: ${({media}) => media ? '16px': 0};
103
+ padding-top: ${({media}) => media ? '8px': 0};
100
104
  color: ${({ color }) => color || '#333'};
101
105
  `;
102
106
 
@@ -113,6 +117,15 @@ const CustomMessageTimestamp = styled.Text<{
113
117
  align-self: flex-end;
114
118
  `;
115
119
 
120
+ const CustomTimestampRow = styled.View<{media: boolean}>`
121
+ flex-direction: row;
122
+ align-items: center;
123
+ align-self: flex-end;
124
+ margin-top: 4;
125
+ gap: 4;
126
+ padding-right: ${({media}) => media ? '10px': 0};
127
+ `;
128
+
116
129
  const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
117
130
  const dispatch = useDispatch();
118
131
  const { client } = useXmppClient();
@@ -127,12 +140,15 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
127
140
  }
128
141
 
129
142
  const [contextMenuPosition, setContextMenuPosition] = useState<{
130
- x: number;
131
- y: number;
143
+ left: number;
144
+ right: number;
145
+ top: number;
146
+ bottom: number;
132
147
  } | null>(null);
133
148
 
134
149
  const timerRef = useRef<NodeJS.Timeout | null>(null);
135
150
  const messageRef = useRef<View>(null);
151
+ const bubbleRef = useRef<View>(null);
136
152
 
137
153
  const handleUserAvatarClick = (user: IUser): void => {
138
154
  dispatch(setActiveModal(MODAL_TYPES.PROFILE));
@@ -219,45 +235,23 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
219
235
  );
220
236
  };
221
237
 
238
+ // Long-press → capture the bubble's on-screen bounding box. The actual
239
+ // menu placement (above / below, clamped to the viewport) is done in
240
+ // MessageInteractions, which measures the rendered menu height via
241
+ // onLayout — more accurate than estimating it here, and it's what keeps
242
+ // the menu adjacent to the message instead of floating far above (#13).
222
243
  const handleLongPress = () => {
223
- if (messageRef.current) {
224
- const nodeHandle = findNodeHandle(messageRef.current);
244
+ const target = bubbleRef.current ?? messageRef.current;
245
+ if (target) {
246
+ const nodeHandle = findNodeHandle(target);
225
247
  if (nodeHandle) {
226
- UIManager.measure(nodeHandle, (_x, _y, _width, height, pageX, pageY) => {
227
- const screenHeight = Dimensions.get('window').height;
228
- // MessageInteractions is ~5 rows (delete/edit/reply/react/copy)
229
- // at ~48px each + padding, plus emoji bar on top. 280 covers
230
- // both even with a couple of long labels. The legacy 150
231
- // figure assumed a much shorter menu and stranded the popup
232
- // off-screen on the last message of the room.
233
- const MENU_HEIGHT = 280;
234
- // Treat the consumer-supplied keyboardVerticalOffset as a
235
- // proxy for tab bar + bottom safe-area when present; falls
236
- // back to a reasonable default so the menu doesn't bump into
237
- // the nav bar on phones without one in config.
238
- const bottomReserve =
239
- (config?.keyboardVerticalOffset ?? 0) + 24;
240
- const topReserve = 16;
241
-
242
- const spaceBelow = screenHeight - (pageY + height) - bottomReserve;
243
- const spaceAbove = pageY - topReserve;
244
-
245
- let menuY: number;
246
- if (spaceBelow >= MENU_HEIGHT) {
247
- menuY = pageY + height;
248
- } else if (spaceAbove >= MENU_HEIGHT) {
249
- menuY = pageY - MENU_HEIGHT;
250
- } else {
251
- // Neither side has room (e.g. very small phone, large
252
- // menu) — clamp to the visible region so at least the
253
- // top of the menu is reachable.
254
- menuY = Math.max(
255
- topReserve,
256
- screenHeight - MENU_HEIGHT - bottomReserve
257
- );
258
- }
259
-
260
- setContextMenuPosition({ x: pageX, y: menuY });
248
+ UIManager.measure(nodeHandle, (_x, _y, width, height, pageX, pageY) => {
249
+ setContextMenuPosition({
250
+ left: pageX,
251
+ right: pageX + width,
252
+ top: pageY,
253
+ bottom: pageY + height,
254
+ });
261
255
  });
262
256
  }
263
257
  }
@@ -307,6 +301,15 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
307
301
  </CustomMessagePhotoContainer>
308
302
  )}
309
303
  <Pressable
304
+ // The Pressable fills the row's content area, so the bubble
305
+ // inside must be aligned to the sender's side — otherwise it
306
+ // hugs the left and own media bubbles sit with a right-side
307
+ // gap (text bubbles only looked right because they're narrow).
308
+ // flex-end for own, flex-start for others.
309
+ style={{
310
+ flex: 1,
311
+ alignItems: isUser ? 'flex-end' : 'flex-start',
312
+ }}
310
313
  // disableInteractions hides the long-press → context menu
311
314
  // (delete / edit / reply / react). Mirrors web's config gate.
312
315
  onLongPress={
@@ -315,6 +318,7 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
315
318
  delayLongPress={500}
316
319
  >
317
320
  <CustomMessageBubble
321
+ {...({ ref: bubbleRef, collapsable: false } as any)}
318
322
  isUser={isUser}
319
323
  deleted={message.isDeleted}
320
324
  isMedia={message?.isMediafile === 'true' && !message?.isDeleted}
@@ -322,7 +326,7 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
322
326
  backgroundMessage={config?.messageColor?.backgroundMessage}
323
327
  >
324
328
  {!isUser && (
325
- <CustomUserName color={config?.colors?.primary}>
329
+ <CustomUserName color={config?.colors?.primary} media={message?.isMediafile === 'true'}>
326
330
  {message.user.name}
327
331
  </CustomUserName>
328
332
  )}
@@ -341,6 +345,7 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
341
345
  messageText={message.locationPreview}
342
346
  location={message?.location}
343
347
  message={message}
348
+ isUser={isUser}
344
349
  />
345
350
  ) : (
346
351
  <>
@@ -364,26 +369,31 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
364
369
  langSource={langSource}
365
370
  />
366
371
  )}
367
- <CustomMessageTimestamp>
368
- {!config?.disableSentLogic && isUser && isPending && <Text>sending...</Text>}
369
- {!config?.disableSentLogic && isUser && isFailed && (
370
- <Text
371
- onPress={onRetryPress}
372
- style={styles.failedText}
373
- accessibilityRole="button"
374
- accessibilityLabel="Retry sending message"
375
- >
376
- ! Failed — tap to retry
372
+ {/* <View style={styles.timestampRow}> */}
373
+ <CustomTimestampRow media={message?.isMediafile === 'true'}>
374
+ {!config?.disableSentLogic && isUser && isPending && (
375
+ <Text style={styles.timestampText}>sending...</Text>
376
+ )}
377
+ {!config?.disableSentLogic && isUser && isFailed && (
378
+ <Text
379
+ onPress={onRetryPress}
380
+ style={styles.failedText}
381
+ accessibilityRole="button"
382
+ accessibilityLabel="Retry sending message"
383
+ >
384
+ ! Failed — tap to retry
385
+ </Text>
386
+ )}
387
+ <Text style={styles.timestampText}>
388
+ {new Date(message.date).toLocaleTimeString([], {
389
+ hour: '2-digit',
390
+ minute: '2-digit',
391
+ })}
377
392
  </Text>
378
- )}
379
- {new Date(message.date).toLocaleTimeString([], {
380
- hour: '2-digit',
381
- minute: '2-digit',
382
- })}
383
- {!config?.disableSentLogic && isUser && !isPending && !isFailed && (
384
- <DoubleTick />
385
- )}
386
- </CustomMessageTimestamp>
393
+ {!config?.disableSentLogic && isUser && !isPending && !isFailed && (
394
+ <DoubleTick />
395
+ )}
396
+ </CustomTimestampRow>
387
397
 
388
398
  {message?.reply?.length && message?.reply?.length > 0 ? (
389
399
  <BottomReplyContainer
@@ -455,4 +465,23 @@ const styles = StyleSheet.create({
455
465
  fontWeight: '600',
456
466
  marginRight: 6,
457
467
  },
468
+ // Row keeps the time text and the DoubleTick SVG on the same line.
469
+ // The time Text gets an explicit lineHeight equal to the 16px tick so
470
+ // both occupy the same-height box; with alignItems center they line up
471
+ // exactly. Without the lineHeight the Text's natural line box was
472
+ // shorter than the icon, so the centred tick floated above the digits.
473
+ timestampRow: {
474
+ flexDirection: 'row',
475
+ alignItems: 'center',
476
+ alignSelf: 'flex-end',
477
+ marginTop: 4,
478
+ gap: 4,
479
+ },
480
+ timestampText: {
481
+ fontSize: 12,
482
+ lineHeight: 16,
483
+ color: '#999',
484
+ includeFontPadding: false,
485
+ textAlignVertical: 'center',
486
+ },
458
487
  });
@@ -1,4 +1,4 @@
1
- import React, { useMemo } from 'react';
1
+ import React, { useMemo, useState } from 'react';
2
2
  import { Delimeter, MenuItem } from '../ContextMenu/ContextMenuComponents';
3
3
  import { useSelector } from 'react-redux';
4
4
  import { RootState } from '../../roomStore';
@@ -13,6 +13,8 @@ import {
13
13
  StyleSheet,
14
14
  View,
15
15
  Pressable,
16
+ Dimensions,
17
+ type LayoutChangeEvent,
16
18
  } from 'react-native';
17
19
  import * as Clipboard from 'expo-clipboard';
18
20
  import { useToast } from '../../context/ToastContext';
@@ -21,7 +23,12 @@ interface MessageInteractionsProps {
21
23
  isReply?: boolean;
22
24
  isUser?: boolean;
23
25
  message: IMessage;
24
- position: { x: number; y: number } | null;
26
+ position: {
27
+ left: number;
28
+ right: number;
29
+ top: number;
30
+ bottom: number;
31
+ } | null;
25
32
  closeMenu: () => void;
26
33
  handleReplyMessage: () => void;
27
34
  handleDeleteMessage: () => void;
@@ -45,6 +52,19 @@ const MessageInteractions: React.FC<MessageInteractionsProps> = ({
45
52
  (state: RootState) => state.chatSettingStore.config
46
53
  );
47
54
 
55
+ const [menuSize, setMenuSize] = useState({ width: 0, height: 0 });
56
+
57
+ const handleMenuLayout = (e: LayoutChangeEvent) => {
58
+ const { width, height } = e.nativeEvent.layout;
59
+ if (
60
+ width &&
61
+ height &&
62
+ (width !== menuSize.width || height !== menuSize.height)
63
+ ) {
64
+ setMenuSize({ width, height });
65
+ }
66
+ };
67
+
48
68
  const handleCopyMessage = async (text: string) => {
49
69
  try {
50
70
  await Clipboard.setStringAsync(text);
@@ -71,20 +91,44 @@ const MessageInteractions: React.FC<MessageInteractionsProps> = ({
71
91
  };
72
92
 
73
93
  const memoPosition = useMemo(() => {
74
- if (position) {
75
- if (isUser) {
76
- return {
77
- top: position.y,
78
- right: 10,
79
- };
80
- }
81
-
82
- return {
83
- top: position.y,
84
- left: 10,
85
- };
94
+ if (!position) {
95
+ return undefined;
96
+ }
97
+
98
+ if (!menuSize.width || !menuSize.height) {
99
+ return { top: position.bottom, left: position.left, opacity: 0 };
100
+ }
101
+
102
+ const { width: screenWidth, height: screenHeight } =
103
+ Dimensions.get('window');
104
+ const bottomReserve = (config?.keyboardVerticalOffset ?? 0) + 24;
105
+ const topReserve = 16;
106
+ const sideMargin = 8;
107
+
108
+ const spaceBelow = screenHeight - position.bottom - bottomReserve;
109
+ const spaceAbove = position.top - topReserve;
110
+
111
+ let top: number;
112
+ if (spaceBelow >= menuSize.height) {
113
+ top = position.bottom;
114
+ } else if (spaceAbove >= menuSize.height) {
115
+ top = position.top - menuSize.height;
116
+ } else {
117
+ top = Math.max(topReserve, screenHeight - menuSize.height - bottomReserve);
86
118
  }
87
- }, [position, isUser]);
119
+
120
+ const leftMargin = position.left;
121
+ const rightMargin = screenWidth - position.right;
122
+ const rightAligned = rightMargin < leftMargin;
123
+
124
+ let left = rightAligned ? position.right - menuSize.width : position.left;
125
+ left = Math.max(
126
+ sideMargin,
127
+ Math.min(left, screenWidth - menuSize.width - sideMargin)
128
+ );
129
+
130
+ return { top, left };
131
+ }, [position, menuSize, config?.keyboardVerticalOffset]);
88
132
 
89
133
  if (config?.disableInteractions) {return null;}
90
134
 
@@ -98,21 +142,32 @@ const MessageInteractions: React.FC<MessageInteractionsProps> = ({
98
142
  {!message.isDeleted && (
99
143
  <View style={styles.overlayFill}>
100
144
  <Pressable style={StyleSheet.absoluteFill} onPress={closeMenu} />
101
- <View style={[styles.contextMenu, memoPosition]}>
145
+ <View
146
+ style={[styles.contextMenu, memoPosition]}
147
+ onLayout={handleMenuLayout}
148
+ >
102
149
  <MenuItem onPress={() => handleCopyMessage(message.body!)}>
103
- <Text>{MESSAGE_INTERACTIONS.COPY}</Text>
150
+ <Text style={styles.menuText}>{MESSAGE_INTERACTIONS.COPY}</Text>
104
151
  <MESSAGE_INTERACTIONS_ICONS.COPY />
105
152
  </MenuItem>
106
153
  {isUser && (
107
154
  <>
108
- <Delimeter />
109
- <MenuItem onPress={handleEditMessage}>
110
- <Text>{MESSAGE_INTERACTIONS.EDIT}</Text>
111
- <MESSAGE_INTERACTIONS_ICONS.EDIT />
112
- </MenuItem>
155
+ {/* Edit only for non-media messages (upstream guard) +
156
+ consistent label styling (menuText). */}
157
+ {message?.isMediafile !== 'true' && (
158
+ <>
159
+ <Delimeter />
160
+ <MenuItem onPress={handleEditMessage}>
161
+ <Text style={styles.menuText}>
162
+ {MESSAGE_INTERACTIONS.EDIT}
163
+ </Text>
164
+ <MESSAGE_INTERACTIONS_ICONS.EDIT />
165
+ </MenuItem>
166
+ </>
167
+ )}
113
168
  <Delimeter />
114
169
  <MenuItem onPress={handleDeleteMessage}>
115
- <Text>{MESSAGE_INTERACTIONS.DELETE}</Text>
170
+ <Text style={styles.menuText}>{MESSAGE_INTERACTIONS.DELETE}</Text>
116
171
  <MESSAGE_INTERACTIONS_ICONS.DELETE />
117
172
  </MenuItem>
118
173
  </>
@@ -135,14 +190,28 @@ const styles = StyleSheet.create({
135
190
  contextMenu: {
136
191
  position: 'absolute',
137
192
  backgroundColor: 'white',
138
- paddingVertical: 8,
139
- paddingHorizontal: 16,
140
- borderRadius: 8,
193
+ paddingVertical: 4,
194
+ paddingHorizontal: 8,
195
+ borderRadius: 12,
141
196
  shadowColor: '#000',
142
197
  shadowOffset: { width: 0, height: 2 },
143
- shadowOpacity: 0.25,
144
- shadowRadius: 4,
145
- elevation: 5,
198
+ shadowOpacity: 0.18,
199
+ shadowRadius: 8,
200
+ elevation: 6,
146
201
  minWidth: 180,
202
+ // Clip the children to the rounded corners (the hairline dividers
203
+ // run full width; without this they'd poke past the rounding on
204
+ // Android).
205
+ overflow: 'hidden',
206
+ },
207
+ // Explicit label styling so rows are the same height on both platforms
208
+ // — Android's default includeFontPadding made the menu rows taller and
209
+ // misaligned vs iOS. marginRight keeps the label off the trailing icon.
210
+ menuText: {
211
+ fontSize: 16,
212
+ color: '#141414',
213
+ includeFontPadding: false,
214
+ textAlignVertical: 'center',
215
+ marginRight: 16,
147
216
  },
148
217
  });
@@ -1,6 +1,6 @@
1
1
  /** @format */
2
2
 
3
- import React from 'react';
3
+ import React, { useRef } from 'react';
4
4
  import {
5
5
  Modal,
6
6
  Platform,
@@ -28,9 +28,22 @@ const AttachSheet: React.FC<AttachSheetProps> = ({
28
28
  onDocument,
29
29
  primaryColor = '#0052CD',
30
30
  }) => {
31
+ const pendingRef = useRef<(() => void) | null>(null);
32
+
33
+ const runPending = () => {
34
+ const fn = pendingRef.current;
35
+ pendingRef.current = null;
36
+ fn?.();
37
+ };
38
+
31
39
  const trigger = (handler: () => void) => () => {
32
- onClose();
33
- setTimeout(handler, 150);
40
+ if (Platform.OS === 'ios') {
41
+ pendingRef.current = handler;
42
+ onClose();
43
+ } else {
44
+ onClose();
45
+ setTimeout(handler, 150);
46
+ }
34
47
  };
35
48
 
36
49
  const rows: {
@@ -65,6 +78,7 @@ const AttachSheet: React.FC<AttachSheetProps> = ({
65
78
  visible={visible}
66
79
  animationType="slide"
67
80
  onRequestClose={onClose}
81
+ onDismiss={runPending}
68
82
  >
69
83
  <TouchableOpacity
70
84
  style={styles.backdrop}
@@ -0,0 +1,93 @@
1
+ import React, { useState } from 'react';
2
+ import { ActivityIndicator, StyleSheet, Text, View } from 'react-native';
3
+ import { WebView } from 'react-native-webview';
4
+
5
+ interface DocumentViewerProps {
6
+ /** Public CDN URL of the doc. Must be reachable from Google's servers. */
7
+ url: string;
8
+ /** Optional display filename; used for the error fallback only. */
9
+ fileName?: string;
10
+ }
11
+
12
+ /**
13
+ * Inline preview for office documents (DOC / DOCX / XLS / XLSX / PPT /
14
+ * PPTX / TXT / RTF / CSV) and other non-PDF formats supported by
15
+ * Google's free gview rendering service.
16
+ *
17
+ * Strategy: embed `https://docs.google.com/gview?url=<encoded>&embedded=true`
18
+ * inside a WebView. Google fetches the doc from its public URL and
19
+ * renders it to a viewable iframe — no client-side dependency on
20
+ * native office libraries, no extra peer dep. Works for any backend
21
+ * whose file URLs are publicly reachable (which Ethora's `/files/`
22
+ * already are — they're CDN URLs returned in `item.location`).
23
+ *
24
+ * Limitations (documented for the integrator):
25
+ * - Requires public URL — won't work for auth-gated downloads.
26
+ * - First load can take 2-5s on slow networks.
27
+ * - Read-only — no editing, no print/share buttons.
28
+ *
29
+ * For PDFs use the existing PdfViewer (downloads then renders locally).
30
+ */
31
+ const DocumentViewer: React.FC<DocumentViewerProps> = ({ url, fileName }) => {
32
+ const [loaded, setLoaded] = useState(false);
33
+ const [errored, setErrored] = useState(false);
34
+
35
+ const viewerUrl = `https://docs.google.com/gview?url=${encodeURIComponent(
36
+ url
37
+ )}&embedded=true`;
38
+
39
+ if (errored) {
40
+ return (
41
+ <View style={styles.center}>
42
+ <Text style={styles.errorTitle}>
43
+ {fileName || 'Document'}
44
+ </Text>
45
+ <Text style={styles.errorBody}>
46
+ Couldn't render an inline preview. Tap the save icon above to
47
+ download the file.
48
+ </Text>
49
+ </View>
50
+ );
51
+ }
52
+
53
+ return (
54
+ <View style={styles.container}>
55
+ <WebView
56
+ source={{ uri: viewerUrl }}
57
+ style={styles.webview}
58
+ originWhitelist={['*']}
59
+ startInLoadingState
60
+ onLoadEnd={() => setLoaded(true)}
61
+ onError={() => setErrored(true)}
62
+ onHttpError={() => setErrored(true)}
63
+ />
64
+ {!loaded && (
65
+ <View style={styles.loadingOverlay} pointerEvents="none">
66
+ <ActivityIndicator size="large" />
67
+ </View>
68
+ )}
69
+ </View>
70
+ );
71
+ };
72
+
73
+ export default DocumentViewer;
74
+
75
+ const styles = StyleSheet.create({
76
+ container: { flex: 1, width: '100%', backgroundColor: '#fff' },
77
+ webview: { flex: 1, backgroundColor: '#fff' },
78
+ loadingOverlay: {
79
+ ...StyleSheet.absoluteFillObject,
80
+ justifyContent: 'center',
81
+ alignItems: 'center',
82
+ backgroundColor: '#ffffff',
83
+ },
84
+ center: {
85
+ flex: 1,
86
+ alignItems: 'center',
87
+ justifyContent: 'center',
88
+ padding: 24,
89
+ gap: 8,
90
+ },
91
+ errorTitle: { fontSize: 16, fontWeight: '600' },
92
+ errorBody: { color: '#666', textAlign: 'center' },
93
+ });