@ethora/chat-component-rn 26.7.4 → 26.7.5

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 (288) hide show
  1. package/CHANGELOG.md +0 -54
  2. package/README.md +8 -31
  3. package/lib/commonjs/assets/icons.js +2 -7
  4. package/lib/commonjs/assets/icons.js.map +1 -1
  5. package/lib/commonjs/components/Header/HeaderRoomList.js +6 -20
  6. package/lib/commonjs/components/Header/HeaderRoomList.js.map +1 -1
  7. package/lib/commonjs/components/MainComponents/ChatRoom.js +14 -36
  8. package/lib/commonjs/components/MainComponents/ChatRoom.js.map +1 -1
  9. package/lib/commonjs/components/MainComponents/MessageList.js +23 -7
  10. package/lib/commonjs/components/MainComponents/MessageList.js.map +1 -1
  11. package/lib/commonjs/components/MainComponents/ProfileImagePlaceholder.js +13 -14
  12. package/lib/commonjs/components/MainComponents/ProfileImagePlaceholder.js.map +1 -1
  13. package/lib/commonjs/components/MainComponents/RoomList.js +32 -62
  14. package/lib/commonjs/components/MainComponents/RoomList.js.map +1 -1
  15. package/lib/commonjs/components/Menu/HeaderRoomListMenu.js +98 -309
  16. package/lib/commonjs/components/Menu/HeaderRoomListMenu.js.map +1 -1
  17. package/lib/commonjs/components/Modals/AttachSheet/AttachSheet.js +110 -320
  18. package/lib/commonjs/components/Modals/AttachSheet/AttachSheet.js.map +1 -1
  19. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js +213 -552
  20. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  21. package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js +2 -6
  22. package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
  23. package/lib/commonjs/components/Modals/Modal/Modal.js +0 -8
  24. package/lib/commonjs/components/Modals/Modal/Modal.js.map +1 -1
  25. package/lib/commonjs/components/Modals/NewChatModal/ModalContent.js +1 -2
  26. package/lib/commonjs/components/Modals/NewChatModal/ModalContent.js.map +1 -1
  27. package/lib/commonjs/components/Modals/NewChatModal/NewChatModal.js +104 -202
  28. package/lib/commonjs/components/Modals/NewChatModal/NewChatModal.js.map +1 -1
  29. package/lib/commonjs/components/Modals/NewChatModal/UpdNewChatModal.js +0 -8
  30. package/lib/commonjs/components/Modals/NewChatModal/UpdNewChatModal.js.map +1 -1
  31. package/lib/commonjs/components/Modals/SelectUsersModal/SelectUsersModal.js +2 -4
  32. package/lib/commonjs/components/Modals/SelectUsersModal/SelectUsersModal.js.map +1 -1
  33. package/lib/commonjs/components/Modals/UserProfileModal/EditUserModal.js +21 -7
  34. package/lib/commonjs/components/Modals/UserProfileModal/EditUserModal.js.map +1 -1
  35. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js +244 -507
  36. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  37. package/lib/commonjs/components/Modals/UserSettingsModal/UserSettingsModal.js +46 -74
  38. package/lib/commonjs/components/Modals/UserSettingsModal/UserSettingsModal.js.map +1 -1
  39. package/lib/commonjs/components/Modals/styledModalComponents.js +2 -9
  40. package/lib/commonjs/components/Modals/styledModalComponents.js.map +1 -1
  41. package/lib/commonjs/components/RoomComponents/ChatRoomItem.js +7 -17
  42. package/lib/commonjs/components/RoomComponents/ChatRoomItem.js.map +1 -1
  43. package/lib/commonjs/components/styled/SendInput.js +11 -80
  44. package/lib/commonjs/components/styled/SendInput.js.map +1 -1
  45. package/lib/commonjs/components/styled/StyledComponents.js +3 -9
  46. package/lib/commonjs/components/styled/StyledComponents.js.map +1 -1
  47. package/lib/commonjs/components/styled/StyledInputComponents/StyledInputComponents.js +4 -11
  48. package/lib/commonjs/components/styled/StyledInputComponents/StyledInputComponents.js.map +1 -1
  49. package/lib/commonjs/context/xmppProvider.js +35 -6
  50. package/lib/commonjs/context/xmppProvider.js.map +1 -1
  51. package/lib/commonjs/helpers/updateMessagesTillLast.js +40 -12
  52. package/lib/commonjs/helpers/updateMessagesTillLast.js.map +1 -1
  53. package/lib/commonjs/hooks/useChatRoomFocus.js +9 -3
  54. package/lib/commonjs/hooks/useChatRoomFocus.js.map +1 -1
  55. package/lib/commonjs/hooks/useLogout.js +5 -0
  56. package/lib/commonjs/hooks/useLogout.js.map +1 -1
  57. package/lib/commonjs/hooks/useSendMessage.js +50 -23
  58. package/lib/commonjs/hooks/useSendMessage.js.map +1 -1
  59. package/lib/commonjs/i18n/strings.js +1 -121
  60. package/lib/commonjs/i18n/strings.js.map +1 -1
  61. package/lib/commonjs/main.js +0 -6
  62. package/lib/commonjs/main.js.map +1 -1
  63. package/lib/commonjs/networking/api-requests/user.api.js +0 -59
  64. package/lib/commonjs/networking/api-requests/user.api.js.map +1 -1
  65. package/lib/commonjs/networking/outboundQueue.js +18 -0
  66. package/lib/commonjs/networking/outboundQueue.js.map +1 -1
  67. package/lib/commonjs/networking/stanzaHandlers.js +10 -1
  68. package/lib/commonjs/networking/stanzaHandlers.js.map +1 -1
  69. package/lib/commonjs/networking/xmppClient.js +107 -7
  70. package/lib/commonjs/networking/xmppClient.js.map +1 -1
  71. package/lib/commonjs/roomStore/chatSettingsSlice.js +1 -3
  72. package/lib/commonjs/roomStore/chatSettingsSlice.js.map +1 -1
  73. package/lib/commonjs/roomStore/roomsSlice.js +22 -3
  74. package/lib/commonjs/roomStore/roomsSlice.js.map +1 -1
  75. package/lib/module/assets/icons.js +1 -6
  76. package/lib/module/assets/icons.js.map +1 -1
  77. package/lib/module/components/Header/HeaderRoomList.js +6 -20
  78. package/lib/module/components/Header/HeaderRoomList.js.map +1 -1
  79. package/lib/module/components/MainComponents/ChatRoom.js +14 -36
  80. package/lib/module/components/MainComponents/ChatRoom.js.map +1 -1
  81. package/lib/module/components/MainComponents/MessageList.js +24 -8
  82. package/lib/module/components/MainComponents/MessageList.js.map +1 -1
  83. package/lib/module/components/MainComponents/ProfileImagePlaceholder.js +13 -14
  84. package/lib/module/components/MainComponents/ProfileImagePlaceholder.js.map +1 -1
  85. package/lib/module/components/MainComponents/RoomList.js +32 -62
  86. package/lib/module/components/MainComponents/RoomList.js.map +1 -1
  87. package/lib/module/components/Menu/HeaderRoomListMenu.js +101 -311
  88. package/lib/module/components/Menu/HeaderRoomListMenu.js.map +1 -1
  89. package/lib/module/components/Modals/AttachSheet/AttachSheet.js +113 -311
  90. package/lib/module/components/Modals/AttachSheet/AttachSheet.js.map +1 -1
  91. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js +217 -555
  92. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  93. package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js +2 -6
  94. package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
  95. package/lib/module/components/Modals/Modal/Modal.js +1 -9
  96. package/lib/module/components/Modals/Modal/Modal.js.map +1 -1
  97. package/lib/module/components/Modals/NewChatModal/ModalContent.js +1 -2
  98. package/lib/module/components/Modals/NewChatModal/ModalContent.js.map +1 -1
  99. package/lib/module/components/Modals/NewChatModal/NewChatModal.js +106 -205
  100. package/lib/module/components/Modals/NewChatModal/NewChatModal.js.map +1 -1
  101. package/lib/module/components/Modals/NewChatModal/UpdNewChatModal.js +0 -8
  102. package/lib/module/components/Modals/NewChatModal/UpdNewChatModal.js.map +1 -1
  103. package/lib/module/components/Modals/SelectUsersModal/SelectUsersModal.js +2 -4
  104. package/lib/module/components/Modals/SelectUsersModal/SelectUsersModal.js.map +1 -1
  105. package/lib/module/components/Modals/UserProfileModal/EditUserModal.js +23 -9
  106. package/lib/module/components/Modals/UserProfileModal/EditUserModal.js.map +1 -1
  107. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js +248 -509
  108. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  109. package/lib/module/components/Modals/UserSettingsModal/UserSettingsModal.js +48 -76
  110. package/lib/module/components/Modals/UserSettingsModal/UserSettingsModal.js.map +1 -1
  111. package/lib/module/components/Modals/styledModalComponents.js +2 -9
  112. package/lib/module/components/Modals/styledModalComponents.js.map +1 -1
  113. package/lib/module/components/RoomComponents/ChatRoomItem.js +7 -17
  114. package/lib/module/components/RoomComponents/ChatRoomItem.js.map +1 -1
  115. package/lib/module/components/styled/SendInput.js +12 -81
  116. package/lib/module/components/styled/SendInput.js.map +1 -1
  117. package/lib/module/components/styled/StyledComponents.js +3 -9
  118. package/lib/module/components/styled/StyledComponents.js.map +1 -1
  119. package/lib/module/components/styled/StyledInputComponents/StyledInputComponents.js +4 -11
  120. package/lib/module/components/styled/StyledInputComponents/StyledInputComponents.js.map +1 -1
  121. package/lib/module/context/xmppProvider.js +35 -6
  122. package/lib/module/context/xmppProvider.js.map +1 -1
  123. package/lib/module/helpers/updateMessagesTillLast.js +41 -13
  124. package/lib/module/helpers/updateMessagesTillLast.js.map +1 -1
  125. package/lib/module/hooks/useChatRoomFocus.js +9 -3
  126. package/lib/module/hooks/useChatRoomFocus.js.map +1 -1
  127. package/lib/module/hooks/useLogout.js +5 -0
  128. package/lib/module/hooks/useLogout.js.map +1 -1
  129. package/lib/module/hooks/useSendMessage.js +51 -24
  130. package/lib/module/hooks/useSendMessage.js.map +1 -1
  131. package/lib/module/i18n/strings.js +1 -121
  132. package/lib/module/i18n/strings.js.map +1 -1
  133. package/lib/module/main.js +1 -1
  134. package/lib/module/main.js.map +1 -1
  135. package/lib/module/networking/api-requests/user.api.js +0 -56
  136. package/lib/module/networking/api-requests/user.api.js.map +1 -1
  137. package/lib/module/networking/outboundQueue.js +17 -0
  138. package/lib/module/networking/outboundQueue.js.map +1 -1
  139. package/lib/module/networking/stanzaHandlers.js +10 -1
  140. package/lib/module/networking/stanzaHandlers.js.map +1 -1
  141. package/lib/module/networking/xmppClient.js +107 -7
  142. package/lib/module/networking/xmppClient.js.map +1 -1
  143. package/lib/module/roomStore/chatSettingsSlice.js +1 -3
  144. package/lib/module/roomStore/chatSettingsSlice.js.map +1 -1
  145. package/lib/module/roomStore/roomsSlice.js +20 -2
  146. package/lib/module/roomStore/roomsSlice.js.map +1 -1
  147. package/lib/typescript/assets/icons.d.ts +0 -16
  148. package/lib/typescript/assets/icons.d.ts.map +1 -1
  149. package/lib/typescript/components/Header/HeaderRoomList.d.ts.map +1 -1
  150. package/lib/typescript/components/MainComponents/ChatRoom.d.ts.map +1 -1
  151. package/lib/typescript/components/MainComponents/MessageList.d.ts.map +1 -1
  152. package/lib/typescript/components/MainComponents/ProfileImagePlaceholder.d.ts.map +1 -1
  153. package/lib/typescript/components/MainComponents/RoomList.d.ts.map +1 -1
  154. package/lib/typescript/components/Menu/HeaderRoomListMenu.d.ts +0 -3
  155. package/lib/typescript/components/Menu/HeaderRoomListMenu.d.ts.map +1 -1
  156. package/lib/typescript/components/Modals/AttachSheet/AttachSheet.d.ts +0 -9
  157. package/lib/typescript/components/Modals/AttachSheet/AttachSheet.d.ts.map +1 -1
  158. package/lib/typescript/components/Modals/ChatProfileModal/ChatProfileModal.d.ts +0 -10
  159. package/lib/typescript/components/Modals/ChatProfileModal/ChatProfileModal.d.ts.map +1 -1
  160. package/lib/typescript/components/Modals/FilePreviewModal/FilePreviewModal.d.ts.map +1 -1
  161. package/lib/typescript/components/Modals/Modal/Modal.d.ts.map +1 -1
  162. package/lib/typescript/components/Modals/NewChatModal/ModalContent.d.ts +2 -4
  163. package/lib/typescript/components/Modals/NewChatModal/ModalContent.d.ts.map +1 -1
  164. package/lib/typescript/components/Modals/NewChatModal/NewChatModal.d.ts +1 -1
  165. package/lib/typescript/components/Modals/NewChatModal/NewChatModal.d.ts.map +1 -1
  166. package/lib/typescript/components/Modals/NewChatModal/UpdNewChatModal.d.ts.map +1 -1
  167. package/lib/typescript/components/Modals/SelectUsersModal/SelectUsersModal.d.ts +1 -6
  168. package/lib/typescript/components/Modals/SelectUsersModal/SelectUsersModal.d.ts.map +1 -1
  169. package/lib/typescript/components/Modals/UserProfileModal/EditUserModal.d.ts.map +1 -1
  170. package/lib/typescript/components/Modals/UserProfileModal/UserProfileModal.d.ts +0 -16
  171. package/lib/typescript/components/Modals/UserProfileModal/UserProfileModal.d.ts.map +1 -1
  172. package/lib/typescript/components/Modals/UserSettingsModal/UserSettingsModal.d.ts +0 -1
  173. package/lib/typescript/components/Modals/UserSettingsModal/UserSettingsModal.d.ts.map +1 -1
  174. package/lib/typescript/components/Modals/styledModalComponents.d.ts.map +1 -1
  175. package/lib/typescript/components/RoomComponents/ChatRoomItem.d.ts.map +1 -1
  176. package/lib/typescript/components/styled/SendInput.d.ts.map +1 -1
  177. package/lib/typescript/components/styled/StyledComponents.d.ts.map +1 -1
  178. package/lib/typescript/components/styled/StyledInputComponents/StyledInputComponents.d.ts.map +1 -1
  179. package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
  180. package/lib/typescript/helpers/updateMessagesTillLast.d.ts.map +1 -1
  181. package/lib/typescript/hooks/useChatRoomFocus.d.ts.map +1 -1
  182. package/lib/typescript/hooks/useLogout.d.ts.map +1 -1
  183. package/lib/typescript/hooks/useSendMessage.d.ts.map +1 -1
  184. package/lib/typescript/i18n/strings.d.ts.map +1 -1
  185. package/lib/typescript/main.d.ts +1 -1
  186. package/lib/typescript/main.d.ts.map +1 -1
  187. package/lib/typescript/networking/api-requests/user.api.d.ts +0 -27
  188. package/lib/typescript/networking/api-requests/user.api.d.ts.map +1 -1
  189. package/lib/typescript/networking/outboundQueue.d.ts +16 -0
  190. package/lib/typescript/networking/outboundQueue.d.ts.map +1 -1
  191. package/lib/typescript/networking/stanzaHandlers.d.ts.map +1 -1
  192. package/lib/typescript/networking/xmppClient.d.ts +33 -1
  193. package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
  194. package/lib/typescript/roomStore/chatSettingsSlice.d.ts.map +1 -1
  195. package/lib/typescript/roomStore/roomsSlice.d.ts +18 -1
  196. package/lib/typescript/roomStore/roomsSlice.d.ts.map +1 -1
  197. package/lib/typescript/types/models/room.model.d.ts +15 -0
  198. package/lib/typescript/types/models/room.model.d.ts.map +1 -1
  199. package/lib/typescript/types/types.d.ts +20 -20
  200. package/lib/typescript/types/types.d.ts.map +1 -1
  201. package/package.json +1 -6
  202. package/src/assets/icons.tsx +9 -48
  203. package/src/components/Header/HeaderRoomList.tsx +5 -20
  204. package/src/components/MainComponents/ChatRoom.tsx +20 -38
  205. package/src/components/MainComponents/MessageList.tsx +24 -8
  206. package/src/components/MainComponents/ProfileImagePlaceholder.tsx +14 -21
  207. package/src/components/MainComponents/RoomList.tsx +34 -65
  208. package/src/components/Menu/HeaderRoomListMenu.tsx +71 -310
  209. package/src/components/Modals/AttachSheet/AttachSheet.tsx +97 -331
  210. package/src/components/Modals/ChatProfileModal/ChatProfileModal.tsx +279 -598
  211. package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +2 -4
  212. package/src/components/Modals/Modal/Modal.tsx +0 -7
  213. package/src/components/Modals/NewChatModal/ModalContent.tsx +3 -6
  214. package/src/components/Modals/NewChatModal/NewChatModal.tsx +117 -219
  215. package/src/components/Modals/NewChatModal/UpdNewChatModal.tsx +1 -12
  216. package/src/components/Modals/SelectUsersModal/SelectUsersModal.tsx +4 -14
  217. package/src/components/Modals/UserProfileModal/EditUserModal.tsx +25 -9
  218. package/src/components/Modals/UserProfileModal/UserProfileModal.tsx +307 -617
  219. package/src/components/Modals/UserSettingsModal/UserSettingsModal.tsx +58 -75
  220. package/src/components/Modals/styledModalComponents.tsx +2 -9
  221. package/src/components/RoomComponents/ChatRoomItem.tsx +7 -14
  222. package/src/components/styled/SendInput.tsx +12 -83
  223. package/src/components/styled/StyledComponents.ts +3 -9
  224. package/src/components/styled/StyledInputComponents/StyledInputComponents.tsx +4 -11
  225. package/src/context/xmppProvider.tsx +41 -8
  226. package/src/helpers/updateMessagesTillLast.tsx +42 -17
  227. package/src/hooks/useChatRoomFocus.ts +11 -3
  228. package/src/hooks/useLogout.tsx +7 -0
  229. package/src/hooks/useSendMessage.tsx +60 -24
  230. package/src/i18n/strings.ts +1 -121
  231. package/src/main.ts +1 -1
  232. package/src/networking/api-requests/user.api.ts +0 -71
  233. package/src/networking/outboundQueue.ts +29 -0
  234. package/src/networking/stanzaHandlers.ts +9 -1
  235. package/src/networking/xmppClient.ts +118 -7
  236. package/src/roomStore/chatSettingsSlice.ts +1 -3
  237. package/src/roomStore/roomsSlice.ts +20 -2
  238. package/src/types/models/room.model.ts +15 -0
  239. package/src/types/types.ts +20 -22
  240. package/lib/commonjs/components/Modals/ChatProfileModal/ReportChatModal.js +0 -226
  241. package/lib/commonjs/components/Modals/ChatProfileModal/ReportChatModal.js.map +0 -1
  242. package/lib/commonjs/components/Modals/ProfileHeader/ProfileHeader.js +0 -556
  243. package/lib/commonjs/components/Modals/ProfileHeader/ProfileHeader.js.map +0 -1
  244. package/lib/commonjs/components/Modals/ProfileHeader/ProfileMenu.js +0 -210
  245. package/lib/commonjs/components/Modals/ProfileHeader/ProfileMenu.js.map +0 -1
  246. package/lib/commonjs/helpers/getChatBackground.js +0 -23
  247. package/lib/commonjs/helpers/getChatBackground.js.map +0 -1
  248. package/lib/commonjs/helpers/mediaLibraryRuntime.js +0 -31
  249. package/lib/commonjs/helpers/mediaLibraryRuntime.js.map +0 -1
  250. package/lib/commonjs/helpers/pickImageAsset.js +0 -68
  251. package/lib/commonjs/helpers/pickImageAsset.js.map +0 -1
  252. package/lib/commonjs/helpers/sheetGestures.js +0 -32
  253. package/lib/commonjs/helpers/sheetGestures.js.map +0 -1
  254. package/lib/module/components/Modals/ChatProfileModal/ReportChatModal.js +0 -223
  255. package/lib/module/components/Modals/ChatProfileModal/ReportChatModal.js.map +0 -1
  256. package/lib/module/components/Modals/ProfileHeader/ProfileHeader.js +0 -548
  257. package/lib/module/components/Modals/ProfileHeader/ProfileHeader.js.map +0 -1
  258. package/lib/module/components/Modals/ProfileHeader/ProfileMenu.js +0 -205
  259. package/lib/module/components/Modals/ProfileHeader/ProfileMenu.js.map +0 -1
  260. package/lib/module/helpers/getChatBackground.js +0 -18
  261. package/lib/module/helpers/getChatBackground.js.map +0 -1
  262. package/lib/module/helpers/mediaLibraryRuntime.js +0 -25
  263. package/lib/module/helpers/mediaLibraryRuntime.js.map +0 -1
  264. package/lib/module/helpers/pickImageAsset.js +0 -63
  265. package/lib/module/helpers/pickImageAsset.js.map +0 -1
  266. package/lib/module/helpers/sheetGestures.js +0 -26
  267. package/lib/module/helpers/sheetGestures.js.map +0 -1
  268. package/lib/typescript/components/Modals/ChatProfileModal/ReportChatModal.d.ts +0 -9
  269. package/lib/typescript/components/Modals/ChatProfileModal/ReportChatModal.d.ts.map +0 -1
  270. package/lib/typescript/components/Modals/ProfileHeader/ProfileHeader.d.ts +0 -75
  271. package/lib/typescript/components/Modals/ProfileHeader/ProfileHeader.d.ts.map +0 -1
  272. package/lib/typescript/components/Modals/ProfileHeader/ProfileMenu.d.ts +0 -25
  273. package/lib/typescript/components/Modals/ProfileHeader/ProfileMenu.d.ts.map +0 -1
  274. package/lib/typescript/helpers/getChatBackground.d.ts +0 -15
  275. package/lib/typescript/helpers/getChatBackground.d.ts.map +0 -1
  276. package/lib/typescript/helpers/mediaLibraryRuntime.d.ts +0 -3
  277. package/lib/typescript/helpers/mediaLibraryRuntime.d.ts.map +0 -1
  278. package/lib/typescript/helpers/pickImageAsset.d.ts +0 -27
  279. package/lib/typescript/helpers/pickImageAsset.d.ts.map +0 -1
  280. package/lib/typescript/helpers/sheetGestures.d.ts +0 -20
  281. package/lib/typescript/helpers/sheetGestures.d.ts.map +0 -1
  282. package/src/components/Modals/ChatProfileModal/ReportChatModal.tsx +0 -244
  283. package/src/components/Modals/ProfileHeader/ProfileHeader.tsx +0 -615
  284. package/src/components/Modals/ProfileHeader/ProfileMenu.tsx +0 -217
  285. package/src/helpers/getChatBackground.ts +0 -18
  286. package/src/helpers/mediaLibraryRuntime.ts +0 -25
  287. package/src/helpers/pickImageAsset.ts +0 -68
  288. package/src/helpers/sheetGestures.ts +0 -25
@@ -1,16 +1,18 @@
1
- /** @format */
2
-
3
1
  import React, { useCallback, useMemo } from 'react';
4
- import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
5
- import { useDispatch } from 'react-redux';
6
- import { ModalContainerFullScreen } from '../styledModalComponents';
2
+
3
+ import {
4
+ ModalContainerFullScreen,
5
+ Viewider,
6
+ Label,
7
+ LabelData,
8
+ BorderedContainer,
9
+ CenterContainer,
10
+ } from '../styledModalComponents';
7
11
  import ModalHeaderComponent from '../ModalHeaderComponent';
8
- import { ArowDownIcon } from '../../../assets/icons';
12
+ import Button from '../../styled/Button';
13
+ import { useDispatch } from 'react-redux';
9
14
  import { setActiveModal } from '../../../roomStore/chatSettingsSlice';
10
15
  import { MODAL_TYPES } from '../../../helpers/constants/MODAL_TYPES';
11
- import { useChatSettingState } from '../../../hooks/useChatSettingState';
12
- import { chatTextStyle } from '../../../helpers/typography';
13
- import { useT } from '../../../i18n/useT';
14
16
 
15
17
  interface UserSettingsModalProps {
16
18
  handleCloseModal: any;
@@ -19,84 +21,65 @@ interface UserSettingsModalProps {
19
21
  const UserSettingsModal: React.FC<UserSettingsModalProps> = ({
20
22
  handleCloseModal,
21
23
  }) => {
22
- const t = useT();
23
- const dispatch = useDispatch();
24
- const { config } = useChatSettingState();
25
-
26
24
  const options = useMemo(
27
25
  () => [
28
- { label: t('settings.manageData.title'), key: MODAL_TYPES.MANAGE_DATA },
29
- { label: t('settings.visibility.title'), key: MODAL_TYPES.VISIBILITY },
26
+ { value: 'Manage Data', key: MODAL_TYPES.MANAGE_DATA },
27
+ { value: 'Visiblility', key: MODAL_TYPES.VISIBILITY },
28
+ // { value: 'Profile Shares', key: MODAL_TYPES.PROFILE_SHARES },
29
+ // { value: 'Document Shares', key: MODAL_TYPES.DOCUMENT_SHARES },
30
+ // { value: 'Blocked Users', key: MODAL_TYPES.BLOCKED_USERS },
31
+ // { value: 'Referrals', key: MODAL_TYPES.REFERRALS },
30
32
  ],
31
- [t]
33
+ []
32
34
  );
33
35
 
34
- const handleClick = useCallback(
35
- (key: string) => {
36
- dispatch(setActiveModal(key as any));
37
- },
38
- [dispatch]
39
- );
36
+ const dispatch = useDispatch();
37
+
38
+ const handleClick = useCallback((key: string) => {
39
+ dispatch(setActiveModal(key as any));
40
+ }, []);
40
41
 
41
42
  return (
42
- <ModalContainerFullScreen style={styles.screen}>
43
+ <ModalContainerFullScreen>
43
44
  <ModalHeaderComponent
44
45
  handleCloseModal={handleCloseModal}
45
- headerTitle={t('settings.menu.title')}
46
- titleStyle={chatTextStyle(config?.typography?.profile?.screenTitle)}
46
+ headerTitle={'Settings'}
47
47
  />
48
- {/* One card per entry, rather than a single bordered block with
49
- * hairline dividers — the rows are separate destinations, and the
50
- * design gives each its own surface. */}
51
- <View style={styles.body}>
52
- {options.map((option) => (
53
- <TouchableOpacity
54
- key={option.key}
55
- testID={`settings-row-${option.key}`}
56
- activeOpacity={0.7}
57
- style={styles.card}
58
- onPress={() => handleClick(option.key)}
59
- >
60
- <Text style={styles.label}>{option.label}</Text>
61
- <ArowDownIcon color="#8C8C8C" width={20} height={20} style={styles.chevron} />
62
- </TouchableOpacity>
63
- ))}
64
- </View>
48
+ <CenterContainer>
49
+ <BorderedContainer
50
+ style={{
51
+ padding: 16,
52
+ display: 'flex',
53
+ flexDirection: 'column',
54
+ gap: 8,
55
+ }}
56
+ >
57
+ {options.map((option: { value: string; key: string }, index) => (
58
+ <>
59
+ <Button
60
+ variant="default"
61
+ style={{
62
+ minHeight: 40,
63
+ display: 'flex',
64
+ flexDirection: 'column',
65
+ gap: 2,
66
+ width: '100%',
67
+ justifyContent: 'center',
68
+ borderRadius: 0,
69
+ alignItems: 'flex-start',
70
+ }}
71
+ onPress={() => handleClick(option.key)}
72
+ >
73
+ <Label style={{ textAlign: 'center' }}>{option.value}</Label>
74
+ {[2, 3, 4].includes(index) && <LabelData>0</LabelData>}
75
+ </Button>
76
+ {index < 5 && <Viewider />}
77
+ </>
78
+ ))}
79
+ </BorderedContainer>
80
+ </CenterContainer>
65
81
  </ModalContainerFullScreen>
66
82
  );
67
83
  };
68
84
 
69
- const styles = StyleSheet.create({
70
- screen: {
71
- backgroundColor: '#E8EDF2',
72
- },
73
- body: {
74
- width: '100%',
75
- padding: 12,
76
- gap: 12,
77
- },
78
- card: {
79
- flexDirection: 'row',
80
- alignItems: 'center',
81
- justifyContent: 'space-between',
82
- backgroundColor: '#FFFFFF',
83
- borderRadius: 20,
84
- paddingHorizontal: 20,
85
- paddingVertical: 22,
86
- shadowColor: '#101828',
87
- shadowOffset: { width: 0, height: 4 },
88
- shadowOpacity: 0.06,
89
- shadowRadius: 12,
90
- elevation: 3,
91
- },
92
- label: {
93
- fontSize: 17,
94
- fontWeight: '500',
95
- color: '#141414',
96
- },
97
- chevron: {
98
- transform: [{ rotate: '-90deg' }],
99
- },
100
- });
101
-
102
85
  export default UserSettingsModal;
@@ -86,15 +86,8 @@ export const HeaderContainer = styled.View`
86
86
  * button and 8px below — properly sized and properly centered. */
87
87
  padding: 8px 16px;
88
88
  background-color: #fff;
89
- /* Same card treatment as the room-list header: rounded bottom corners
90
- * and a soft drop shadow instead of a hairline rule. */
91
- border-bottom-left-radius: 20px;
92
- border-bottom-right-radius: 20px;
93
- shadow-color: #101828;
94
- shadow-offset: 0px 4px;
95
- shadow-opacity: 0.06;
96
- shadow-radius: 12px;
97
- elevation: 4;
89
+ border-bottom-width: 1px;
90
+ border-bottom-color: #f0f0f0;
98
91
  z-index: 1;
99
92
  flex-direction: row;
100
93
  justify-content: space-between;
@@ -90,13 +90,11 @@ const ChatRoomItem: React.FC<ChatRoomItemProps> = ({
90
90
 
91
91
  return (
92
92
  <ChatItem key={index}>
93
- <View pointerEvents="none">
94
- <ProfileImagePlaceholder
95
- name={displayName}
96
- icon={appendFileToken(chat?.icon, fileToken)}
97
- active={false}
98
- />
99
- </View>
93
+ <ProfileImagePlaceholder
94
+ name={displayName}
95
+ icon={appendFileToken(chat?.icon, fileToken)}
96
+ active={false}
97
+ />
100
98
  <View
101
99
  style={{
102
100
  flexDirection: 'column',
@@ -106,9 +104,7 @@ const ChatRoomItem: React.FC<ChatRoomItemProps> = ({
106
104
  paddingVertical: 8,
107
105
  paddingRight: 8,
108
106
  borderBottomWidth: 1,
109
- // Reads against the list's #E8EDF2 ground; #F0F0F0 was lighter
110
- // than the ground itself, so the row separators vanished.
111
- borderBottomColor: '#C6CFDA',
107
+ borderBottomColor: '#F0F0F0',
112
108
  }}
113
109
  >
114
110
  <View
@@ -219,11 +215,8 @@ const ChatRoomItem: React.FC<ChatRoomItemProps> = ({
219
215
  ) : null}
220
216
  </View>
221
217
  {isDriver && (
222
- // Darker than the list's own ground (#E8EDF2), otherwise the
223
- // separator disappears into it — white and the old #0052CD0D
224
- // hairline both did.
225
218
  <View
226
- style={{ height: 1, backgroundColor: '#C6CFDA', marginTop: 8 }}
219
+ style={{ height: 1, backgroundColor: '#0052CD0D', marginTop: 8 }}
227
220
  />
228
221
  )}
229
222
  </View>
@@ -10,16 +10,14 @@ import { IConfig, MediaFile } from '../../types/types';
10
10
  import Button from './Button';
11
11
  import { SendIcon, AttachIcon, RecordIcon } from '../../assets/icons';
12
12
  import { KeyboardAvoidingView, Platform, View, Text, TouchableOpacity, Alert, Linking } from 'react-native';
13
- import AttachSheet, { PickedMedia } from '../Modals/AttachSheet/AttachSheet';
13
+ import AttachSheet from '../Modals/AttachSheet/AttachSheet';
14
14
  import { MediaFilePreview } from './MediaFilePreview';
15
15
  import { getIconColor } from '../../helpers/getIconColor';
16
16
  import { getElementFont } from '../../helpers/getElementFont';
17
17
  import { useT } from '../../i18n/useT';
18
- import { getMediaLibrary } from '../../helpers/mediaLibraryRuntime';
19
18
  import * as ImagePicker from 'expo-image-picker';
20
19
  import * as DocumentPicker from 'expo-document-picker';
21
20
  import * as ImageManipulator from 'expo-image-manipulator';
22
- import * as FileSystem from 'expo-file-system/legacy';
23
21
  // expo-audio replaces the discontinued expo-av: `useAudioRecorder` owns a
24
22
  // single reusable recorder for this input's lifetime (prepare → record →
25
23
  // stop → prepare again), and the module-level helpers cover permissions
@@ -261,59 +259,22 @@ const SendInput: React.FC<SendInputProps> = ({
261
259
  }
262
260
  };
263
261
 
264
- // A tap on a thumbnail in the attach sheet's recents strip. The sheet
265
- // already resolved the asset to a real on-disk uri (iOS `ph://` ids are
266
- // not uploadable); here we apply the same treatment the full-blown
267
- // pickers get — HEIC→JPEG for images, and a size probe so the 50 MB cap
268
- // is enforced before anything hits the network.
269
- const handleRecentMediaSelection = async (media: PickedMedia) => {
270
- try {
271
- let size: number | undefined;
272
- try {
273
- const info = await FileSystem.getInfoAsync(media.uri);
274
- if (info.exists && typeof (info as any).size === 'number') {
275
- size = (info as any).size;
276
- }
277
- } catch {
278
- // Size is a nice-to-have; the backend still rejects oversized bodies.
279
- }
280
-
281
- if (media.isVideo) {
282
- handleFileSelect([
283
- {
284
- uri: media.uri,
285
- type: media.mimeType || 'video/mp4',
286
- name: media.name,
287
- size,
288
- },
289
- ]);
290
- return;
291
- }
292
-
293
- const normalized = await normalizeImageAsset({
294
- uri: media.uri,
295
- mimeType: media.mimeType,
296
- fileName: media.name,
297
- } as ImagePicker.ImagePickerAsset);
298
- handleFileSelect([
299
- {
300
- uri: normalized.uri,
301
- type: normalized.mime,
302
- name: normalized.name,
303
- size,
304
- },
305
- ]);
306
- } catch (error) {
307
- console.error('Recent media error:', error);
308
- }
309
- };
310
-
311
262
  const handleFileSelection = async () => {
312
263
  // Drop overlapping invocations — a second getDocumentAsync while the
313
264
  // first is still presented throws PickingInProgressException on iOS.
314
265
  if (pickingDocRef.current) {return;}
315
266
  pickingDocRef.current = true;
316
267
  try {
268
+ // Let the attach-sheet's modal finish its NATIVE dismissal before we
269
+ // present the picker. The sheet fires this handler right after its
270
+ // slide-out animation, but the underlying RN Modal view-controller
271
+ // dismissal is async — presenting the document picker over a still-
272
+ // dismissing modal makes iOS tear it down without a cancel callback,
273
+ // which strands expo-document-picker's pickingContext and makes every
274
+ // subsequent open throw "Different document picking in progress".
275
+ if (Platform.OS === 'ios') {
276
+ await new Promise((resolve) => setTimeout(resolve, 350));
277
+ }
317
278
  const result = await DocumentPicker.getDocumentAsync({
318
279
  type: '*/*',
319
280
  multiple: false,
@@ -336,39 +297,8 @@ const SendInput: React.FC<SendInputProps> = ({
336
297
  }
337
298
  };
338
299
 
339
- // Photo-library access is requested HERE, not inside the sheet: iOS
340
- // cannot present the permission alert while an RN <Modal> owns the
341
- // screen (the same UIKit limitation that used to swallow the pickers),
342
- // so asking from inside resolved to a silent "denied" and the recents
343
- // strip stayed empty forever. Asking on the paperclip tap puts the
344
- // system alert over the plain chat screen, where it can actually show.
345
- // Only the first tap ever waits on it — afterwards the status is
346
- // already determined and `getPermissionsAsync` returns immediately.
347
- const ensureLibraryPermission = async () => {
348
- const MediaLibrary = getMediaLibrary();
349
- if (!MediaLibrary?.getPermissionsAsync) {return;}
350
- try {
351
- const perm = await MediaLibrary.getPermissionsAsync();
352
- if (perm?.granted || perm?.canAskAgain === false) {return;}
353
- await MediaLibrary.requestPermissionsAsync();
354
- } catch (err) {
355
- // Optional peer / unsupported platform: the sheet just shows the
356
- // camera tile and the full-library route.
357
- console.warn('Media library permission check failed:', err);
358
- }
359
- };
360
-
361
- // Only the very first tap of a session goes through the permission
362
- // check; afterwards the sheet opens with zero added latency.
363
- const libraryPermissionCheckedRef = useRef(false);
364
-
365
300
  const handleAttachPress = () => {
366
- if (libraryPermissionCheckedRef.current) {
367
- setShowMediaMenu(true);
368
- return;
369
- }
370
- libraryPermissionCheckedRef.current = true;
371
- ensureLibraryPermission().finally(() => setShowMediaMenu(true));
301
+ setShowMediaMenu(true);
372
302
  };
373
303
 
374
304
  // ─── Voice recording ──────────────────────────────────────────────
@@ -772,7 +702,6 @@ const SendInput: React.FC<SendInputProps> = ({
772
702
  onCamera={handleCameraSelection}
773
703
  onGallery={handleGallerySelection}
774
704
  onDocument={handleFileSelection}
775
- onPickMedia={handleRecentMediaSelection}
776
705
  primaryColor={getIconColor(config)}
777
706
  />
778
707
  </InputContainer>
@@ -28,15 +28,9 @@ export const ChatContainerHeader = styled.View`
28
28
  background-color: #ffffff;
29
29
  align-items: center;
30
30
  justify-content: space-between;
31
- /* Rounded bottom corners + a soft drop shadow instead of the hairline
32
- * rule, so the header reads as a card over the conversation. */
33
- border-bottom-left-radius: 20px;
34
- border-bottom-right-radius: 20px;
35
- shadow-color: #101828;
36
- shadow-offset: 0px 4px;
37
- shadow-opacity: 0.06;
38
- shadow-radius: 12px;
39
- elevation: 4;
31
+ border-bottom-width: 1px;
32
+ border-bottom-color: #e5e7eb;
33
+ border-style: solid;
40
34
  z-index: 1000;
41
35
  `;
42
36
 
@@ -13,21 +13,14 @@ const colors = {
13
13
 
14
14
  export const InputContainer = styled.View<{ isText?: boolean }>`
15
15
  flex-direction: column;
16
- padding: 12px 16px 8px 16px;
17
- /* Transparent, so the dock's white surface — and its rounded top
18
- * corners — are what shows. An opaque child painted straight over those
19
- * corners (a parent's border radius does not clip children without
20
- * overflow:hidden, and that would clip the dock's shadow too). */
21
- background-color: transparent;
16
+ padding: 8px 16px 4px 16px;
17
+ background-color: ${colors.white};
22
18
  z-index: 100;
23
19
  width: 100%;
24
20
  bottom: 0;
25
21
  left: 0;
26
- /* No radius or shadow here: the dock this sits in (ChatRoom's
27
- * InputDockTag) is the composer's outer surface and carries both. Having
28
- * them on this inner view too drew its shadow onto the dock's white
29
- * background — the faint seam that made the strip below look like a
30
- * different colour. */
22
+ border-top-width: 1px;
23
+ border-top-color: #e5e7eb;
31
24
  `;
32
25
 
33
26
  export const MediaContainer = styled.View`
@@ -557,7 +557,9 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config, is
557
557
  devPushLog('rn', `watchdog: client ${client.status} → forceReconnect`);
558
558
  client.forceReconnect();
559
559
  } else {
560
- client.ensureStreamAlive?.();
560
+ // The periodic health check is the probe's primary owner, so it
561
+ // bypasses the throttle that keeps send-path probes rare.
562
+ client.ensureStreamAlive?.({ force: true, reason: 'watchdog' });
561
563
  }
562
564
  }, 30000);
563
565
  return () => clearInterval(id);
@@ -622,9 +624,19 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config, is
622
624
  // the provider so consumers using the component as a package get
623
625
  // correct unread without reaching into the chat store themselves.
624
626
  visibleBeforeBackground = visibleRoomJID;
627
+ // Prefer the read boundary the user actually reached. Stamping
628
+ // `Date.now()` while they are scrolled up marks unseen messages as
629
+ // read, and because we flush to the server private store below the
630
+ // loss survives an app restart. Customer-reported #33.
631
+ const bgBoundaryTs = visibleRoomJID
632
+ ? rooms?.[visibleRoomJID]?.readBoundaryTs ?? null
633
+ : null;
625
634
  if (visibleRoomJID) {
626
635
  store.dispatch(
627
- setLastViewedTimestamp({ chatJID: visibleRoomJID, timestamp: Date.now() })
636
+ setLastViewedTimestamp({
637
+ chatJID: visibleRoomJID,
638
+ timestamp: bgBoundaryTs ?? Date.now(),
639
+ })
628
640
  );
629
641
  // Drop the "New messages" divider for the room we're leaving so it
630
642
  // doesn't linger when the user comes back (mirrors ChatRoom's
@@ -636,9 +648,10 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config, is
636
648
  }
637
649
  // Fire-and-forget — we're going to the background and don't
638
650
  // care about the resolution path.
639
- c.flushLastViewedToPrivateStoreStanza(rooms, { visibleRoomJID }).catch(
640
- () => {}
641
- );
651
+ c.flushLastViewedToPrivateStoreStanza(rooms, {
652
+ visibleRoomJID,
653
+ visibleRoomTs: bgBoundaryTs,
654
+ }).catch(() => {});
642
655
  });
643
656
  return () => sub.remove();
644
657
  }, [client]);
@@ -669,9 +682,16 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config, is
669
682
  store.dispatch(setVisibleRoom({ roomJID: activeRoomJID }));
670
683
  }
671
684
  } else {
685
+ // Same read-boundary rule as the AppState path above (#33).
686
+ const hideBoundaryTs = activeRoomJID
687
+ ? rooms?.[activeRoomJID]?.readBoundaryTs ?? null
688
+ : null;
672
689
  if (wasVisible && activeRoomJID) {
673
690
  store.dispatch(
674
- setLastViewedTimestamp({ chatJID: activeRoomJID, timestamp: Date.now() })
691
+ setLastViewedTimestamp({
692
+ chatJID: activeRoomJID,
693
+ timestamp: hideBoundaryTs ?? Date.now(),
694
+ })
675
695
  );
676
696
  // Drop the "New messages" divider so it's gone when the user
677
697
  // returns to the chat (mirrors ChatRoom's unmount cleanup).
@@ -683,7 +703,10 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config, is
683
703
  store.dispatch(clearVisibleRoom());
684
704
  if (wasVisible && client?.flushLastViewedToPrivateStoreStanza) {
685
705
  client
686
- .flushLastViewedToPrivateStoreStanza(rooms, { visibleRoomJID: activeRoomJID })
706
+ .flushLastViewedToPrivateStoreStanza(rooms, {
707
+ visibleRoomJID: activeRoomJID,
708
+ visibleRoomTs: hideBoundaryTs,
709
+ })
687
710
  .catch(() => {});
688
711
  }
689
712
  }
@@ -704,6 +727,15 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config, is
704
727
  if (room.messages === lastMessagesRef) {return;}
705
728
  lastMessagesRef = room.messages;
706
729
 
730
+ // "Chat is on screen" does NOT mean "user is looking at the newest
731
+ // message". While they are scrolled up, MessageList publishes a read
732
+ // boundary; advancing the marker to `now` here would clear the very
733
+ // unread messages the scroll-up state exists to preserve — and the
734
+ // 2s flush below would push that loss to the server, making it
735
+ // survive an app restart. Leave the marker alone until they come
736
+ // back to the bottom (boundary back to null). Customer-reported #33.
737
+ if (room.readBoundaryTs != null) {return;}
738
+
707
739
  let newest = 0;
708
740
  const list = room.messages || [];
709
741
  for (const m of list) {
@@ -762,7 +794,8 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config, is
762
794
  devPushLog('rn', `NetInfo: reachable & client ${client.status} → forceReconnect`);
763
795
  client.forceReconnect();
764
796
  } else if (client.status === 'online') {
765
- client.ensureStreamAlive?.();
797
+ // Connectivity just came back — worth an immediate check.
798
+ client.ensureStreamAlive?.({ force: true, reason: 'netinfo' });
766
799
  }
767
800
  });
768
801
  return () => unsub();
@@ -1,6 +1,6 @@
1
1
  import XmppClient from '../networking/xmppClient';
2
2
  import { store } from '../roomStore';
3
- import { setRoomMessages } from '../roomStore/roomsSlice';
3
+ import { applyRoomsPreloadBatch } from '../roomStore/roomsSlice';
4
4
  import { IMessage, IRoom } from '../types/types';
5
5
  import { checkUniqueUsers } from './checkUniqueUsers';
6
6
 
@@ -76,22 +76,47 @@ export const updateMessagesTillLast = async (
76
76
  Number(message.id) === Number(lastCachedMessagesTimeStamp)
77
77
  );
78
78
 
79
- if (!isMessageFound && !(counter <= maxFetchAttempts - 1)) {
80
- // Fire-and-forget — checkUniqueUsers resolves to an
81
- // updated RoomMember list, but there's no slice
82
- // action wired to receive it on RN yet (the web SDK
83
- // has one; the port left a TODO). Keep the call so
84
- // the API request goes out (it warms the cache for
85
- // later renders) but stop short of dispatching.
86
- await checkUniqueUsers(currentJidNewMessages);
87
-
88
- store.dispatch(
89
- setRoomMessages({
90
- roomJID: jid,
91
- messages: currentJidNewMessages,
92
- })
93
- );
94
- }
79
+ }
80
+
81
+ // Publish whatever the catch-up actually retrieved.
82
+ //
83
+ // This used to sit INSIDE the loop behind
84
+ // `!isMessageFound && !(counter <= maxFetchAttempts - 1)`,
85
+ // i.e. it only ran when the walk-back gave up. In the common
86
+ // case — the gap is small and the anchor is found on the first
87
+ // fetch — `isMessageFound` flips true immediately and NOTHING
88
+ // was ever dispatched, so the reconnect catch-up silently
89
+ // dropped its own result. The messages still appeared, but only
90
+ // as a side effect of the always-on `onMessageHistory` stanza
91
+ // handler, which parses the same stanza a different way
92
+ // (positional form, no explicit `user` object) and inserts via
93
+ // addRoomMessage. That copy could carry a wrong sender identity
94
+ // and, being treated as an own-message, was excluded from the
95
+ // unread count — the "wrong sender + no unread after reconnect,
96
+ // both correct after restart" symptom, because only a fresh
97
+ // start re-runs the preload scheduler that overwrites it.
98
+ //
99
+ // Dispatching through `applyRoomsPreloadBatch` (rather than the
100
+ // previous `setRoomMessages` array-replace) reuses
101
+ // mergeHistoryIntoCache: it dedupes by id with "fetched wins",
102
+ // preserves locally-pending sends and older cached history, and
103
+ // is already a trigger for the unread middleware — exactly what
104
+ // the startup path does, so reconnect and cold start now agree.
105
+ // Customer-reported #32.
106
+ if (currentJidNewMessages.length > 0) {
107
+ // Fire-and-forget — checkUniqueUsers resolves to an
108
+ // updated RoomMember list, but there's no slice
109
+ // action wired to receive it on RN yet (the web SDK
110
+ // has one; the port left a TODO). Keep the call so
111
+ // the API request goes out (it warms the cache for
112
+ // later renders) but stop short of dispatching.
113
+ await checkUniqueUsers(currentJidNewMessages);
114
+
115
+ store.dispatch(
116
+ applyRoomsPreloadBatch({
117
+ rooms: [{ jid, messages: currentJidNewMessages }],
118
+ })
119
+ );
95
120
  }
96
121
  } catch (error) {
97
122
  console.error(`Error processing room ${jid}:`, error);
@@ -59,12 +59,20 @@ export const useChatRoomFocus = ({
59
59
  });
60
60
 
61
61
  const leaveRoom = (jid: string) => {
62
- const timestamp = Date.now();
62
+ const rooms = store.getState().rooms?.rooms;
63
+ // Only mark read as far as the user actually scrolled. Blindly
64
+ // stamping `Date.now()` here clears messages they never reached, and
65
+ // the server flush below makes that permanent across a restart.
66
+ // Customer-reported #33.
67
+ const boundaryTs = rooms?.[jid]?.readBoundaryTs ?? null;
68
+ const timestamp = boundaryTs ?? Date.now();
63
69
  dispatch(setLastViewedTimestamp({ chatJID: jid, timestamp }));
64
70
  dispatch(clearVisibleRoom());
65
- const rooms = store.getState().rooms?.rooms;
66
71
  (store.getState().chatSettingStore as any)?.client
67
- ?.flushLastViewedToPrivateStoreStanza(rooms, { visibleRoomJID: jid })
72
+ ?.flushLastViewedToPrivateStoreStanza(rooms, {
73
+ visibleRoomJID: jid,
74
+ visibleRoomTs: boundaryTs,
75
+ })
68
76
  .catch(() => {});
69
77
  };
70
78
 
@@ -63,6 +63,13 @@ const logoutService = {
63
63
  await Promise.race([
64
64
  client.flushLastViewedToPrivateStoreStanza(rooms, {
65
65
  visibleRoomJID,
66
+ // `onlyIfNoUnread` deliberately exempts the visible room, so
67
+ // without this the open-but-scrolled-up room would still be
68
+ // stamped "read up to now" on logout and come back read on
69
+ // the next login. Customer-reported #33.
70
+ visibleRoomTs: visibleRoomJID
71
+ ? rooms?.[visibleRoomJID]?.readBoundaryTs ?? null
72
+ : null,
66
73
  onlyIfNoUnread: true,
67
74
  }),
68
75
  new Promise((res) => setTimeout(res, 2000)),