@ethora/chat-component-rn 26.5.7 → 26.5.11

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 (417) hide show
  1. package/CHANGELOG.md +191 -0
  2. package/README.md +95 -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 +31 -16
  9. package/lib/commonjs/components/MainComponents/ChatHeader.js.map +1 -1
  10. package/lib/commonjs/components/MainComponents/ChatRoom.js +78 -38
  11. package/lib/commonjs/components/MainComponents/ChatRoom.js.map +1 -1
  12. package/lib/commonjs/components/MainComponents/ChatWrapper.js +74 -27
  13. package/lib/commonjs/components/MainComponents/ChatWrapper.js.map +1 -1
  14. package/lib/commonjs/components/MainComponents/MediaMessage.js +16 -11
  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 +165 -28
  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/InteractionsOverlay.js +119 -0
  25. package/lib/commonjs/components/MessageBubble/InteractionsOverlay.js.map +1 -0
  26. package/lib/commonjs/components/MessageBubble/Message.js +122 -41
  27. package/lib/commonjs/components/MessageBubble/Message.js.map +1 -1
  28. package/lib/commonjs/components/MessageBubble/MessageInteractions.js +132 -44
  29. package/lib/commonjs/components/MessageBubble/MessageInteractions.js.map +1 -1
  30. package/lib/commonjs/components/Modals/AttachSheet/AttachSheet.js +155 -52
  31. package/lib/commonjs/components/Modals/AttachSheet/AttachSheet.js.map +1 -1
  32. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js +20 -5
  33. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  34. package/lib/commonjs/components/Modals/FilePreviewModal/DocumentViewer.js +104 -0
  35. package/lib/commonjs/components/Modals/FilePreviewModal/DocumentViewer.js.map +1 -0
  36. package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js +144 -24
  37. package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
  38. package/lib/commonjs/components/Modals/FilePreviewModal/PdfView.js +29 -1
  39. package/lib/commonjs/components/Modals/FilePreviewModal/PdfView.js.map +1 -1
  40. package/lib/commonjs/components/Modals/Modal/Modal.js +5 -1
  41. package/lib/commonjs/components/Modals/Modal/Modal.js.map +1 -1
  42. package/lib/commonjs/components/Modals/ModalHeaderComponent.js +10 -0
  43. package/lib/commonjs/components/Modals/ModalHeaderComponent.js.map +1 -1
  44. package/lib/commonjs/components/Modals/ModalWrapper/ModalWrapper.js +77 -47
  45. package/lib/commonjs/components/Modals/ModalWrapper/ModalWrapper.js.map +1 -1
  46. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js +3 -3
  47. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  48. package/lib/commonjs/components/Modals/styledModalComponents.js +17 -13
  49. package/lib/commonjs/components/Modals/styledModalComponents.js.map +1 -1
  50. package/lib/commonjs/components/styled/MediaFilePreview.js +47 -28
  51. package/lib/commonjs/components/styled/MediaFilePreview.js.map +1 -1
  52. package/lib/commonjs/components/styled/MessageImage.js +49 -9
  53. package/lib/commonjs/components/styled/MessageImage.js.map +1 -1
  54. package/lib/commonjs/components/styled/NewMessageLabel.js +8 -6
  55. package/lib/commonjs/components/styled/NewMessageLabel.js.map +1 -1
  56. package/lib/commonjs/components/styled/SendInput.js +49 -27
  57. package/lib/commonjs/components/styled/SendInput.js.map +1 -1
  58. package/lib/commonjs/components/styled/StyledComponents.js +6 -3
  59. package/lib/commonjs/components/styled/StyledComponents.js.map +1 -1
  60. package/lib/commonjs/components/styled/StyledInputComponents/MediaComponents.js +14 -4
  61. package/lib/commonjs/components/styled/StyledInputComponents/MediaComponents.js.map +1 -1
  62. package/lib/commonjs/components/styled/StyledInputComponents/StyledInputComponents.js +7 -2
  63. package/lib/commonjs/components/styled/StyledInputComponents/StyledInputComponents.js.map +1 -1
  64. package/lib/commonjs/components/styled/UnsupportedType.js +5 -1
  65. package/lib/commonjs/components/styled/UnsupportedType.js.map +1 -1
  66. package/lib/commonjs/components/styled/VideoMessage.js +81 -16
  67. package/lib/commonjs/components/styled/VideoMessage.js.map +1 -1
  68. package/lib/commonjs/context/MessageNotificationContext.js +7 -7
  69. package/lib/commonjs/context/MessageNotificationContext.js.map +1 -1
  70. package/lib/commonjs/context/xmppProvider.js +244 -32
  71. package/lib/commonjs/context/xmppProvider.js.map +1 -1
  72. package/lib/commonjs/helpers/chatAutoEnterer.js +2 -7
  73. package/lib/commonjs/helpers/chatAutoEnterer.js.map +1 -1
  74. package/lib/commonjs/helpers/historyPreloadScheduler.js +4 -1
  75. package/lib/commonjs/helpers/historyPreloadScheduler.js.map +1 -1
  76. package/lib/commonjs/helpers/insertMessageWithDelimiter.js +15 -5
  77. package/lib/commonjs/helpers/insertMessageWithDelimiter.js.map +1 -1
  78. package/lib/commonjs/helpers/keyboardLayout.js +18 -0
  79. package/lib/commonjs/helpers/keyboardLayout.js.map +1 -0
  80. package/lib/commonjs/helpers/mediaDimensions.js +51 -0
  81. package/lib/commonjs/helpers/mediaDimensions.js.map +1 -0
  82. package/lib/commonjs/helpers/mimeToExtension.js +56 -0
  83. package/lib/commonjs/helpers/mimeToExtension.js.map +1 -1
  84. package/lib/commonjs/helpers/normalizeRoomJid.js +20 -0
  85. package/lib/commonjs/helpers/normalizeRoomJid.js.map +1 -0
  86. package/lib/commonjs/hooks/useChatRoomFocus.js +87 -0
  87. package/lib/commonjs/hooks/useChatRoomFocus.js.map +1 -0
  88. package/lib/commonjs/hooks/useHeapSender.js +1 -1
  89. package/lib/commonjs/hooks/useHeapSender.js.map +1 -1
  90. package/lib/commonjs/hooks/useLogout.js +36 -11
  91. package/lib/commonjs/hooks/useLogout.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 +201 -10
  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 +67 -0
  103. package/lib/commonjs/networking/api-requests/auth.api.js.map +1 -1
  104. package/lib/commonjs/networking/api-requests/rooms.api.js +29 -4
  105. package/lib/commonjs/networking/api-requests/rooms.api.js.map +1 -1
  106. package/lib/commonjs/networking/apiClient.js +2 -1
  107. package/lib/commonjs/networking/apiClient.js.map +1 -1
  108. package/lib/commonjs/networking/outboundQueue.js +92 -0
  109. package/lib/commonjs/networking/outboundQueue.js.map +1 -0
  110. package/lib/commonjs/networking/xmpp/createTimeoutPromise.xmpp.js +4 -1
  111. package/lib/commonjs/networking/xmpp/createTimeoutPromise.xmpp.js.map +1 -1
  112. package/lib/commonjs/networking/xmpp/flushLastViewedToPrivateStore.js +7 -8
  113. package/lib/commonjs/networking/xmpp/flushLastViewedToPrivateStore.js.map +1 -1
  114. package/lib/commonjs/networking/xmpp/getRoomsPaged.xmpp.js +26 -3
  115. package/lib/commonjs/networking/xmpp/getRoomsPaged.xmpp.js.map +1 -1
  116. package/lib/commonjs/networking/xmpp/presenceInRoom.xmpp.js +26 -8
  117. package/lib/commonjs/networking/xmpp/presenceInRoom.xmpp.js.map +1 -1
  118. package/lib/commonjs/networking/xmpp/sendTextMessage.xmpp.js +2 -1
  119. package/lib/commonjs/networking/xmpp/sendTextMessage.xmpp.js.map +1 -1
  120. package/lib/commonjs/networking/xmppClient.js +294 -35
  121. package/lib/commonjs/networking/xmppClient.js.map +1 -1
  122. package/lib/commonjs/roomStore/Middleware/unreadMidlleware.js +22 -6
  123. package/lib/commonjs/roomStore/Middleware/unreadMidlleware.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 +31 -16
  135. package/lib/module/components/MainComponents/ChatHeader.js.map +1 -1
  136. package/lib/module/components/MainComponents/ChatRoom.js +80 -40
  137. package/lib/module/components/MainComponents/ChatRoom.js.map +1 -1
  138. package/lib/module/components/MainComponents/ChatWrapper.js +74 -27
  139. package/lib/module/components/MainComponents/ChatWrapper.js.map +1 -1
  140. package/lib/module/components/MainComponents/MediaMessage.js +17 -12
  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 +166 -29
  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/InteractionsOverlay.js +112 -0
  151. package/lib/module/components/MessageBubble/InteractionsOverlay.js.map +1 -0
  152. package/lib/module/components/MessageBubble/Message.js +123 -42
  153. package/lib/module/components/MessageBubble/Message.js.map +1 -1
  154. package/lib/module/components/MessageBubble/MessageInteractions.js +134 -46
  155. package/lib/module/components/MessageBubble/MessageInteractions.js.map +1 -1
  156. package/lib/module/components/Modals/AttachSheet/AttachSheet.js +155 -52
  157. package/lib/module/components/Modals/AttachSheet/AttachSheet.js.map +1 -1
  158. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js +20 -5
  159. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  160. package/lib/module/components/Modals/FilePreviewModal/DocumentViewer.js +99 -0
  161. package/lib/module/components/Modals/FilePreviewModal/DocumentViewer.js.map +1 -0
  162. package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js +148 -26
  163. package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
  164. package/lib/module/components/Modals/FilePreviewModal/PdfView.js +31 -3
  165. package/lib/module/components/Modals/FilePreviewModal/PdfView.js.map +1 -1
  166. package/lib/module/components/Modals/Modal/Modal.js +5 -1
  167. package/lib/module/components/Modals/Modal/Modal.js.map +1 -1
  168. package/lib/module/components/Modals/ModalHeaderComponent.js +10 -0
  169. package/lib/module/components/Modals/ModalHeaderComponent.js.map +1 -1
  170. package/lib/module/components/Modals/ModalWrapper/ModalWrapper.js +79 -49
  171. package/lib/module/components/Modals/ModalWrapper/ModalWrapper.js.map +1 -1
  172. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js +3 -3
  173. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  174. package/lib/module/components/Modals/styledModalComponents.js +17 -13
  175. package/lib/module/components/Modals/styledModalComponents.js.map +1 -1
  176. package/lib/module/components/styled/MediaFilePreview.js +47 -28
  177. package/lib/module/components/styled/MediaFilePreview.js.map +1 -1
  178. package/lib/module/components/styled/MessageImage.js +51 -11
  179. package/lib/module/components/styled/MessageImage.js.map +1 -1
  180. package/lib/module/components/styled/NewMessageLabel.js +8 -6
  181. package/lib/module/components/styled/NewMessageLabel.js.map +1 -1
  182. package/lib/module/components/styled/SendInput.js +50 -28
  183. package/lib/module/components/styled/SendInput.js.map +1 -1
  184. package/lib/module/components/styled/StyledComponents.js +6 -3
  185. package/lib/module/components/styled/StyledComponents.js.map +1 -1
  186. package/lib/module/components/styled/StyledInputComponents/MediaComponents.js +14 -4
  187. package/lib/module/components/styled/StyledInputComponents/MediaComponents.js.map +1 -1
  188. package/lib/module/components/styled/StyledInputComponents/StyledInputComponents.js +7 -2
  189. package/lib/module/components/styled/StyledInputComponents/StyledInputComponents.js.map +1 -1
  190. package/lib/module/components/styled/UnsupportedType.js +5 -1
  191. package/lib/module/components/styled/UnsupportedType.js.map +1 -1
  192. package/lib/module/components/styled/VideoMessage.js +82 -17
  193. package/lib/module/components/styled/VideoMessage.js.map +1 -1
  194. package/lib/module/context/MessageNotificationContext.js +7 -7
  195. package/lib/module/context/MessageNotificationContext.js.map +1 -1
  196. package/lib/module/context/xmppProvider.js +246 -34
  197. package/lib/module/context/xmppProvider.js.map +1 -1
  198. package/lib/module/helpers/chatAutoEnterer.js +2 -7
  199. package/lib/module/helpers/chatAutoEnterer.js.map +1 -1
  200. package/lib/module/helpers/historyPreloadScheduler.js +4 -1
  201. package/lib/module/helpers/historyPreloadScheduler.js.map +1 -1
  202. package/lib/module/helpers/insertMessageWithDelimiter.js +15 -5
  203. package/lib/module/helpers/insertMessageWithDelimiter.js.map +1 -1
  204. package/lib/module/helpers/keyboardLayout.js +12 -0
  205. package/lib/module/helpers/keyboardLayout.js.map +1 -0
  206. package/lib/module/helpers/mediaDimensions.js +46 -0
  207. package/lib/module/helpers/mediaDimensions.js.map +1 -0
  208. package/lib/module/helpers/mimeToExtension.js +55 -0
  209. package/lib/module/helpers/mimeToExtension.js.map +1 -1
  210. package/lib/module/helpers/normalizeRoomJid.js +15 -0
  211. package/lib/module/helpers/normalizeRoomJid.js.map +1 -0
  212. package/lib/module/hooks/useChatRoomFocus.js +82 -0
  213. package/lib/module/hooks/useChatRoomFocus.js.map +1 -0
  214. package/lib/module/hooks/useHeapSender.js +1 -1
  215. package/lib/module/hooks/useHeapSender.js.map +1 -1
  216. package/lib/module/hooks/useLogout.js +36 -11
  217. package/lib/module/hooks/useLogout.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 +203 -12
  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 +67 -1
  229. package/lib/module/networking/api-requests/auth.api.js.map +1 -1
  230. package/lib/module/networking/api-requests/rooms.api.js +29 -4
  231. package/lib/module/networking/api-requests/rooms.api.js.map +1 -1
  232. package/lib/module/networking/apiClient.js +2 -1
  233. package/lib/module/networking/apiClient.js.map +1 -1
  234. package/lib/module/networking/outboundQueue.js +84 -0
  235. package/lib/module/networking/outboundQueue.js.map +1 -0
  236. package/lib/module/networking/xmpp/createTimeoutPromise.xmpp.js +4 -1
  237. package/lib/module/networking/xmpp/createTimeoutPromise.xmpp.js.map +1 -1
  238. package/lib/module/networking/xmpp/flushLastViewedToPrivateStore.js +7 -8
  239. package/lib/module/networking/xmpp/flushLastViewedToPrivateStore.js.map +1 -1
  240. package/lib/module/networking/xmpp/getRoomsPaged.xmpp.js +26 -3
  241. package/lib/module/networking/xmpp/getRoomsPaged.xmpp.js.map +1 -1
  242. package/lib/module/networking/xmpp/presenceInRoom.xmpp.js +26 -8
  243. package/lib/module/networking/xmpp/presenceInRoom.xmpp.js.map +1 -1
  244. package/lib/module/networking/xmpp/sendTextMessage.xmpp.js +2 -1
  245. package/lib/module/networking/xmpp/sendTextMessage.xmpp.js.map +1 -1
  246. package/lib/module/networking/xmppClient.js +294 -35
  247. package/lib/module/networking/xmppClient.js.map +1 -1
  248. package/lib/module/roomStore/Middleware/unreadMidlleware.js +22 -6
  249. package/lib/module/roomStore/Middleware/unreadMidlleware.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/InteractionsOverlay.d.ts +13 -0
  271. package/lib/typescript/components/MessageBubble/InteractionsOverlay.d.ts.map +1 -0
  272. package/lib/typescript/components/MessageBubble/Message.d.ts.map +1 -1
  273. package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts +4 -2
  274. package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts.map +1 -1
  275. package/lib/typescript/components/Modals/AttachSheet/AttachSheet.d.ts.map +1 -1
  276. package/lib/typescript/components/Modals/ChatProfileModal/ChatProfileModal.d.ts.map +1 -1
  277. package/lib/typescript/components/Modals/FilePreviewModal/DocumentViewer.d.ts +29 -0
  278. package/lib/typescript/components/Modals/FilePreviewModal/DocumentViewer.d.ts.map +1 -0
  279. package/lib/typescript/components/Modals/FilePreviewModal/FilePreviewModal.d.ts +0 -1
  280. package/lib/typescript/components/Modals/FilePreviewModal/FilePreviewModal.d.ts.map +1 -1
  281. package/lib/typescript/components/Modals/FilePreviewModal/PdfView.d.ts.map +1 -1
  282. package/lib/typescript/components/Modals/Modal/Modal.d.ts.map +1 -1
  283. package/lib/typescript/components/Modals/ModalHeaderComponent.d.ts.map +1 -1
  284. package/lib/typescript/components/Modals/ModalWrapper/ModalWrapper.d.ts.map +1 -1
  285. package/lib/typescript/components/Modals/UserProfileModal/UserProfileModal.d.ts.map +1 -1
  286. package/lib/typescript/components/Modals/styledModalComponents.d.ts.map +1 -1
  287. package/lib/typescript/components/styled/MediaFilePreview.d.ts.map +1 -1
  288. package/lib/typescript/components/styled/MessageImage.d.ts.map +1 -1
  289. package/lib/typescript/components/styled/NewMessageLabel.d.ts +1 -5
  290. package/lib/typescript/components/styled/NewMessageLabel.d.ts.map +1 -1
  291. package/lib/typescript/components/styled/SendInput.d.ts.map +1 -1
  292. package/lib/typescript/components/styled/StyledComponents.d.ts.map +1 -1
  293. package/lib/typescript/components/styled/StyledInputComponents/MediaComponents.d.ts +9 -1
  294. package/lib/typescript/components/styled/StyledInputComponents/MediaComponents.d.ts.map +1 -1
  295. package/lib/typescript/components/styled/StyledInputComponents/StyledInputComponents.d.ts.map +1 -1
  296. package/lib/typescript/components/styled/UnsupportedType.d.ts +1 -0
  297. package/lib/typescript/components/styled/UnsupportedType.d.ts.map +1 -1
  298. package/lib/typescript/components/styled/VideoMessage.d.ts.map +1 -1
  299. package/lib/typescript/context/xmppProvider.d.ts +1 -0
  300. package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
  301. package/lib/typescript/helpers/chatAutoEnterer.d.ts.map +1 -1
  302. package/lib/typescript/helpers/historyPreloadScheduler.d.ts.map +1 -1
  303. package/lib/typescript/helpers/insertMessageWithDelimiter.d.ts.map +1 -1
  304. package/lib/typescript/helpers/keyboardLayout.d.ts +9 -0
  305. package/lib/typescript/helpers/keyboardLayout.d.ts.map +1 -0
  306. package/lib/typescript/helpers/mediaDimensions.d.ts +9 -0
  307. package/lib/typescript/helpers/mediaDimensions.d.ts.map +1 -0
  308. package/lib/typescript/helpers/mimeToExtension.d.ts +8 -0
  309. package/lib/typescript/helpers/mimeToExtension.d.ts.map +1 -1
  310. package/lib/typescript/helpers/normalizeRoomJid.d.ts +2 -0
  311. package/lib/typescript/helpers/normalizeRoomJid.d.ts.map +1 -0
  312. package/lib/typescript/hooks/useChatRoomFocus.d.ts +42 -0
  313. package/lib/typescript/hooks/useChatRoomFocus.d.ts.map +1 -0
  314. package/lib/typescript/hooks/useHeapSender.d.ts.map +1 -1
  315. package/lib/typescript/hooks/useLogout.d.ts +23 -1
  316. package/lib/typescript/hooks/useLogout.d.ts.map +1 -1
  317. package/lib/typescript/hooks/usePushNotifications.d.ts.map +1 -1
  318. package/lib/typescript/hooks/useRoomState.d.ts +1 -0
  319. package/lib/typescript/hooks/useRoomState.d.ts.map +1 -1
  320. package/lib/typescript/hooks/useSendMessage.d.ts.map +1 -1
  321. package/lib/typescript/hooks/useUnreadMessagesCounter.d.ts.map +1 -1
  322. package/lib/typescript/main.d.ts +1 -0
  323. package/lib/typescript/main.d.ts.map +1 -1
  324. package/lib/typescript/networking/api-requests/auth.api.d.ts +3 -0
  325. package/lib/typescript/networking/api-requests/auth.api.d.ts.map +1 -1
  326. package/lib/typescript/networking/api-requests/rooms.api.d.ts.map +1 -1
  327. package/lib/typescript/networking/apiClient.d.ts.map +1 -1
  328. package/lib/typescript/networking/outboundQueue.d.ts +57 -0
  329. package/lib/typescript/networking/outboundQueue.d.ts.map +1 -0
  330. package/lib/typescript/networking/xmpp/createTimeoutPromise.xmpp.d.ts.map +1 -1
  331. package/lib/typescript/networking/xmpp/flushLastViewedToPrivateStore.d.ts +1 -1
  332. package/lib/typescript/networking/xmpp/flushLastViewedToPrivateStore.d.ts.map +1 -1
  333. package/lib/typescript/networking/xmpp/getRoomsPaged.xmpp.d.ts +1 -1
  334. package/lib/typescript/networking/xmpp/getRoomsPaged.xmpp.d.ts.map +1 -1
  335. package/lib/typescript/networking/xmpp/presenceInRoom.xmpp.d.ts.map +1 -1
  336. package/lib/typescript/networking/xmpp/sendTextMessage.xmpp.d.ts.map +1 -1
  337. package/lib/typescript/networking/xmppClient.d.ts +33 -4
  338. package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
  339. package/lib/typescript/roomStore/Middleware/unreadMidlleware.d.ts.map +1 -1
  340. package/lib/typescript/roomStore/roomsSlice.d.ts +8 -1
  341. package/lib/typescript/roomStore/roomsSlice.d.ts.map +1 -1
  342. package/lib/typescript/types/models/media.model.d.ts +1 -0
  343. package/lib/typescript/types/models/media.model.d.ts.map +1 -1
  344. package/lib/typescript/types/models/xmpp.model.d.ts +1 -1
  345. package/lib/typescript/types/models/xmpp.model.d.ts.map +1 -1
  346. package/lib/typescript/types/types.d.ts +20 -0
  347. package/lib/typescript/types/types.d.ts.map +1 -1
  348. package/lib/typescript/utils/installPromiseRejectionTracker.d.ts +20 -0
  349. package/lib/typescript/utils/installPromiseRejectionTracker.d.ts.map +1 -0
  350. package/lib/typescript/utils/resendMessage.d.ts.map +1 -1
  351. package/package.json +23 -4
  352. package/src/assets/icons.tsx +23 -14
  353. package/src/components/ContextMenu/ContextMenuComponents.tsx +10 -5
  354. package/src/components/MainComponents/ChatHeader.tsx +46 -15
  355. package/src/components/MainComponents/ChatRoom.tsx +95 -66
  356. package/src/components/MainComponents/ChatWrapper.tsx +127 -42
  357. package/src/components/MainComponents/MediaMessage.tsx +14 -8
  358. package/src/components/MainComponents/MessageContainer.tsx +4 -2
  359. package/src/components/MainComponents/MessageList.tsx +175 -28
  360. package/src/components/MainComponents/ReduxWrapper.tsx +19 -9
  361. package/src/components/MainComponents/SystemMessage.tsx +38 -26
  362. package/src/components/MessageBubble/InteractionsOverlay.tsx +121 -0
  363. package/src/components/MessageBubble/Message.tsx +133 -63
  364. package/src/components/MessageBubble/MessageInteractions.tsx +146 -58
  365. package/src/components/Modals/AttachSheet/AttachSheet.tsx +146 -39
  366. package/src/components/Modals/ChatProfileModal/ChatProfileModal.tsx +25 -3
  367. package/src/components/Modals/FilePreviewModal/DocumentViewer.tsx +93 -0
  368. package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +161 -26
  369. package/src/components/Modals/FilePreviewModal/PdfView.tsx +32 -4
  370. package/src/components/Modals/Modal/Modal.tsx +4 -1
  371. package/src/components/Modals/ModalHeaderComponent.tsx +8 -1
  372. package/src/components/Modals/ModalWrapper/ModalWrapper.tsx +70 -58
  373. package/src/components/Modals/UserProfileModal/UserProfileModal.tsx +19 -15
  374. package/src/components/Modals/styledModalComponents.tsx +17 -13
  375. package/src/components/styled/MediaFilePreview.tsx +46 -26
  376. package/src/components/styled/MessageImage.tsx +58 -13
  377. package/src/components/styled/NewMessageLabel.tsx +8 -5
  378. package/src/components/styled/SendInput.tsx +64 -29
  379. package/src/components/styled/StyledComponents.ts +6 -3
  380. package/src/components/styled/StyledInputComponents/MediaComponents.tsx +15 -5
  381. package/src/components/styled/StyledInputComponents/StyledInputComponents.tsx +7 -2
  382. package/src/components/styled/UnsupportedType.tsx +7 -1
  383. package/src/components/styled/VideoMessage.tsx +76 -12
  384. package/src/context/MessageNotificationContext.tsx +8 -8
  385. package/src/context/xmppProvider.tsx +247 -30
  386. package/src/helpers/chatAutoEnterer.ts +2 -8
  387. package/src/helpers/historyPreloadScheduler.ts +4 -1
  388. package/src/helpers/insertMessageWithDelimiter.ts +22 -15
  389. package/src/helpers/keyboardLayout.ts +18 -0
  390. package/src/helpers/mediaDimensions.ts +47 -0
  391. package/src/helpers/mimeToExtension.ts +64 -0
  392. package/src/helpers/normalizeRoomJid.ts +6 -0
  393. package/src/hooks/useChatRoomFocus.ts +95 -0
  394. package/src/hooks/useHeapSender.tsx +2 -1
  395. package/src/hooks/useLogout.tsx +36 -13
  396. package/src/hooks/usePushNotifications.ts +21 -8
  397. package/src/hooks/useRoomState.tsx +4 -0
  398. package/src/hooks/useSendMessage.tsx +258 -13
  399. package/src/hooks/useUnreadMessagesCounter.ts +13 -16
  400. package/src/main.ts +1 -0
  401. package/src/networking/api-requests/auth.api.ts +55 -1
  402. package/src/networking/api-requests/rooms.api.ts +31 -7
  403. package/src/networking/apiClient.ts +2 -1
  404. package/src/networking/outboundQueue.ts +98 -0
  405. package/src/networking/xmpp/createTimeoutPromise.xmpp.ts +5 -1
  406. package/src/networking/xmpp/flushLastViewedToPrivateStore.ts +11 -13
  407. package/src/networking/xmpp/getRoomsPaged.xmpp.ts +27 -3
  408. package/src/networking/xmpp/presenceInRoom.xmpp.ts +24 -9
  409. package/src/networking/xmpp/sendTextMessage.xmpp.ts +2 -1
  410. package/src/networking/xmppClient.ts +317 -43
  411. package/src/roomStore/Middleware/unreadMidlleware.tsx +23 -5
  412. package/src/roomStore/roomsSlice.ts +177 -27
  413. package/src/types/models/media.model.ts +3 -0
  414. package/src/types/models/xmpp.model.ts +2 -1
  415. package/src/types/types.ts +31 -0
  416. package/src/utils/installPromiseRejectionTracker.ts +91 -0
  417. package/src/utils/resendMessage.ts +2 -1
@@ -5,8 +5,8 @@ import {
5
5
  StyleSheet,
6
6
  findNodeHandle,
7
7
  UIManager,
8
- Dimensions,
9
8
  Text,
9
+ Keyboard,
10
10
  } from 'react-native';
11
11
  import { useDispatch, useSelector } from 'react-redux';
12
12
  import { RootState } from '../../roomStore';
@@ -52,7 +52,9 @@ const CustomMessageBubble = styled.View<{
52
52
  }>`
53
53
  position: relative;
54
54
  max-width: 85%;
55
- min-width: 30%;
55
+ min-width: ${({ isMedia }) => (isMedia ? '0' : '30%')};
56
+ align-items: ${({ isMedia, isUser }) =>
57
+ isMedia ? (isUser ? 'flex-end' : 'flex-start') : 'stretch'};
56
58
  padding: ${({isMedia}) => isMedia ? '0' : '10'}px;
57
59
  padding-bottom: ${({isMedia}) => isMedia ? '6' : '10'}px;
58
60
  margin-right: ${({isUser}) => isUser ? '0' : '10px'};
@@ -94,9 +96,12 @@ const CustomMessagePhotoContainer = styled.TouchableOpacity`
94
96
  margin-right: 10px;
95
97
  `;
96
98
 
97
- const CustomUserName = styled.Text<{ color?: string }>`
99
+ const CustomUserName = styled.Text<{ color?: string; media: boolean }>`
98
100
  font-size: 14px;
99
101
  font-weight: 500;
102
+ padding-bottom: 8px;
103
+ padding-left: ${({media}) => media ? '16px': 0};
104
+ padding-top: ${({media}) => media ? '8px': 0};
100
105
  color: ${({ color }) => color || '#333'};
101
106
  `;
102
107
 
@@ -113,6 +118,15 @@ const CustomMessageTimestamp = styled.Text<{
113
118
  align-self: flex-end;
114
119
  `;
115
120
 
121
+ const CustomTimestampRow = styled.View<{media: boolean}>`
122
+ flex-direction: row;
123
+ align-items: center;
124
+ align-self: flex-end;
125
+ margin-top: 4;
126
+ gap: 4;
127
+ padding-right: ${({media}) => media ? '10px': 0};
128
+ `;
129
+
116
130
  const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
117
131
  const dispatch = useDispatch();
118
132
  const { client } = useXmppClient();
@@ -127,12 +141,15 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
127
141
  }
128
142
 
129
143
  const [contextMenuPosition, setContextMenuPosition] = useState<{
130
- x: number;
131
- y: number;
144
+ left: number;
145
+ right: number;
146
+ top: number;
147
+ bottom: number;
132
148
  } | null>(null);
133
149
 
134
150
  const timerRef = useRef<NodeJS.Timeout | null>(null);
135
151
  const messageRef = useRef<View>(null);
152
+ const bubbleRef = useRef<View>(null);
136
153
 
137
154
  const handleUserAvatarClick = (user: IUser): void => {
138
155
  dispatch(setActiveModal(MODAL_TYPES.PROFILE));
@@ -219,45 +236,40 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
219
236
  );
220
237
  };
221
238
 
239
+ // Long-press → capture the bubble's on-screen bounding box. The actual
240
+ // menu placement (above / below, clamped to the viewport) is done in
241
+ // MessageInteractions, which measures the rendered menu height via
242
+ // onLayout — more accurate than estimating it here, and it's what keeps
243
+ // the menu adjacent to the message instead of floating far above (#13).
222
244
  const handleLongPress = () => {
223
- if (messageRef.current) {
224
- const nodeHandle = findNodeHandle(messageRef.current);
245
+ const target = (bubbleRef.current ?? messageRef.current) as any;
246
+ if (target?.measureInWindow) {
247
+ // Use measureInWindow — the SAME API the overlay host uses to find
248
+ // its own window origin. Both are then in identical window
249
+ // coordinates, so MessageInteractions' host-local conversion
250
+ // (subtract origin) is exact. Mixing this with UIManager.measure
251
+ // (pageX/pageY) drifted on Android (status-bar offset) and pushed the
252
+ // "below" menu too far from the message.
253
+ target.measureInWindow(
254
+ (x: number, y: number, width: number, height: number) => {
255
+ setContextMenuPosition({
256
+ left: x,
257
+ right: x + width,
258
+ top: y,
259
+ bottom: y + height,
260
+ });
261
+ }
262
+ );
263
+ } else if (target) {
264
+ const nodeHandle = findNodeHandle(target);
225
265
  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 });
266
+ UIManager.measure(nodeHandle, (_x, _y, width, height, pageX, pageY) => {
267
+ setContextMenuPosition({
268
+ left: pageX,
269
+ right: pageX + width,
270
+ top: pageY,
271
+ bottom: pageY + height,
272
+ });
261
273
  });
262
274
  }
263
275
  }
@@ -297,7 +309,19 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
297
309
  >
298
310
  {!isUser && (
299
311
  <CustomMessagePhotoContainer
300
- onPress={() => handleUserAvatarClick(message.user)}
312
+ // `disableProfilesInteractions` gates every entry point to the
313
+ // user-profile popup: this in-bubble avatar AND the chat-title
314
+ // press in ChatHeader. When disabled we still render the avatar
315
+ // visually but make it non-interactive (no press, no a11y
316
+ // affordance) — consumers who want it gone entirely can return
317
+ // null from a custom message component.
318
+ onPress={
319
+ config?.disableProfilesInteractions
320
+ ? undefined
321
+ : () => handleUserAvatarClick(message.user)
322
+ }
323
+ disabled={!!config?.disableProfilesInteractions}
324
+ accessible={!config?.disableProfilesInteractions}
301
325
  >
302
326
  {message.user?.profileImage ? (
303
327
  <CustomMessagePhoto source={{ uri: message.user.profileImage }} />
@@ -307,6 +331,26 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
307
331
  </CustomMessagePhotoContainer>
308
332
  )}
309
333
  <Pressable
334
+ // The Pressable fills the row's content area, so the bubble
335
+ // inside must be aligned to the sender's side — otherwise it
336
+ // hugs the left and own media bubbles sit with a right-side
337
+ // gap (text bubbles only looked right because they're narrow).
338
+ // flex-end for own, flex-start for others.
339
+ style={{
340
+ flex: 1,
341
+ alignItems: isUser ? 'flex-end' : 'flex-start',
342
+ }}
343
+ // A plain tap anywhere on a message bubble dismisses the
344
+ // keyboard. The FlatList's keyboardShouldPersistTaps="handled"
345
+ // already dismisses taps on the empty GAPS between bubbles, but
346
+ // the bubble Pressable itself is a touch responder, so taps that
347
+ // landed on a message were "handled" and the keyboard stayed —
348
+ // which is exactly the "closes only every other time" symptom
349
+ // (hit a gap → close, hit a bubble → stay). Explicitly dismissing
350
+ // here makes it reliable across the whole list. onPress does NOT
351
+ // fire when onLongPress fires, so opening MessageInteractions via
352
+ // long-press never triggers this dismiss.
353
+ onPress={() => Keyboard.dismiss()}
310
354
  // disableInteractions hides the long-press → context menu
311
355
  // (delete / edit / reply / react). Mirrors web's config gate.
312
356
  onLongPress={
@@ -315,6 +359,7 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
315
359
  delayLongPress={500}
316
360
  >
317
361
  <CustomMessageBubble
362
+ {...({ ref: bubbleRef, collapsable: false } as any)}
318
363
  isUser={isUser}
319
364
  deleted={message.isDeleted}
320
365
  isMedia={message?.isMediafile === 'true' && !message?.isDeleted}
@@ -322,7 +367,7 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
322
367
  backgroundMessage={config?.messageColor?.backgroundMessage}
323
368
  >
324
369
  {!isUser && (
325
- <CustomUserName color={config?.colors?.primary}>
370
+ <CustomUserName color={config?.colors?.primary} media={message?.isMediafile === 'true'}>
326
371
  {message.user.name}
327
372
  </CustomUserName>
328
373
  )}
@@ -341,6 +386,7 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
341
386
  messageText={message.locationPreview}
342
387
  location={message?.location}
343
388
  message={message}
389
+ isUser={isUser}
344
390
  />
345
391
  ) : (
346
392
  <>
@@ -364,26 +410,31 @@ const Message: React.FC<MessageProps> = ({ message, isUser, isReply }) => {
364
410
  langSource={langSource}
365
411
  />
366
412
  )}
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
413
+ {/* <View style={styles.timestampRow}> */}
414
+ <CustomTimestampRow media={message?.isMediafile === 'true'}>
415
+ {!config?.disableSentLogic && isUser && isPending && (
416
+ <Text style={styles.timestampText}>sending...</Text>
417
+ )}
418
+ {!config?.disableSentLogic && isUser && isFailed && (
419
+ <Text
420
+ onPress={onRetryPress}
421
+ style={styles.failedText}
422
+ accessibilityRole="button"
423
+ accessibilityLabel="Retry sending message"
424
+ >
425
+ ! Failed — tap to retry
426
+ </Text>
427
+ )}
428
+ <Text style={styles.timestampText}>
429
+ {new Date(message.date).toLocaleTimeString([], {
430
+ hour: '2-digit',
431
+ minute: '2-digit',
432
+ })}
377
433
  </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>
434
+ {!config?.disableSentLogic && isUser && !isPending && !isFailed && (
435
+ <DoubleTick />
436
+ )}
437
+ </CustomTimestampRow>
387
438
 
388
439
  {message?.reply?.length && message?.reply?.length > 0 ? (
389
440
  <BottomReplyContainer
@@ -455,4 +506,23 @@ const styles = StyleSheet.create({
455
506
  fontWeight: '600',
456
507
  marginRight: 6,
457
508
  },
509
+ // Row keeps the time text and the DoubleTick SVG on the same line.
510
+ // The time Text gets an explicit lineHeight equal to the 16px tick so
511
+ // both occupy the same-height box; with alignItems center they line up
512
+ // exactly. Without the lineHeight the Text's natural line box was
513
+ // shorter than the icon, so the centred tick floated above the digits.
514
+ timestampRow: {
515
+ flexDirection: 'row',
516
+ alignItems: 'center',
517
+ alignSelf: 'flex-end',
518
+ marginTop: 4,
519
+ gap: 4,
520
+ },
521
+ timestampText: {
522
+ fontSize: 12,
523
+ lineHeight: 16,
524
+ color: '#999',
525
+ includeFontPadding: false,
526
+ textAlignVertical: 'center',
527
+ },
458
528
  });
@@ -1,4 +1,4 @@
1
- import React, { useMemo } from 'react';
1
+ import React, { useEffect, useId, 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';
@@ -8,20 +8,27 @@ import {
8
8
  } from '../../helpers/constants/MESSAGE_INTERACTIONS';
9
9
  import { IMessage } from '../../types/types';
10
10
  import {
11
- Modal,
12
11
  Text,
13
12
  StyleSheet,
14
13
  View,
15
14
  Pressable,
15
+ Dimensions,
16
+ type LayoutChangeEvent,
16
17
  } from 'react-native';
17
18
  import * as Clipboard from 'expo-clipboard';
18
19
  import { useToast } from '../../context/ToastContext';
20
+ import { useInteractionsOverlay } from './InteractionsOverlay';
19
21
 
20
22
  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;
@@ -40,11 +47,26 @@ const MessageInteractions: React.FC<MessageInteractionsProps> = ({
40
47
  handleEditMessage,
41
48
  }) => {
42
49
  const { showToast } = useToast();
50
+ const { present, dismiss, originX, originY } = useInteractionsOverlay();
51
+ const overlayId = useId();
43
52
 
44
53
  const config = useSelector(
45
54
  (state: RootState) => state.chatSettingStore.config
46
55
  );
47
56
 
57
+ const [menuSize, setMenuSize] = useState({ width: 0, height: 0 });
58
+
59
+ const handleMenuLayout = (e: LayoutChangeEvent) => {
60
+ const { width, height } = e.nativeEvent.layout;
61
+ if (
62
+ width &&
63
+ height &&
64
+ (width !== menuSize.width || height !== menuSize.height)
65
+ ) {
66
+ setMenuSize({ width, height });
67
+ }
68
+ };
69
+
48
70
  const handleCopyMessage = async (text: string) => {
49
71
  try {
50
72
  await Clipboard.setStringAsync(text);
@@ -71,58 +93,110 @@ const MessageInteractions: React.FC<MessageInteractionsProps> = ({
71
93
  };
72
94
 
73
95
  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
- };
96
+ if (!position) {
97
+ return undefined;
98
+ }
99
+
100
+ if (!menuSize.width || !menuSize.height) {
101
+ return { top: position.bottom, left: position.left, opacity: 0 };
102
+ }
103
+
104
+ const { width: screenWidth, height: screenHeight } =
105
+ Dimensions.get('window');
106
+ const bottomReserve = (config?.keyboardVerticalOffset ?? 0) + 24;
107
+ const topReserve = 16;
108
+ const sideMargin = 8;
109
+
110
+ const spaceBelow = screenHeight - position.bottom - bottomReserve;
111
+ const spaceAbove = position.top - topReserve;
112
+
113
+ let top: number;
114
+ if (spaceBelow >= menuSize.height) {
115
+ top = position.bottom;
116
+ } else if (spaceAbove >= menuSize.height) {
117
+ top = position.top - menuSize.height;
118
+ } else {
119
+ top = Math.max(topReserve, screenHeight - menuSize.height - bottomReserve);
86
120
  }
87
- }, [position, isUser]);
88
-
89
- if (config?.disableInteractions) {return null;}
90
-
91
- return (
92
- <Modal
93
- transparent
94
- animationType="fade"
95
- visible={true}
96
- onRequestClose={closeMenu}
97
- >
98
- {!message.isDeleted && (
99
- <View style={styles.overlayFill}>
100
- <Pressable style={StyleSheet.absoluteFill} onPress={closeMenu} />
101
- <View style={[styles.contextMenu, memoPosition]}>
102
- <MenuItem onPress={() => handleCopyMessage(message.body!)}>
103
- <Text>{MESSAGE_INTERACTIONS.COPY}</Text>
104
- <MESSAGE_INTERACTIONS_ICONS.COPY />
105
- </MenuItem>
106
- {isUser && (
107
- <>
108
- <Delimeter />
109
- <MenuItem onPress={handleEditMessage}>
110
- <Text>{MESSAGE_INTERACTIONS.EDIT}</Text>
111
- <MESSAGE_INTERACTIONS_ICONS.EDIT />
112
- </MenuItem>
113
- <Delimeter />
114
- <MenuItem onPress={handleDeleteMessage}>
115
- <Text>{MESSAGE_INTERACTIONS.DELETE}</Text>
116
- <MESSAGE_INTERACTIONS_ICONS.DELETE />
117
- </MenuItem>
118
- </>
119
- )}
120
- </View>
121
+
122
+ const leftMargin = position.left;
123
+ const rightMargin = screenWidth - position.right;
124
+ const rightAligned = rightMargin < leftMargin;
125
+
126
+ let left = rightAligned ? position.right - menuSize.width : position.left;
127
+ left = Math.max(
128
+ sideMargin,
129
+ Math.min(left, screenWidth - menuSize.width - sideMargin)
130
+ );
131
+
132
+ return { top, left };
133
+ }, [position, menuSize, config?.keyboardVerticalOffset]);
134
+
135
+ // Window coords (memoPosition) → host-local coords. The overlay host
136
+ // sits below the status bar / header, so subtract its measured origin.
137
+ const localPosition = useMemo(() => {
138
+ if (!memoPosition) {return undefined;}
139
+ return {
140
+ ...memoPosition,
141
+ top: (memoPosition as { top: number }).top - originY,
142
+ left: (memoPosition as { left: number }).left - originX,
143
+ };
144
+ }, [memoPosition, originX, originY]);
145
+
146
+ const content =
147
+ config?.disableInteractions || message.isDeleted ? null : (
148
+ <View style={styles.overlayFill}>
149
+ <Pressable style={StyleSheet.absoluteFill} onPress={closeMenu} />
150
+ <View
151
+ style={[styles.contextMenu, localPosition]}
152
+ onLayout={handleMenuLayout}
153
+ >
154
+ <MenuItem onPress={() => handleCopyMessage(message.body!)}>
155
+ <Text style={styles.menuText}>{MESSAGE_INTERACTIONS.COPY}</Text>
156
+ <MESSAGE_INTERACTIONS_ICONS.COPY />
157
+ </MenuItem>
158
+ {isUser && (
159
+ <>
160
+ {/* Edit only for non-media messages (upstream guard) +
161
+ consistent label styling (menuText). */}
162
+ {message?.isMediafile !== 'true' && (
163
+ <>
164
+ <Delimeter />
165
+ <MenuItem onPress={handleEditMessage}>
166
+ <Text style={styles.menuText}>
167
+ {MESSAGE_INTERACTIONS.EDIT}
168
+ </Text>
169
+ <MESSAGE_INTERACTIONS_ICONS.EDIT />
170
+ </MenuItem>
171
+ </>
172
+ )}
173
+ <Delimeter />
174
+ <MenuItem onPress={handleDeleteMessage}>
175
+ <Text style={styles.menuText}>{MESSAGE_INTERACTIONS.DELETE}</Text>
176
+ <MESSAGE_INTERACTIONS_ICONS.DELETE />
177
+ </MenuItem>
178
+ </>
179
+ )}
121
180
  </View>
122
- )}
181
+ </View>
182
+ );
123
183
 
124
- </Modal>
125
- );
184
+ // Render the menu in the in-tree overlay host instead of a React Native
185
+ // <Modal>. A Modal opens its own window on Android, which steals focus
186
+ // from the chat input and dismisses the keyboard when the menu opens;
187
+ // hosting it in the same view tree keeps the keyboard up. Re-run every
188
+ // render so menuSize/position updates refresh the hosted node.
189
+ // eslint-disable-next-line react-hooks/exhaustive-deps
190
+ useEffect(() => {
191
+ if (content) {
192
+ present(overlayId, content);
193
+ } else {
194
+ dismiss(overlayId);
195
+ }
196
+ });
197
+ useEffect(() => () => dismiss(overlayId), [dismiss, overlayId]);
198
+
199
+ return null;
126
200
  };
127
201
 
128
202
  export default MessageInteractions;
@@ -135,14 +209,28 @@ const styles = StyleSheet.create({
135
209
  contextMenu: {
136
210
  position: 'absolute',
137
211
  backgroundColor: 'white',
138
- paddingVertical: 8,
139
- paddingHorizontal: 16,
140
- borderRadius: 8,
212
+ paddingVertical: 4,
213
+ paddingHorizontal: 8,
214
+ borderRadius: 12,
141
215
  shadowColor: '#000',
142
216
  shadowOffset: { width: 0, height: 2 },
143
- shadowOpacity: 0.25,
144
- shadowRadius: 4,
145
- elevation: 5,
217
+ shadowOpacity: 0.18,
218
+ shadowRadius: 8,
219
+ elevation: 6,
146
220
  minWidth: 180,
221
+ // Clip the children to the rounded corners (the hairline dividers
222
+ // run full width; without this they'd poke past the rounding on
223
+ // Android).
224
+ overflow: 'hidden',
225
+ },
226
+ // Explicit label styling so rows are the same height on both platforms
227
+ // — Android's default includeFontPadding made the menu rows taller and
228
+ // misaligned vs iOS. marginRight keeps the label off the trailing icon.
229
+ menuText: {
230
+ fontSize: 16,
231
+ color: '#141414',
232
+ includeFontPadding: false,
233
+ textAlignVertical: 'center',
234
+ marginRight: 16,
147
235
  },
148
236
  });