@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,42 +1,54 @@
1
1
  import React from 'react';
2
- import { CustomSystemMessage } from '../styled/StyledComponents';
3
- import styled from 'styled-components/native';
2
+ import { View, Text, StyleSheet } from 'react-native';
4
3
 
5
4
  interface SystemMessageProps {
6
5
  messageText?: string;
7
6
  colors?: { primary?: string; secondary?: string };
8
7
  }
9
8
 
10
- export const CustomSystemMessageText = styled.Text<{
11
- primary?: string;
12
- secondary?: string;
13
- }>`
14
- margin: 0;
15
- color: ${(props) => props.primary || '#0052cd'};
16
- border-radius: 118px;
17
- padding: 5px 8px;
18
- display: flex;
19
- align-items: center;
20
- justify-content: center;
21
- font-size: 12px;
22
- line-height: 14px;
23
- font-weight: 600;
24
- background-color: ${(props) => props.secondary || '#e7edf9'};
25
- height: 24px;
26
- white-space: nowrap;
27
- `;
28
-
29
9
  const SystemMessage: React.FC<SystemMessageProps> = ({
30
10
  messageText,
31
11
  colors,
32
12
  }) => {
33
13
  return (
34
- <CustomSystemMessage>
35
- <CustomSystemMessageText {...colors}>
36
- {messageText}
37
- </CustomSystemMessageText>
38
- </CustomSystemMessage>
14
+ <View style={styles.container}>
15
+ {/* maxWidth lives on this View (not the Text) so the Text reliably
16
+ wraps to multiple lines instead of staying on one line and
17
+ getting clipped — a styled.Text with a percentage max-width
18
+ doesn't bound its own line box the way a View does. */}
19
+ <View
20
+ style={[
21
+ styles.bubble,
22
+ { backgroundColor: colors?.secondary || '#e7edf9' },
23
+ ]}
24
+ >
25
+ <Text style={[styles.text, { color: colors?.primary || '#0052cd' }]}>
26
+ {messageText}
27
+ </Text>
28
+ </View>
29
+ </View>
39
30
  );
40
31
  };
41
32
 
42
33
  export default SystemMessage;
34
+
35
+ const styles = StyleSheet.create({
36
+ container: {
37
+ width: '100%',
38
+ alignItems: 'center',
39
+ paddingVertical: 8,
40
+ paddingHorizontal: 16,
41
+ },
42
+ bubble: {
43
+ maxWidth: '85%',
44
+ borderRadius: 12,
45
+ paddingVertical: 6,
46
+ paddingHorizontal: 12,
47
+ },
48
+ text: {
49
+ fontSize: 12,
50
+ lineHeight: 16,
51
+ fontWeight: '600',
52
+ textAlign: 'center',
53
+ },
54
+ });
@@ -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';
@@ -31,6 +30,7 @@ import { parseMessageBody } from '../../helpers/parseMessageBody';
31
30
  import { useMessageHeapState } from '../../hooks/useMessageHeapState';
32
31
  import { DoubleTick } from '../../assets/icons';
33
32
  import { useXmppClient } from '../../context/xmppProvider';
33
+ import { useSendMessage } from '../../hooks/useSendMessage';
34
34
  import { MessageReaction } from './MessageReaction';
35
35
  import { MessageFooter } from '../styled/StyledComponents';
36
36
 
@@ -47,14 +47,19 @@ const CustomMessageBubble = styled.View<{
47
47
  deleted?: boolean;
48
48
  backgroundMessageUser?: string;
49
49
  backgroundMessage?: string;
50
+ isMedia?: boolean;
50
51
  }>`
51
52
  position: relative;
52
53
  max-width: 85%;
53
- min-width: 30%;
54
- padding: 10px;
54
+ min-width: ${({ isMedia }) => (isMedia ? '0' : '30%')};
55
+ align-items: ${({ isMedia, isUser }) =>
56
+ isMedia ? (isUser ? 'flex-end' : 'flex-start') : 'stretch'};
57
+ padding: ${({isMedia}) => isMedia ? '0' : '10'}px;
58
+ padding-bottom: ${({isMedia}) => isMedia ? '6' : '10'}px;
55
59
  margin-right: ${({isUser}) => isUser ? '0' : '10px'};
56
60
  margin-left: ${({isUser}) => isUser ? '10px' : '0'};
57
61
  border-radius: 10px;
62
+ overflow: hidden;
58
63
  border-bottom-left-radius: ${({ isUser }) => (isUser ? '10' : '0')}px;
59
64
  border-bottom-right-radius: ${({ isUser }) => (isUser ? '0' : '10')}px;
60
65
  background-color: ${({
@@ -90,9 +95,12 @@ const CustomMessagePhotoContainer = styled.TouchableOpacity`
90
95
  margin-right: 10px;
91
96
  `;
92
97
 
93
- const CustomUserName = styled.Text<{ color?: string }>`
98
+ const CustomUserName = styled.Text<{ color?: string; media: boolean }>`
94
99
  font-size: 14px;
95
100
  font-weight: 500;
101
+ padding-bottom: 8px;
102
+ padding-left: ${({media}) => media ? '16px': 0};
103
+ padding-top: ${({media}) => media ? '8px': 0};
96
104
  color: ${({ color }) => color || '#333'};
97
105
  `;
98
106
 
@@ -109,11 +117,21 @@ const CustomMessageTimestamp = styled.Text<{
109
117
  align-self: flex-end;
110
118
  `;
111
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
+
112
129
  const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
113
130
  const dispatch = useDispatch();
114
131
  const { client } = useXmppClient();
115
132
  const { config, langSource, user} = useChatSettingState();
116
- const { idSet } = useMessageHeapState();
133
+ const { idSet, failedIdSet } = useMessageHeapState();
134
+ const { retryMessage } = useSendMessage();
117
135
 
118
136
  const [isPressed, setIsPressed] = useState(false);
119
137
 
@@ -122,12 +140,15 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
122
140
  }
123
141
 
124
142
  const [contextMenuPosition, setContextMenuPosition] = useState<{
125
- x: number;
126
- y: number;
143
+ left: number;
144
+ right: number;
145
+ top: number;
146
+ bottom: number;
127
147
  } | null>(null);
128
148
 
129
149
  const timerRef = useRef<NodeJS.Timeout | null>(null);
130
150
  const messageRef = useRef<View>(null);
151
+ const bubbleRef = useRef<View>(null);
131
152
 
132
153
  const handleUserAvatarClick = (user: IUser): void => {
133
154
  dispatch(setActiveModal(MODAL_TYPES.PROFILE));
@@ -214,17 +235,22 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
214
235
  );
215
236
  };
216
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).
217
243
  const handleLongPress = () => {
218
- if (messageRef.current) {
219
- const nodeHandle = findNodeHandle(messageRef.current);
244
+ const target = bubbleRef.current ?? messageRef.current;
245
+ if (target) {
246
+ const nodeHandle = findNodeHandle(target);
220
247
  if (nodeHandle) {
221
- UIManager.measure(nodeHandle, (x, y, width, height, pageX, pageY) => {
222
- const screenHeight = Dimensions.get('window').height;
223
-
224
- const enoughSpaceBelow = screenHeight - pageY - height > 150;
248
+ UIManager.measure(nodeHandle, (_x, _y, width, height, pageX, pageY) => {
225
249
  setContextMenuPosition({
226
- x: pageX,
227
- y: enoughSpaceBelow ? pageY + height : pageY - 150,
250
+ left: pageX,
251
+ right: pageX + width,
252
+ top: pageY,
253
+ bottom: pageY + height,
228
254
  });
229
255
  });
230
256
  }
@@ -236,7 +262,13 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
236
262
  ? parseMessageBody(config?.messageTextFilter.filterFunction(message.body))
237
263
  : parseMessageBody(message.body);
238
264
 
239
- const isPending = idSet.has(message.id) || message?.pending || false;
265
+ const isFailed = failedIdSet.has(message.id);
266
+ const isPending =
267
+ !isFailed && (idSet.has(message.id) || message?.pending || false);
268
+
269
+ const onRetryPress = () => {
270
+ if (isFailed) {retryMessage(message.id);}
271
+ };
240
272
 
241
273
  return (
242
274
  <View>
@@ -269,6 +301,15 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
269
301
  </CustomMessagePhotoContainer>
270
302
  )}
271
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
+ }}
272
313
  // disableInteractions hides the long-press → context menu
273
314
  // (delete / edit / reply / react). Mirrors web's config gate.
274
315
  onLongPress={
@@ -277,13 +318,15 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
277
318
  delayLongPress={500}
278
319
  >
279
320
  <CustomMessageBubble
321
+ {...({ ref: bubbleRef, collapsable: false } as any)}
280
322
  isUser={isUser}
281
323
  deleted={message.isDeleted}
324
+ isMedia={message?.isMediafile === 'true' && !message?.isDeleted}
282
325
  backgroundMessageUser={config?.messageColor?.backgroundMessageUser}
283
326
  backgroundMessage={config?.messageColor?.backgroundMessage}
284
327
  >
285
328
  {!isUser && (
286
- <CustomUserName color={config?.colors?.primary}>
329
+ <CustomUserName color={config?.colors?.primary} media={message?.isMediafile === 'true'}>
287
330
  {message.user.name}
288
331
  </CustomUserName>
289
332
  )}
@@ -302,6 +345,7 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
302
345
  messageText={message.locationPreview}
303
346
  location={message?.location}
304
347
  message={message}
348
+ isUser={isUser}
305
349
  />
306
350
  ) : (
307
351
  <>
@@ -325,16 +369,31 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
325
369
  langSource={langSource}
326
370
  />
327
371
  )}
328
- <CustomMessageTimestamp>
329
- {!config?.disableSentLogic && isUser && isPending && <Text>sending...</Text>}
330
- {new Date(message.date).toLocaleTimeString([], {
331
- hour: '2-digit',
332
- minute: '2-digit',
333
- })}
334
- {!config?.disableSentLogic && isUser && !isPending && (
335
- <DoubleTick />
336
- )}
337
- </CustomMessageTimestamp>
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
+ })}
392
+ </Text>
393
+ {!config?.disableSentLogic && isUser && !isPending && !isFailed && (
394
+ <DoubleTick />
395
+ )}
396
+ </CustomTimestampRow>
338
397
 
339
398
  {message?.reply?.length && message?.reply?.length > 0 ? (
340
399
  <BottomReplyContainer
@@ -401,4 +460,28 @@ const styles = StyleSheet.create({
401
460
  marginTop: 5,
402
461
  alignSelf: 'flex-end',
403
462
  },
463
+ failedText: {
464
+ color: '#E53935',
465
+ fontWeight: '600',
466
+ marginRight: 6,
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
+ },
404
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
+ });