@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
@@ -1,7 +1,9 @@
1
1
  /** @format */
2
2
 
3
- import React from 'react';
3
+ import React, { useEffect, useRef, useState } from 'react';
4
4
  import {
5
+ Animated,
6
+ Easing,
5
7
  Modal,
6
8
  Platform,
7
9
  StyleSheet,
@@ -20,6 +22,11 @@ interface AttachSheetProps {
20
22
  primaryColor?: string;
21
23
  }
22
24
 
25
+ // Starting translateY for the sheet. Larger than any realistic sheet
26
+ // height so it sits fully below the viewport before opening; the exact
27
+ // number doesn't matter visually because it animates to 0.
28
+ const SHEET_OFFSCREEN = 600;
29
+
23
30
  const AttachSheet: React.FC<AttachSheetProps> = ({
24
31
  visible,
25
32
  onClose,
@@ -28,9 +35,94 @@ const AttachSheet: React.FC<AttachSheetProps> = ({
28
35
  onDocument,
29
36
  primaryColor = '#0052CD',
30
37
  }) => {
38
+ // Pending handler captured on row tap — runs once the EXIT animation
39
+ // finishes so iOS doesn't try to present an image picker over a
40
+ // still-dismissing modal (was the original reason for the
41
+ // platform-split + Modal.onDismiss dance). The animation completion
42
+ // callback now drives this on both platforms.
43
+ const pendingRef = useRef<(() => void) | null>(null);
44
+
45
+ // Local "modal stays mounted while animating out" state. The parent
46
+ // controls `visible`; we mirror it but delay unmount until the exit
47
+ // animation has played so the slide-down is actually seen.
48
+ const [mounted, setMounted] = useState(visible);
49
+
50
+ // Two separate Animated values — the whole point of the rewrite:
51
+ // • backdropOpacity: fades the dim layer in/out (no slide).
52
+ // • sheetTranslateY: slides ONLY the sheet up from below.
53
+ // RN Modal's built-in `animationType="slide"` slides the entire
54
+ // contents (backdrop included) so the dim shade entered from the
55
+ // bottom — what looked "awful". Splitting these gives the standard
56
+ // bottom-sheet feel.
57
+ const backdropOpacity = useRef(new Animated.Value(0)).current;
58
+ const sheetTranslateY = useRef(
59
+ new Animated.Value(SHEET_OFFSCREEN)
60
+ ).current;
61
+
62
+ useEffect(() => {
63
+ if (visible) {
64
+ setMounted(true);
65
+ // Reset to starting positions BEFORE the Modal becomes visible so
66
+ // the first animated frame is below-the-fold (not a flash of the
67
+ // settled sheet).
68
+ backdropOpacity.setValue(0);
69
+ sheetTranslateY.setValue(SHEET_OFFSCREEN);
70
+ // requestAnimationFrame lets the Modal commit its mount before the
71
+ // animation starts; without this the first frame can be skipped.
72
+ const raf = requestAnimationFrame(() => {
73
+ Animated.parallel([
74
+ Animated.timing(backdropOpacity, {
75
+ toValue: 1,
76
+ duration: 220,
77
+ easing: Easing.out(Easing.cubic),
78
+ useNativeDriver: true,
79
+ }),
80
+ Animated.spring(sheetTranslateY, {
81
+ toValue: 0,
82
+ // Tuned for a quick-but-not-bouncy entry. tension is the
83
+ // "stiffness" knob, friction the "damping" knob in RN's
84
+ // spring config.
85
+ tension: 90,
86
+ friction: 14,
87
+ useNativeDriver: true,
88
+ }),
89
+ ]).start();
90
+ });
91
+ return () => cancelAnimationFrame(raf);
92
+ }
93
+ if (!mounted) {return;}
94
+ // Exit: fade the backdrop, slide the sheet down, then unmount and
95
+ // fire any pending picker handler.
96
+ Animated.parallel([
97
+ Animated.timing(backdropOpacity, {
98
+ toValue: 0,
99
+ duration: 180,
100
+ easing: Easing.in(Easing.cubic),
101
+ useNativeDriver: true,
102
+ }),
103
+ Animated.timing(sheetTranslateY, {
104
+ toValue: SHEET_OFFSCREEN,
105
+ duration: 220,
106
+ easing: Easing.in(Easing.cubic),
107
+ useNativeDriver: true,
108
+ }),
109
+ ]).start(({ finished }) => {
110
+ if (!finished) {return;} // interrupted (rapid re-open) → leave state alone
111
+ setMounted(false);
112
+ const fn = pendingRef.current;
113
+ pendingRef.current = null;
114
+ fn?.();
115
+ });
116
+ return undefined;
117
+ }, [visible]); // eslint-disable-line react-hooks/exhaustive-deps
118
+
31
119
  const trigger = (handler: () => void) => () => {
120
+ // Capture the handler and ask parent to close. The exit-animation
121
+ // completion above fires `handler` AFTER the sheet has slid away,
122
+ // so iOS no longer tries to present a picker over a dismissing
123
+ // modal — and Android no longer needs the ad-hoc 150ms setTimeout.
124
+ pendingRef.current = handler;
32
125
  onClose();
33
- setTimeout(handler, 150);
34
126
  };
35
127
 
36
128
  const rows: {
@@ -62,51 +154,66 @@ const AttachSheet: React.FC<AttachSheetProps> = ({
62
154
  return (
63
155
  <Modal
64
156
  transparent
65
- visible={visible}
66
- animationType="slide"
157
+ visible={mounted}
158
+ // We drive the animation ourselves — disable RN's built-in slide
159
+ // (which slid the backdrop too) so the fade + spring above are the
160
+ // only motion.
161
+ animationType="none"
67
162
  onRequestClose={onClose}
163
+ // Hardware-back / iOS gesture also routes through onClose, which
164
+ // flips `visible` and triggers our exit animation above.
68
165
  >
69
- <TouchableOpacity
70
- style={styles.backdrop}
71
- activeOpacity={1}
72
- onPress={onClose}
166
+ <Animated.View
167
+ style={[styles.backdrop, { opacity: backdropOpacity }]}
73
168
  >
74
- <TouchableOpacity activeOpacity={1} style={styles.sheet}>
75
- <View style={styles.grabber} />
76
- <Text style={styles.title}>Attach</Text>
77
- {rows.map(({ label, hint, Icon, handler }, idx) => (
78
- <TouchableOpacity
79
- key={label}
80
- activeOpacity={0.6}
81
- style={[
82
- styles.row,
83
- idx < rows.length - 1 && styles.rowDivider,
84
- ]}
85
- onPress={trigger(handler)}
86
- >
87
- <View
169
+ <TouchableOpacity
170
+ style={StyleSheet.absoluteFill}
171
+ activeOpacity={1}
172
+ onPress={onClose}
173
+ />
174
+ <Animated.View
175
+ style={[
176
+ styles.sheet,
177
+ { transform: [{ translateY: sheetTranslateY }] },
178
+ ]}
179
+ >
180
+ <TouchableOpacity activeOpacity={1}>
181
+ <View style={styles.grabber} />
182
+ <Text style={styles.title}>Attach</Text>
183
+ {rows.map(({ label, hint, Icon, handler }, idx) => (
184
+ <TouchableOpacity
185
+ key={label}
186
+ activeOpacity={0.6}
88
187
  style={[
89
- styles.iconBubble,
90
- { backgroundColor: primaryColor + '14' },
188
+ styles.row,
189
+ idx < rows.length - 1 && styles.rowDivider,
91
190
  ]}
191
+ onPress={trigger(handler)}
92
192
  >
93
- <Icon color={primaryColor} width={20} height={20} />
94
- </View>
95
- <View style={styles.rowText}>
96
- <Text style={styles.rowLabel}>{label}</Text>
97
- <Text style={styles.rowHint}>{hint}</Text>
98
- </View>
193
+ <View
194
+ style={[
195
+ styles.iconBubble,
196
+ { backgroundColor: primaryColor + '14' },
197
+ ]}
198
+ >
199
+ <Icon color={primaryColor} width={20} height={20} />
200
+ </View>
201
+ <View style={styles.rowText}>
202
+ <Text style={styles.rowLabel}>{label}</Text>
203
+ <Text style={styles.rowHint}>{hint}</Text>
204
+ </View>
205
+ </TouchableOpacity>
206
+ ))}
207
+ <TouchableOpacity
208
+ activeOpacity={0.6}
209
+ style={styles.cancelRow}
210
+ onPress={onClose}
211
+ >
212
+ <Text style={styles.cancelLabel}>Cancel</Text>
99
213
  </TouchableOpacity>
100
- ))}
101
- <TouchableOpacity
102
- activeOpacity={0.6}
103
- style={styles.cancelRow}
104
- onPress={onClose}
105
- >
106
- <Text style={styles.cancelLabel}>Cancel</Text>
107
214
  </TouchableOpacity>
108
- </TouchableOpacity>
109
- </TouchableOpacity>
215
+ </Animated.View>
216
+ </Animated.View>
110
217
  </Modal>
111
218
  );
112
219
  };
@@ -293,11 +293,22 @@ const ChatProfileModal: React.FC<ChatProfileModalProps> = ({
293
293
  <ProfileImagePlaceholder
294
294
  name={activeRoom.title || activeRoom.name}
295
295
  icon={activeRoom.icon}
296
+ // Two gates control whether the icon is editable:
297
+ // 1. role — only non-participants could edit before.
298
+ // 2. NEW: `disableChatInfo.disableIconEdit` — hard kill from
299
+ // config so a consumer can make the icon read-only for
300
+ // everyone (e.g. branded rooms, patient-facing apps).
301
+ // Both must allow it for the picker/remove affordances to show.
296
302
  upload={{
297
303
  onUpload,
298
- active: activeRoom?.role !== 'participant' ? true : false,
304
+ active:
305
+ !config?.disableChatInfo?.disableIconEdit &&
306
+ activeRoom?.role !== 'participant',
307
+ }}
308
+ remove={{
309
+ enabled: !config?.disableChatInfo?.disableIconEdit,
310
+ onRemoveClick,
299
311
  }}
300
- remove={{ enabled: true, onRemoveClick }}
301
312
  role={activeRoom?.role}
302
313
  size={128}
303
314
  />
@@ -377,7 +388,18 @@ const ChatProfileModal: React.FC<ChatProfileModalProps> = ({
377
388
  >
378
389
  <Pressable
379
390
  style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-start', gap: 16 }}
380
- onPress={() => handleUserAvatarClick(user)}
391
+ // `disableChatInfo.disableMemberTap` blocks the
392
+ // tap entirely so the user-profile popup never
393
+ // opens. `disableMemberProfileActions` only hid
394
+ // the action buttons INSIDE that popup, leaving
395
+ // the popup itself to open on tap — this gate
396
+ // closes the door. Customer-reported #16.
397
+ onPress={
398
+ config?.disableChatInfo?.disableMemberTap
399
+ ? undefined
400
+ : () => handleUserAvatarClick(user)
401
+ }
402
+ disabled={!!config?.disableChatInfo?.disableMemberTap}
381
403
  >
382
404
  <ProfileImagePlaceholder
383
405
  name={`${user.firstName} ${user.lastName}`}
@@ -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
+ });
@@ -1,4 +1,4 @@
1
- import React, { useMemo } from 'react';
1
+ import React, { useEffect, useState } from 'react';
2
2
  import styled from 'styled-components/native';
3
3
  import {
4
4
  CenterContainer,
@@ -11,19 +11,94 @@ import Button from '../../styled/Button';
11
11
  import { RootState } from '../../../roomStore';
12
12
  import { FullScreenImage } from '../../styled/StyledInputComponents/MediaComponents';
13
13
  import { setActiveFile } from '../../../roomStore/chatSettingsSlice';
14
- import { Alert, Text, View, Platform } from 'react-native';
15
- import { Video, ResizeMode } from 'expo-av';
14
+ import {
15
+ Alert,
16
+ Text,
17
+ View,
18
+ Platform,
19
+ TouchableOpacity,
20
+ StyleSheet,
21
+ } from 'react-native';
22
+ import { useVideoPlayer, VideoView } from 'expo-video';
23
+ import { PlayIcon } from '../../../assets/icons';
16
24
  import * as FileSystem from 'expo-file-system/legacy';
17
25
  import * as MediaLibrary from 'expo-media-library';
18
26
  import { useToast } from '../../../context/ToastContext';
19
27
  import PdfViewer from './PdfView';
20
- import { ensureFilenameHasExtension } from '../../../helpers/mimeToExtension';
28
+ import DocumentViewer from './DocumentViewer';
29
+ import AudioMessage from '../../styled/AudioMessage';
30
+ import { ensureFilenameHasExtension, isLikelyAudio } from '../../../helpers/mimeToExtension';
31
+
32
+ // MIME types Google's gview embed renders reliably. Everything else
33
+ // falls through to the info-card so the user can still download.
34
+ const GVIEW_PREVIEWABLE = new Set<string>([
35
+ 'application/msword',
36
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
37
+ 'application/vnd.ms-excel',
38
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
39
+ 'application/vnd.ms-powerpoint',
40
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
41
+ 'text/plain',
42
+ 'text/csv',
43
+ 'application/rtf',
44
+ ]);
45
+ const isGviewPreviewable = (mime: string | undefined | null) => {
46
+ if (!mime) {return false;}
47
+ return GVIEW_PREVIEWABLE.has(mime.toLowerCase().split(';')[0]!.trim());
48
+ };
49
+
50
+ // Full-screen video preview on expo-video (expo-av <Video> is deprecated
51
+ // and rendered blank on Android). `contentFit: contain` fits the clip to
52
+ // the area with the surrounding letterbox painted as the view's own
53
+ // (transparent → theme) background, NOT black. `textureView` so the play
54
+ // overlay composites on top and nothing is clipped on Android.
55
+ const ModalVideo: React.FC<{ uri: string }> = ({ uri }) => {
56
+ const [showPlay, setShowPlay] = useState(true);
21
57
 
22
- export const FullScreenVideo = styled.View`
23
- width: 100%;
24
- height: 100%;
25
- object-fit: contain;
26
- `;
58
+ const player = useVideoPlayer(uri, (p) => {
59
+ p.muted = false;
60
+ });
61
+
62
+ useEffect(() => {
63
+ const sub = player.addListener('playingChange', ({ isPlaying }) => {
64
+ if (isPlaying) {setShowPlay(false);}
65
+ });
66
+ return () => sub.remove();
67
+ }, [player]);
68
+
69
+ const handlePlay = () => {
70
+ setShowPlay(false);
71
+ player.play();
72
+ };
73
+
74
+ return (
75
+ <View style={{ flex: 1, width: '100%' }}>
76
+ <VideoView
77
+ player={player}
78
+ style={{ flex: 1, backgroundColor: 'transparent' }}
79
+ contentFit="contain"
80
+ nativeControls
81
+ surfaceType="textureView"
82
+ allowsFullscreen
83
+ />
84
+ {/* Play affordance shown immediately on open; tapping starts
85
+ playback and the native controls take over. */}
86
+ {showPlay && (
87
+ <TouchableOpacity
88
+ activeOpacity={0.8}
89
+ onPress={handlePlay}
90
+ style={StyleSheet.absoluteFillObject}
91
+ >
92
+ <View style={styles.playOverlay}>
93
+ <View style={styles.playButton}>
94
+ <PlayIcon width={28} height={28} />
95
+ </View>
96
+ </View>
97
+ </TouchableOpacity>
98
+ )}
99
+ </View>
100
+ );
101
+ };
27
102
 
28
103
  interface FilePreviewModalProps {
29
104
  handleCloseModal: any;
@@ -39,7 +114,7 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
39
114
  (state: RootState) => state.chatSettingStore
40
115
  );
41
116
 
42
- if (!activeFile) {return;}
117
+ if (!activeFile) {return null;}
43
118
 
44
119
  const requestStoragePermission = async () => {
45
120
  try {
@@ -143,30 +218,55 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
143
218
  handleCloseModal?.();
144
219
  };
145
220
 
146
- const getMediaComponent = useMemo(() => {
221
+ // NOTE: plain computed value, NOT useMemo. It sits AFTER the
222
+ // `if (!activeFile) return` guard above, so as a hook it would run
223
+ // conditionally (skipped when activeFile is null) — changing the hook
224
+ // count between renders and throwing "rendered more hooks than during
225
+ // the previous render", which crashed the modal the instant you tapped
226
+ // a video/image to open it. Recomputing this on each render is cheap.
227
+ const getMediaComponent: React.ReactNode = (() => {
147
228
  switch (true) {
148
229
  case activeFile.mimetype.startsWith('image/'):
149
230
  return (
150
231
  <FullScreenImage
151
- src={
152
- activeFile.fileURL ||
153
- 'https://as2.ftcdn.net/v2/jpg/02/51/95/53/1000_F_251955356_FAQH0U1y1TZw3ZcdPGybwUkH90a3VAhb.jpg'
154
- }
155
- alt={activeFile.fileName}
232
+ source={{
233
+ uri:
234
+ activeFile.fileURL ||
235
+ 'https://as2.ftcdn.net/v2/jpg/02/51/95/53/1000_F_251955356_FAQH0U1y1TZw3ZcdPGybwUkH90a3VAhb.jpg',
236
+ }}
237
+ resizeMode="contain"
238
+ accessibilityLabel={activeFile.fileName}
156
239
  />
157
240
  );
158
241
  case activeFile.mimetype.startsWith('video/'):
242
+ return <ModalVideo uri={activeFile.fileURL} />;
243
+ // Mirrors the MediaMessage heuristic: treat octet-stream voicemails
244
+ // with audio-shaped filenames / URLs as audio so the preview shows
245
+ // the player instead of an "Unsupported" card. Customer-reported
246
+ // #9 voicemail fix — see isLikelyAudio in mimeToExtension.ts.
247
+ case isLikelyAudio(
248
+ activeFile.mimetype,
249
+ activeFile.fileName,
250
+ activeFile.fileURL
251
+ ):
159
252
  return (
160
- <Video
253
+ <View
161
254
  style={{
162
255
  width: '100%',
163
- height: '100%',
256
+ padding: 20,
257
+ gap: 12,
258
+ backgroundColor: '#FFF8ED',
259
+ borderRadius: 16,
164
260
  }}
165
- source={{ uri: activeFile.fileURL }}
166
- useNativeControls
167
- resizeMode={ResizeMode.CONTAIN}
168
- shouldPlay={false}
169
- />
261
+ >
262
+ <Text style={{ fontSize: 16, fontWeight: '600' }}>
263
+ {ensureFilenameHasExtension(
264
+ activeFile.fileName,
265
+ activeFile.mimetype
266
+ )}
267
+ </Text>
268
+ <AudioMessage src={activeFile.fileURL} />
269
+ </View>
170
270
  );
171
271
  case activeFile.mimetype === 'application/pdf':
172
272
  return <PdfViewer pdfUrl={activeFile.fileURL} />;
@@ -175,6 +275,20 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
175
275
  activeFile.fileName,
176
276
  activeFile.mimetype
177
277
  );
278
+ // Office docs (.docx / .xlsx / .pptx / .doc / .xls / .ppt /
279
+ // .txt / .csv / .rtf) → render inline via Google's gview embed
280
+ // — fixes the "blank preview" complaint for docs (bug #9).
281
+ if (isGviewPreviewable(activeFile.mimetype)) {
282
+ return (
283
+ <DocumentViewer
284
+ url={activeFile.fileURL}
285
+ fileName={displayName}
286
+ />
287
+ );
288
+ }
289
+ // True last-resort fallback for genuinely unrenderable types
290
+ // (binary blobs, exotic MIMEs). Friendly info card with the
291
+ // filename so the user can still download.
178
292
  return (
179
293
  <View
180
294
  style={{
@@ -198,7 +312,7 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
198
312
  );
199
313
  }
200
314
  }
201
- }, [activeFile]);
315
+ })();
202
316
 
203
317
  return (
204
318
  <ModalContainerFullScreen>
@@ -217,10 +331,15 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
217
331
  <CenterContainer
218
332
  style={{
219
333
  display: 'flex',
220
- height: '100%',
334
+ flex: 1,
221
335
  justifyContent: 'center',
222
- overflow: 'hidden',
336
+ // NOTE: do NOT set overflow:'hidden' here — on Android it clips
337
+ // hardware-accelerated children (expo-av Video TextureView and
338
+ // react-native-webview) to nothing, so video/PDF/doc previews
339
+ // render blank while plain <Image> survives.
340
+ overflow: 'visible',
223
341
  padding: 16,
342
+ paddingBottom: 48,
224
343
  }}
225
344
  >
226
345
  {getMediaComponent}
@@ -230,4 +349,20 @@ const FilePreviewModal: React.FC<FilePreviewModalProps> = ({
230
349
  );
231
350
  };
232
351
 
352
+ const styles = StyleSheet.create({
353
+ playOverlay: {
354
+ ...StyleSheet.absoluteFillObject,
355
+ justifyContent: 'center',
356
+ alignItems: 'center',
357
+ },
358
+ playButton: {
359
+ width: 64,
360
+ height: 64,
361
+ borderRadius: 32,
362
+ backgroundColor: 'rgba(0, 0, 0, 0.55)',
363
+ justifyContent: 'center',
364
+ alignItems: 'center',
365
+ },
366
+ });
367
+
233
368
  export default FilePreviewModal;
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect, useState } from 'react';
2
- import { ActivityIndicator, StyleSheet, Text, View } from 'react-native';
2
+ import { ActivityIndicator, Platform, StyleSheet, Text, View } from 'react-native';
3
3
  import { WebView } from 'react-native-webview';
4
4
  import * as FileSystem from 'expo-file-system/legacy';
5
5
 
@@ -10,13 +10,20 @@ interface PdfViewerProps {
10
10
  const PdfViewer: React.FC<PdfViewerProps> = ({ pdfUrl }) => {
11
11
  const [localUri, setLocalUri] = useState<string | null>(null);
12
12
  const [error, setError] = useState<string | null>(null);
13
+ // Android's WebView has NO native PDF renderer, so a file:// (or direct)
14
+ // PDF loads blank. Route it through Google's gview embed using the
15
+ // (public) remote URL — same approach as DocumentViewer for office docs.
16
+ // iOS WKWebView renders PDFs natively, so we download + show the file.
17
+ const isAndroid = Platform.OS === 'android';
13
18
 
14
19
  useEffect(() => {
20
+ if (isAndroid) {
21
+ return;
22
+ }
15
23
  let cancelled = false;
16
24
  const run = async () => {
17
25
  try {
18
- const target =
19
- FileSystem.cacheDirectory + `pdf-${Date.now()}.pdf`;
26
+ const target = FileSystem.cacheDirectory + `pdf-${Date.now()}.pdf`;
20
27
  const result = await FileSystem.downloadAsync(pdfUrl, target);
21
28
  if (cancelled) {return;}
22
29
  setLocalUri(result.uri);
@@ -30,7 +37,28 @@ const PdfViewer: React.FC<PdfViewerProps> = ({ pdfUrl }) => {
30
37
  return () => {
31
38
  cancelled = true;
32
39
  };
33
- }, [pdfUrl]);
40
+ }, [pdfUrl, isAndroid]);
41
+
42
+ if (isAndroid) {
43
+ const gviewUrl = `https://docs.google.com/gview?embedded=true&url=${encodeURIComponent(
44
+ pdfUrl
45
+ )}`;
46
+ return (
47
+ <View style={styles.container}>
48
+ <WebView
49
+ source={{ uri: gviewUrl }}
50
+ style={styles.webview}
51
+ originWhitelist={['*']}
52
+ startInLoadingState
53
+ renderLoading={() => (
54
+ <View style={styles.center}>
55
+ <ActivityIndicator size="large" />
56
+ </View>
57
+ )}
58
+ />
59
+ </View>
60
+ );
61
+ }
34
62
 
35
63
  if (error) {
36
64
  return (
@@ -39,7 +39,10 @@ const Modal: React.FC<ModalProps> = ({ children, modal, setOpenModal }) => {
39
39
  };
40
40
  return (
41
41
  modal && (
42
- <ModalBackground id="modal-background" style={{ position: 'absolute' }}>
42
+ <ModalBackground
43
+ id="modal-background"
44
+ style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }}
45
+ >
43
46
  {renderModalContent()}
44
47
  {children}
45
48
  </ModalBackground>