@ethora/chat-component-rn 26.7.2 → 26.7.4

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 (263) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +9 -8
  3. package/lib/commonjs/assets/icons.js +7 -2
  4. package/lib/commonjs/assets/icons.js.map +1 -1
  5. package/lib/commonjs/components/Header/HeaderRoomList.js +20 -6
  6. package/lib/commonjs/components/Header/HeaderRoomList.js.map +1 -1
  7. package/lib/commonjs/components/MainComponents/ChatRoom.js +86 -7
  8. package/lib/commonjs/components/MainComponents/ChatRoom.js.map +1 -1
  9. package/lib/commonjs/components/MainComponents/ChatWrapper.js +18 -0
  10. package/lib/commonjs/components/MainComponents/ChatWrapper.js.map +1 -1
  11. package/lib/commonjs/components/MainComponents/MessageList.js +11 -4
  12. package/lib/commonjs/components/MainComponents/MessageList.js.map +1 -1
  13. package/lib/commonjs/components/MainComponents/ProfileImagePlaceholder.js +5 -5
  14. package/lib/commonjs/components/MainComponents/ProfileImagePlaceholder.js.map +1 -1
  15. package/lib/commonjs/components/MainComponents/RoomList.js +62 -32
  16. package/lib/commonjs/components/MainComponents/RoomList.js.map +1 -1
  17. package/lib/commonjs/components/Menu/HeaderRoomListMenu.js +208 -70
  18. package/lib/commonjs/components/Menu/HeaderRoomListMenu.js.map +1 -1
  19. package/lib/commonjs/components/Modals/AttachSheet/AttachSheet.js +307 -99
  20. package/lib/commonjs/components/Modals/AttachSheet/AttachSheet.js.map +1 -1
  21. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js +544 -200
  22. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  23. package/lib/commonjs/components/Modals/ChatProfileModal/ReportChatModal.js +226 -0
  24. package/lib/commonjs/components/Modals/ChatProfileModal/ReportChatModal.js.map +1 -0
  25. package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js +6 -2
  26. package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
  27. package/lib/commonjs/components/Modals/Modal/Modal.js +8 -0
  28. package/lib/commonjs/components/Modals/Modal/Modal.js.map +1 -1
  29. package/lib/commonjs/components/Modals/NewChatModal/NewChatModal.js +202 -104
  30. package/lib/commonjs/components/Modals/NewChatModal/NewChatModal.js.map +1 -1
  31. package/lib/commonjs/components/Modals/ProfileHeader/ProfileHeader.js +556 -0
  32. package/lib/commonjs/components/Modals/ProfileHeader/ProfileHeader.js.map +1 -0
  33. package/lib/commonjs/components/Modals/ProfileHeader/ProfileMenu.js +210 -0
  34. package/lib/commonjs/components/Modals/ProfileHeader/ProfileMenu.js.map +1 -0
  35. package/lib/commonjs/components/Modals/SelectUsersModal/SelectUsersModal.js +4 -2
  36. package/lib/commonjs/components/Modals/SelectUsersModal/SelectUsersModal.js.map +1 -1
  37. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js +507 -244
  38. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  39. package/lib/commonjs/components/Modals/UserSettingsModal/UserSettingsModal.js +74 -46
  40. package/lib/commonjs/components/Modals/UserSettingsModal/UserSettingsModal.js.map +1 -1
  41. package/lib/commonjs/components/Modals/styledModalComponents.js +9 -2
  42. package/lib/commonjs/components/Modals/styledModalComponents.js.map +1 -1
  43. package/lib/commonjs/components/RoomComponents/ChatRoomItem.js +17 -7
  44. package/lib/commonjs/components/RoomComponents/ChatRoomItem.js.map +1 -1
  45. package/lib/commonjs/components/styled/SendInput.js +80 -1
  46. package/lib/commonjs/components/styled/SendInput.js.map +1 -1
  47. package/lib/commonjs/components/styled/StyledComponents.js +9 -3
  48. package/lib/commonjs/components/styled/StyledComponents.js.map +1 -1
  49. package/lib/commonjs/components/styled/StyledInputComponents/StyledInputComponents.js +11 -4
  50. package/lib/commonjs/components/styled/StyledInputComponents/StyledInputComponents.js.map +1 -1
  51. package/lib/commonjs/context/xmppProvider.js +12 -0
  52. package/lib/commonjs/context/xmppProvider.js.map +1 -1
  53. package/lib/commonjs/helpers/getChatBackground.js +23 -0
  54. package/lib/commonjs/helpers/getChatBackground.js.map +1 -0
  55. package/lib/commonjs/helpers/historyPreloadScheduler.js +65 -2
  56. package/lib/commonjs/helpers/historyPreloadScheduler.js.map +1 -1
  57. package/lib/commonjs/helpers/mediaLibraryRuntime.js +31 -0
  58. package/lib/commonjs/helpers/mediaLibraryRuntime.js.map +1 -0
  59. package/lib/commonjs/helpers/shallowEqual.js +36 -0
  60. package/lib/commonjs/helpers/shallowEqual.js.map +1 -0
  61. package/lib/commonjs/helpers/sheetGestures.js +32 -0
  62. package/lib/commonjs/helpers/sheetGestures.js.map +1 -0
  63. package/lib/commonjs/hooks/useChatWrapperInit.js +15 -0
  64. package/lib/commonjs/hooks/useChatWrapperInit.js.map +1 -1
  65. package/lib/commonjs/hooks/useComposing.js +27 -0
  66. package/lib/commonjs/hooks/useComposing.js.map +1 -1
  67. package/lib/commonjs/hooks/useSendMessage.js +27 -1
  68. package/lib/commonjs/hooks/useSendMessage.js.map +1 -1
  69. package/lib/commonjs/hooks/useUnreadMessagesCounter.js +6 -1
  70. package/lib/commonjs/hooks/useUnreadMessagesCounter.js.map +1 -1
  71. package/lib/commonjs/i18n/strings.js +121 -1
  72. package/lib/commonjs/i18n/strings.js.map +1 -1
  73. package/lib/commonjs/networking/api-requests/user.api.js +59 -0
  74. package/lib/commonjs/networking/api-requests/user.api.js.map +1 -1
  75. package/lib/commonjs/networking/xmpp/flushLastViewedToPrivateStore.js +6 -2
  76. package/lib/commonjs/networking/xmpp/flushLastViewedToPrivateStore.js.map +1 -1
  77. package/lib/commonjs/networking/xmppClient.js.map +1 -1
  78. package/lib/commonjs/roomStore/chatSettingsSlice.js +3 -1
  79. package/lib/commonjs/roomStore/chatSettingsSlice.js.map +1 -1
  80. package/lib/commonjs/roomStore/roomsSlice.js +2 -2
  81. package/lib/commonjs/roomStore/roomsSlice.js.map +1 -1
  82. package/lib/module/assets/icons.js +6 -1
  83. package/lib/module/assets/icons.js.map +1 -1
  84. package/lib/module/components/Header/HeaderRoomList.js +20 -6
  85. package/lib/module/components/Header/HeaderRoomList.js.map +1 -1
  86. package/lib/module/components/MainComponents/ChatRoom.js +87 -8
  87. package/lib/module/components/MainComponents/ChatRoom.js.map +1 -1
  88. package/lib/module/components/MainComponents/ChatWrapper.js +19 -1
  89. package/lib/module/components/MainComponents/ChatWrapper.js.map +1 -1
  90. package/lib/module/components/MainComponents/MessageList.js +11 -4
  91. package/lib/module/components/MainComponents/MessageList.js.map +1 -1
  92. package/lib/module/components/MainComponents/ProfileImagePlaceholder.js +5 -5
  93. package/lib/module/components/MainComponents/ProfileImagePlaceholder.js.map +1 -1
  94. package/lib/module/components/MainComponents/RoomList.js +62 -32
  95. package/lib/module/components/MainComponents/RoomList.js.map +1 -1
  96. package/lib/module/components/Menu/HeaderRoomListMenu.js +211 -73
  97. package/lib/module/components/Menu/HeaderRoomListMenu.js.map +1 -1
  98. package/lib/module/components/Modals/AttachSheet/AttachSheet.js +298 -102
  99. package/lib/module/components/Modals/AttachSheet/AttachSheet.js.map +1 -1
  100. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js +547 -204
  101. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  102. package/lib/module/components/Modals/ChatProfileModal/ReportChatModal.js +223 -0
  103. package/lib/module/components/Modals/ChatProfileModal/ReportChatModal.js.map +1 -0
  104. package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js +6 -2
  105. package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
  106. package/lib/module/components/Modals/Modal/Modal.js +9 -1
  107. package/lib/module/components/Modals/Modal/Modal.js.map +1 -1
  108. package/lib/module/components/Modals/NewChatModal/NewChatModal.js +205 -106
  109. package/lib/module/components/Modals/NewChatModal/NewChatModal.js.map +1 -1
  110. package/lib/module/components/Modals/ProfileHeader/ProfileHeader.js +548 -0
  111. package/lib/module/components/Modals/ProfileHeader/ProfileHeader.js.map +1 -0
  112. package/lib/module/components/Modals/ProfileHeader/ProfileMenu.js +205 -0
  113. package/lib/module/components/Modals/ProfileHeader/ProfileMenu.js.map +1 -0
  114. package/lib/module/components/Modals/SelectUsersModal/SelectUsersModal.js +4 -2
  115. package/lib/module/components/Modals/SelectUsersModal/SelectUsersModal.js.map +1 -1
  116. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js +509 -248
  117. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  118. package/lib/module/components/Modals/UserSettingsModal/UserSettingsModal.js +76 -48
  119. package/lib/module/components/Modals/UserSettingsModal/UserSettingsModal.js.map +1 -1
  120. package/lib/module/components/Modals/styledModalComponents.js +9 -2
  121. package/lib/module/components/Modals/styledModalComponents.js.map +1 -1
  122. package/lib/module/components/RoomComponents/ChatRoomItem.js +17 -7
  123. package/lib/module/components/RoomComponents/ChatRoomItem.js.map +1 -1
  124. package/lib/module/components/styled/SendInput.js +80 -1
  125. package/lib/module/components/styled/SendInput.js.map +1 -1
  126. package/lib/module/components/styled/StyledComponents.js +9 -3
  127. package/lib/module/components/styled/StyledComponents.js.map +1 -1
  128. package/lib/module/components/styled/StyledInputComponents/StyledInputComponents.js +11 -4
  129. package/lib/module/components/styled/StyledInputComponents/StyledInputComponents.js.map +1 -1
  130. package/lib/module/context/xmppProvider.js +12 -0
  131. package/lib/module/context/xmppProvider.js.map +1 -1
  132. package/lib/module/helpers/getChatBackground.js +18 -0
  133. package/lib/module/helpers/getChatBackground.js.map +1 -0
  134. package/lib/module/helpers/historyPreloadScheduler.js +66 -3
  135. package/lib/module/helpers/historyPreloadScheduler.js.map +1 -1
  136. package/lib/module/helpers/mediaLibraryRuntime.js +25 -0
  137. package/lib/module/helpers/mediaLibraryRuntime.js.map +1 -0
  138. package/lib/module/helpers/shallowEqual.js +31 -0
  139. package/lib/module/helpers/shallowEqual.js.map +1 -0
  140. package/lib/module/helpers/sheetGestures.js +26 -0
  141. package/lib/module/helpers/sheetGestures.js.map +1 -0
  142. package/lib/module/hooks/useChatWrapperInit.js +15 -0
  143. package/lib/module/hooks/useChatWrapperInit.js.map +1 -1
  144. package/lib/module/hooks/useComposing.js +28 -1
  145. package/lib/module/hooks/useComposing.js.map +1 -1
  146. package/lib/module/hooks/useSendMessage.js +28 -2
  147. package/lib/module/hooks/useSendMessage.js.map +1 -1
  148. package/lib/module/hooks/useUnreadMessagesCounter.js +6 -1
  149. package/lib/module/hooks/useUnreadMessagesCounter.js.map +1 -1
  150. package/lib/module/i18n/strings.js +121 -1
  151. package/lib/module/i18n/strings.js.map +1 -1
  152. package/lib/module/networking/api-requests/user.api.js +56 -0
  153. package/lib/module/networking/api-requests/user.api.js.map +1 -1
  154. package/lib/module/networking/xmpp/flushLastViewedToPrivateStore.js +6 -2
  155. package/lib/module/networking/xmpp/flushLastViewedToPrivateStore.js.map +1 -1
  156. package/lib/module/networking/xmppClient.js.map +1 -1
  157. package/lib/module/roomStore/chatSettingsSlice.js +3 -1
  158. package/lib/module/roomStore/chatSettingsSlice.js.map +1 -1
  159. package/lib/module/roomStore/roomsSlice.js +1 -1
  160. package/lib/module/roomStore/roomsSlice.js.map +1 -1
  161. package/lib/typescript/assets/icons.d.ts +12 -0
  162. package/lib/typescript/assets/icons.d.ts.map +1 -1
  163. package/lib/typescript/components/Header/HeaderRoomList.d.ts.map +1 -1
  164. package/lib/typescript/components/MainComponents/ChatRoom.d.ts.map +1 -1
  165. package/lib/typescript/components/MainComponents/ChatWrapper.d.ts.map +1 -1
  166. package/lib/typescript/components/MainComponents/MessageList.d.ts +10 -1
  167. package/lib/typescript/components/MainComponents/MessageList.d.ts.map +1 -1
  168. package/lib/typescript/components/MainComponents/RoomList.d.ts.map +1 -1
  169. package/lib/typescript/components/Menu/HeaderRoomListMenu.d.ts.map +1 -1
  170. package/lib/typescript/components/Modals/AttachSheet/AttachSheet.d.ts +9 -0
  171. package/lib/typescript/components/Modals/AttachSheet/AttachSheet.d.ts.map +1 -1
  172. package/lib/typescript/components/Modals/ChatProfileModal/ChatProfileModal.d.ts +10 -0
  173. package/lib/typescript/components/Modals/ChatProfileModal/ChatProfileModal.d.ts.map +1 -1
  174. package/lib/typescript/components/Modals/ChatProfileModal/ReportChatModal.d.ts +9 -0
  175. package/lib/typescript/components/Modals/ChatProfileModal/ReportChatModal.d.ts.map +1 -0
  176. package/lib/typescript/components/Modals/FilePreviewModal/FilePreviewModal.d.ts.map +1 -1
  177. package/lib/typescript/components/Modals/Modal/Modal.d.ts.map +1 -1
  178. package/lib/typescript/components/Modals/NewChatModal/NewChatModal.d.ts +1 -1
  179. package/lib/typescript/components/Modals/NewChatModal/NewChatModal.d.ts.map +1 -1
  180. package/lib/typescript/components/Modals/ProfileHeader/ProfileHeader.d.ts +75 -0
  181. package/lib/typescript/components/Modals/ProfileHeader/ProfileHeader.d.ts.map +1 -0
  182. package/lib/typescript/components/Modals/ProfileHeader/ProfileMenu.d.ts +25 -0
  183. package/lib/typescript/components/Modals/ProfileHeader/ProfileMenu.d.ts.map +1 -0
  184. package/lib/typescript/components/Modals/SelectUsersModal/SelectUsersModal.d.ts +6 -1
  185. package/lib/typescript/components/Modals/SelectUsersModal/SelectUsersModal.d.ts.map +1 -1
  186. package/lib/typescript/components/Modals/UserProfileModal/UserProfileModal.d.ts +16 -0
  187. package/lib/typescript/components/Modals/UserProfileModal/UserProfileModal.d.ts.map +1 -1
  188. package/lib/typescript/components/Modals/UserSettingsModal/UserSettingsModal.d.ts +1 -0
  189. package/lib/typescript/components/Modals/UserSettingsModal/UserSettingsModal.d.ts.map +1 -1
  190. package/lib/typescript/components/Modals/styledModalComponents.d.ts.map +1 -1
  191. package/lib/typescript/components/RoomComponents/ChatRoomItem.d.ts.map +1 -1
  192. package/lib/typescript/components/styled/SendInput.d.ts.map +1 -1
  193. package/lib/typescript/components/styled/StyledComponents.d.ts.map +1 -1
  194. package/lib/typescript/components/styled/StyledInputComponents/StyledInputComponents.d.ts.map +1 -1
  195. package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
  196. package/lib/typescript/helpers/getChatBackground.d.ts +15 -0
  197. package/lib/typescript/helpers/getChatBackground.d.ts.map +1 -0
  198. package/lib/typescript/helpers/historyPreloadScheduler.d.ts.map +1 -1
  199. package/lib/typescript/helpers/mediaLibraryRuntime.d.ts +3 -0
  200. package/lib/typescript/helpers/mediaLibraryRuntime.d.ts.map +1 -0
  201. package/lib/typescript/helpers/shallowEqual.d.ts +11 -0
  202. package/lib/typescript/helpers/shallowEqual.d.ts.map +1 -0
  203. package/lib/typescript/helpers/sheetGestures.d.ts +20 -0
  204. package/lib/typescript/helpers/sheetGestures.d.ts.map +1 -0
  205. package/lib/typescript/hooks/useChatWrapperInit.d.ts.map +1 -1
  206. package/lib/typescript/hooks/useComposing.d.ts.map +1 -1
  207. package/lib/typescript/hooks/useSendMessage.d.ts.map +1 -1
  208. package/lib/typescript/hooks/useUnreadMessagesCounter.d.ts +1 -0
  209. package/lib/typescript/hooks/useUnreadMessagesCounter.d.ts.map +1 -1
  210. package/lib/typescript/i18n/strings.d.ts.map +1 -1
  211. package/lib/typescript/networking/api-requests/user.api.d.ts +27 -0
  212. package/lib/typescript/networking/api-requests/user.api.d.ts.map +1 -1
  213. package/lib/typescript/networking/xmpp/flushLastViewedToPrivateStore.d.ts +1 -0
  214. package/lib/typescript/networking/xmpp/flushLastViewedToPrivateStore.d.ts.map +1 -1
  215. package/lib/typescript/networking/xmppClient.d.ts +1 -0
  216. package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
  217. package/lib/typescript/roomStore/chatSettingsSlice.d.ts.map +1 -1
  218. package/lib/typescript/roomStore/roomsSlice.d.ts +1 -0
  219. package/lib/typescript/roomStore/roomsSlice.d.ts.map +1 -1
  220. package/lib/typescript/types/types.d.ts +9 -5
  221. package/lib/typescript/types/types.d.ts.map +1 -1
  222. package/package.json +6 -1
  223. package/src/assets/icons.tsx +29 -9
  224. package/src/components/Header/HeaderRoomList.tsx +20 -5
  225. package/src/components/MainComponents/ChatRoom.tsx +92 -8
  226. package/src/components/MainComponents/ChatWrapper.tsx +19 -1
  227. package/src/components/MainComponents/MessageList.tsx +21 -3
  228. package/src/components/MainComponents/ProfileImagePlaceholder.tsx +5 -5
  229. package/src/components/MainComponents/RoomList.tsx +65 -34
  230. package/src/components/Menu/HeaderRoomListMenu.tsx +209 -64
  231. package/src/components/Modals/AttachSheet/AttachSheet.tsx +318 -86
  232. package/src/components/Modals/ChatProfileModal/ChatProfileModal.tsx +599 -278
  233. package/src/components/Modals/ChatProfileModal/ReportChatModal.tsx +244 -0
  234. package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +4 -2
  235. package/src/components/Modals/Modal/Modal.tsx +7 -0
  236. package/src/components/Modals/NewChatModal/NewChatModal.tsx +219 -117
  237. package/src/components/Modals/ProfileHeader/ProfileHeader.tsx +615 -0
  238. package/src/components/Modals/ProfileHeader/ProfileMenu.tsx +217 -0
  239. package/src/components/Modals/SelectUsersModal/SelectUsersModal.tsx +14 -4
  240. package/src/components/Modals/UserProfileModal/UserProfileModal.tsx +617 -307
  241. package/src/components/Modals/UserSettingsModal/UserSettingsModal.tsx +75 -58
  242. package/src/components/Modals/styledModalComponents.tsx +9 -2
  243. package/src/components/RoomComponents/ChatRoomItem.tsx +14 -7
  244. package/src/components/styled/SendInput.tsx +83 -2
  245. package/src/components/styled/StyledComponents.ts +9 -3
  246. package/src/components/styled/StyledInputComponents/StyledInputComponents.tsx +11 -4
  247. package/src/context/xmppProvider.tsx +14 -0
  248. package/src/helpers/getChatBackground.ts +18 -0
  249. package/src/helpers/historyPreloadScheduler.ts +80 -3
  250. package/src/helpers/mediaLibraryRuntime.ts +25 -0
  251. package/src/helpers/shallowEqual.ts +33 -0
  252. package/src/helpers/sheetGestures.ts +25 -0
  253. package/src/hooks/useChatWrapperInit.ts +15 -0
  254. package/src/hooks/useComposing.tsx +37 -1
  255. package/src/hooks/useSendMessage.tsx +32 -2
  256. package/src/hooks/useUnreadMessagesCounter.ts +11 -0
  257. package/src/i18n/strings.ts +121 -1
  258. package/src/networking/api-requests/user.api.ts +71 -0
  259. package/src/networking/xmpp/flushLastViewedToPrivateStore.ts +17 -3
  260. package/src/networking/xmppClient.ts +5 -1
  261. package/src/roomStore/chatSettingsSlice.ts +3 -1
  262. package/src/roomStore/roomsSlice.ts +1 -1
  263. package/src/types/types.ts +16 -5
@@ -1,19 +1,41 @@
1
1
  /** @format */
2
2
 
3
- import React, { useEffect, useRef, useState } from 'react';
3
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
4
4
  import {
5
+ ActivityIndicator,
5
6
  Animated,
6
7
  Easing,
8
+ Image,
7
9
  Modal,
10
+ PanResponder,
8
11
  Platform,
12
+ ScrollView,
9
13
  StyleSheet,
10
14
  Text,
11
15
  TouchableOpacity,
12
16
  View,
13
17
  } from 'react-native';
14
- import { CameraIcon, DocumentIcon, MediaIcon } from '../../../assets/icons';
18
+ import { CameraIcon, DocumentIcon } from '../../../assets/icons';
15
19
  import { useChatSettingState } from '../../../hooks/useChatSettingState';
16
20
  import { chatTextStyle } from '../../../helpers/typography';
21
+ import { getMediaLibrary } from '../../../helpers/mediaLibraryRuntime';
22
+ import {
23
+ shouldClaimVerticalDrag,
24
+ shouldDismissOnDrag,
25
+ } from '../../../helpers/sheetGestures';
26
+
27
+ interface RecentItem {
28
+ id: string;
29
+ uri: string;
30
+ filename: string;
31
+ }
32
+
33
+ export interface PickedMedia {
34
+ uri: string;
35
+ name: string;
36
+ mimeType?: string;
37
+ isVideo: boolean;
38
+ }
17
39
 
18
40
  interface AttachSheetProps {
19
41
  visible: boolean;
@@ -21,13 +43,54 @@ interface AttachSheetProps {
21
43
  onCamera: () => void;
22
44
  onGallery: () => void;
23
45
  onDocument: () => void;
46
+ onPickMedia?: (media: PickedMedia) => void;
24
47
  primaryColor?: string;
25
48
  }
26
49
 
27
50
  // Starting translateY for the sheet. Larger than any realistic sheet
28
51
  // height so it sits fully below the viewport before opening; the exact
29
52
  // number doesn't matter visually because it animates to 0.
30
- const SHEET_OFFSCREEN = 600;
53
+ const SHEET_OFFSCREEN = 800;
54
+ // Re-exported so this sheet's own tests keep addressing them here.
55
+ export { shouldClaimVerticalDrag, shouldDismissOnDrag };
56
+
57
+ const RECENTS_COUNT = 12;
58
+ const THUMB_SIZE = 88;
59
+ const THUMB_DECODE = THUMB_SIZE * 2;
60
+
61
+ const EXT_MIME: Record<string, string> = {
62
+ jpg: 'image/jpeg',
63
+ jpeg: 'image/jpeg',
64
+ png: 'image/png',
65
+ gif: 'image/gif',
66
+ webp: 'image/webp',
67
+ heic: 'image/heic',
68
+ heif: 'image/heif',
69
+ };
70
+
71
+ const guessMimeType = (name: string) => {
72
+ const ext = name.split('.').pop()?.toLowerCase() ?? '';
73
+ return EXT_MIME[ext] ?? 'image/jpeg';
74
+ };
75
+
76
+ const toRecentItem = async (
77
+ MediaLibrary: any,
78
+ asset: any
79
+ ): Promise<RecentItem | null> => {
80
+ const filename = asset?.filename || `photo_${asset?.id ?? Date.now()}`;
81
+ if (asset?.uri && !asset.uri.startsWith('ph://')) {
82
+ return { id: asset.id, uri: asset.uri, filename };
83
+ }
84
+ try {
85
+ const info = await MediaLibrary?.getAssetInfoAsync?.(asset, {
86
+ shouldDownloadFromNetwork: false,
87
+ });
88
+ if (!info?.localUri) {return null;}
89
+ return { id: asset.id, uri: info.localUri, filename };
90
+ } catch {
91
+ return null;
92
+ }
93
+ };
31
94
 
32
95
  const AttachSheet: React.FC<AttachSheetProps> = ({
33
96
  visible,
@@ -35,13 +98,13 @@ const AttachSheet: React.FC<AttachSheetProps> = ({
35
98
  onCamera,
36
99
  onGallery,
37
100
  onDocument,
101
+ onPickMedia,
38
102
  primaryColor = '#0052CD',
39
103
  }) => {
40
104
  const { config } = useChatSettingState();
41
105
  const ts = config?.typography?.attachSheet;
42
106
  const pendingRef = useRef<(() => void) | null>(null);
43
107
  const runPending = () => {
44
- console.log('[timing] runPending', Date.now());
45
108
  const fn = pendingRef.current;
46
109
  pendingRef.current = null;
47
110
  fn?.();
@@ -64,6 +127,9 @@ const AttachSheet: React.FC<AttachSheetProps> = ({
64
127
  new Animated.Value(SHEET_OFFSCREEN)
65
128
  ).current;
66
129
 
130
+ const onCloseRef = useRef(onClose);
131
+ onCloseRef.current = onClose;
132
+
67
133
  useEffect(() => {
68
134
  if (visible) {
69
135
  setMounted(true);
@@ -119,8 +185,96 @@ const AttachSheet: React.FC<AttachSheetProps> = ({
119
185
  return undefined;
120
186
  }, [visible]); // eslint-disable-line react-hooks/exhaustive-deps
121
187
 
188
+ const makeDragResponder = (claimOnStart: boolean) =>
189
+ PanResponder.create({
190
+ onStartShouldSetPanResponder: () => claimOnStart,
191
+ // Capture, so a pull that starts on a row (whose Touchable grabbed
192
+ // the responder on touch-down) still drags the sheet.
193
+ onMoveShouldSetPanResponderCapture: (_evt, g) =>
194
+ claimOnStart || shouldClaimVerticalDrag(g.dy, g.dx),
195
+ onMoveShouldSetPanResponder: (_evt, g) =>
196
+ claimOnStart || shouldClaimVerticalDrag(g.dy, g.dx),
197
+ onPanResponderTerminationRequest: () => false,
198
+ onPanResponderMove: (_evt, g) => {
199
+ sheetTranslateY.setValue(Math.max(0, g.dy));
200
+ },
201
+ onPanResponderRelease: (_evt, g) => {
202
+ if (shouldDismissOnDrag(g.dy, g.vy)) {
203
+ onCloseRef.current();
204
+ return;
205
+ }
206
+ Animated.spring(sheetTranslateY, {
207
+ toValue: 0,
208
+ tension: 90,
209
+ friction: 14,
210
+ useNativeDriver: true,
211
+ }).start();
212
+ },
213
+ onPanResponderTerminate: () => {
214
+ Animated.spring(sheetTranslateY, {
215
+ toValue: 0,
216
+ tension: 90,
217
+ friction: 14,
218
+ useNativeDriver: true,
219
+ }).start();
220
+ },
221
+ });
222
+
223
+ const sheetPanRef = useRef<ReturnType<typeof makeDragResponder> | null>(null);
224
+ const grabberPanRef = useRef<ReturnType<typeof makeDragResponder> | null>(null);
225
+ if (!sheetPanRef.current) {sheetPanRef.current = makeDragResponder(false);}
226
+ if (!grabberPanRef.current) {grabberPanRef.current = makeDragResponder(true);}
227
+ const sheetPan = sheetPanRef.current;
228
+ const grabberPan = grabberPanRef.current;
229
+
230
+ const [recents, setRecents] = useState<RecentItem[]>([]);
231
+ const [loadingRecents, setLoadingRecents] = useState(false);
232
+ const lastLoadRef = useRef(0);
233
+ const RECENTS_TTL_MS = 15000;
234
+
235
+ const loadRecents = useCallback(async () => {
236
+ const MediaLibrary = getMediaLibrary();
237
+ if (!MediaLibrary?.getAssetsAsync) {
238
+ setLoadingRecents(false);
239
+ return;
240
+ }
241
+ try {
242
+ const perm = await MediaLibrary.getPermissionsAsync();
243
+ if (!perm?.granted) {
244
+ setRecents([]);
245
+ return;
246
+ }
247
+ const page = await MediaLibrary.getAssetsAsync({
248
+ first: RECENTS_COUNT,
249
+ sortBy: [MediaLibrary.SortBy?.creationTime ?? 'creationTime'],
250
+ mediaType: [MediaLibrary.MediaType?.photo ?? 'photo'],
251
+ });
252
+ const items = await Promise.all(
253
+ (page?.assets ?? []).map((asset: any) => toRecentItem(MediaLibrary, asset))
254
+ );
255
+ lastLoadRef.current = Date.now();
256
+ setRecents(items.filter(Boolean) as RecentItem[]);
257
+ } catch (err) {
258
+ console.warn('AttachSheet: could not read recent media', err);
259
+ setRecents([]);
260
+ } finally {
261
+ setLoadingRecents(false);
262
+ }
263
+ }, []);
264
+
265
+ useEffect(() => {
266
+ if (!visible) {return undefined;}
267
+ if (recents.length && Date.now() - lastLoadRef.current < RECENTS_TTL_MS) {
268
+ return undefined;
269
+ }
270
+ setLoadingRecents(true);
271
+ const timer = setTimeout(() => {
272
+ loadRecents();
273
+ }, 250);
274
+ return () => clearTimeout(timer);
275
+ }, [visible]);
276
+
122
277
  const trigger = (handler: () => void) => () => {
123
- console.log('[timing] row tap', Date.now());
124
278
  pendingRef.current = handler;
125
279
  if (Platform.OS === 'ios') {
126
280
  setMounted(false);
@@ -128,27 +282,29 @@ const AttachSheet: React.FC<AttachSheetProps> = ({
128
282
  onClose();
129
283
  };
130
284
 
285
+ const handleRecentPress = (item: RecentItem) =>
286
+ trigger(() => {
287
+ if (onPickMedia) {
288
+ onPickMedia({
289
+ uri: item.uri,
290
+ name: item.filename,
291
+ mimeType: guessMimeType(item.filename),
292
+ isVideo: false,
293
+ });
294
+ } else {
295
+ onGallery();
296
+ }
297
+ });
298
+
131
299
  const rows: {
300
+ id: string;
132
301
  label: string;
133
- hint: string;
134
302
  Icon: React.ComponentType<any>;
135
303
  handler: () => void;
136
304
  }[] = [
137
305
  {
138
- label: 'Take photo',
139
- hint: 'Capture with the camera',
140
- Icon: CameraIcon,
141
- handler: onCamera,
142
- },
143
- {
144
- label: 'Photo or video',
145
- hint: 'Pick from your library',
146
- Icon: MediaIcon,
147
- handler: onGallery,
148
- },
149
- {
150
- label: 'Document',
151
- hint: 'Choose a file',
306
+ id: 'Document',
307
+ label: 'Upload a File',
152
308
  Icon: DocumentIcon,
153
309
  handler: onDocument,
154
310
  },
@@ -181,43 +337,101 @@ const AttachSheet: React.FC<AttachSheetProps> = ({
181
337
  styles.sheet,
182
338
  { transform: [{ translateY: sheetTranslateY }] },
183
339
  ]}
340
+ {...sheetPan.panHandlers}
184
341
  >
185
- <TouchableOpacity activeOpacity={1}>
342
+ <View
343
+ testID="attach-grabber"
344
+ style={styles.grabberArea}
345
+ {...grabberPan.panHandlers}
346
+ >
186
347
  <View style={styles.grabber} />
187
- <Text style={[styles.title, chatTextStyle(ts?.title)]}>Attach</Text>
188
- {rows.map(({ label, hint, Icon, handler }, idx) => (
189
- <TouchableOpacity
190
- key={label}
191
- testID={`attach-row-${label}`}
192
- activeOpacity={0.6}
348
+ </View>
349
+
350
+ {/* Photos & Videos ------------------------------------------------ */}
351
+ <View style={styles.sectionHeader}>
352
+ <Text style={[styles.sectionTitle, chatTextStyle(ts?.title)]}>
353
+ Photos & Videos
354
+ </Text>
355
+ <TouchableOpacity
356
+ testID="attach-view-library"
357
+ activeOpacity={0.6}
358
+ onPress={trigger(onGallery)}
359
+ hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
360
+ >
361
+ <Text
193
362
  style={[
194
- styles.row,
195
- idx < rows.length - 1 && styles.rowDivider,
363
+ styles.sectionAction,
364
+ { color: primaryColor },
365
+ chatTextStyle(ts?.viewLibrary),
196
366
  ]}
197
- onPress={trigger(handler)}
198
367
  >
199
- <View
200
- style={[
201
- styles.iconBubble,
202
- { backgroundColor: primaryColor + '14' },
203
- ]}
204
- >
205
- <Icon color={primaryColor} width={20} height={20} />
206
- </View>
207
- <View style={styles.rowText}>
208
- <Text style={[styles.rowLabel, chatTextStyle(ts?.rowLabel)]}>{label}</Text>
209
- <Text style={[styles.rowHint, chatTextStyle(ts?.rowHint)]}>{hint}</Text>
210
- </View>
368
+ View Library
369
+ </Text>
370
+ </TouchableOpacity>
371
+ </View>
372
+
373
+ <ScrollView
374
+ horizontal
375
+ showsHorizontalScrollIndicator={false}
376
+ contentContainerStyle={styles.strip}
377
+ keyboardShouldPersistTaps="handled"
378
+ >
379
+ {/* Camera tile — always first, works without library access. */}
380
+ <TouchableOpacity
381
+ testID="attach-row-Camera"
382
+ activeOpacity={0.7}
383
+ style={styles.cameraTile}
384
+ onPress={trigger(onCamera)}
385
+ >
386
+ <CameraIcon color="#8A8A8E" width={26} height={26} />
387
+ </TouchableOpacity>
388
+
389
+ {loadingRecents && recents.length === 0 && (
390
+ <View style={styles.loadingTile}>
391
+ <ActivityIndicator color="#8A8A8E" />
392
+ </View>
393
+ )}
394
+
395
+ {recents.map((item) => (
396
+ <TouchableOpacity
397
+ key={item.id}
398
+ testID={`attach-recent-${item.id}`}
399
+ activeOpacity={0.8}
400
+ style={styles.thumbWrap}
401
+ onPress={handleRecentPress(item)}
402
+ >
403
+ <Image
404
+ source={{
405
+ uri: item.uri,
406
+ width: THUMB_DECODE,
407
+ height: THUMB_DECODE,
408
+ }}
409
+ style={styles.thumb}
410
+ resizeMethod="resize"
411
+ resizeMode="cover"
412
+ fadeDuration={0}
413
+ />
211
414
  </TouchableOpacity>
212
415
  ))}
416
+ </ScrollView>
417
+
418
+ <View style={styles.rowsDivider} />
419
+ {rows.map(({ id, label, Icon, handler }) => (
213
420
  <TouchableOpacity
421
+ key={id}
422
+ testID={`attach-row-${id}`}
214
423
  activeOpacity={0.6}
215
- style={styles.cancelRow}
216
- onPress={onClose}
424
+ style={styles.row}
425
+ onPress={trigger(handler)}
217
426
  >
218
- <Text style={[styles.cancelLabel, chatTextStyle(ts?.cancelButton)]}>Cancel</Text>
427
+ <View style={styles.rowIcon}>
428
+ <Icon color="#1C1C1E" width={20} height={20} />
429
+ </View>
430
+ <Text style={[styles.rowLabel, chatTextStyle(ts?.rowLabel)]}>
431
+ {label}
432
+ </Text>
219
433
  </TouchableOpacity>
220
- </TouchableOpacity>
434
+ ))}
221
435
  </Animated.View>
222
436
  </Animated.View>
223
437
  </Modal>
@@ -234,7 +448,7 @@ const styles = StyleSheet.create({
234
448
  backgroundColor: '#fff',
235
449
  borderTopLeftRadius: 24,
236
450
  borderTopRightRadius: 24,
237
- paddingTop: 8,
451
+ paddingTop: 4,
238
452
  paddingBottom: Platform.OS === 'ios' ? 32 : 20,
239
453
  shadowColor: '#000',
240
454
  shadowOffset: { width: 0, height: -2 },
@@ -242,65 +456,83 @@ const styles = StyleSheet.create({
242
456
  shadowRadius: 12,
243
457
  elevation: 16,
244
458
  },
459
+ grabberArea: {
460
+ alignItems: 'center',
461
+ paddingTop: 10,
462
+ paddingBottom: 14,
463
+ },
245
464
  grabber: {
246
- alignSelf: 'center',
247
465
  width: 40,
248
466
  height: 4,
249
467
  borderRadius: 2,
250
468
  backgroundColor: '#D9D9DE',
251
- marginBottom: 12,
252
469
  },
253
- title: {
254
- fontSize: 13,
255
- fontWeight: '600',
256
- color: '#8A8A8E',
257
- letterSpacing: 0.4,
258
- textTransform: 'uppercase',
259
- paddingHorizontal: 20,
260
- paddingBottom: 8,
261
- },
262
- row: {
470
+ sectionHeader: {
263
471
  flexDirection: 'row',
264
472
  alignItems: 'center',
473
+ justifyContent: 'space-between',
265
474
  paddingHorizontal: 20,
266
- paddingVertical: 14,
475
+ paddingBottom: 12,
267
476
  },
268
- rowDivider: {
269
- borderBottomWidth: StyleSheet.hairlineWidth,
270
- borderBottomColor: '#EFEFF2',
477
+ sectionTitle: {
478
+ fontSize: 17,
479
+ fontWeight: '700',
480
+ color: '#1C1C1E',
271
481
  },
272
- iconBubble: {
273
- width: 40,
274
- height: 40,
275
- borderRadius: 20,
482
+ sectionAction: {
483
+ fontSize: 15,
484
+ fontWeight: '500',
485
+ },
486
+ strip: {
487
+ paddingHorizontal: 16,
488
+ paddingBottom: 16,
489
+ gap: 8,
490
+ },
491
+ cameraTile: {
492
+ width: THUMB_SIZE,
493
+ height: THUMB_SIZE,
494
+ borderRadius: 16,
495
+ borderWidth: 1,
496
+ borderColor: '#E4E4E9',
497
+ backgroundColor: '#FAFAFC',
276
498
  alignItems: 'center',
277
499
  justifyContent: 'center',
278
- marginRight: 14,
279
500
  },
280
- rowText: {
281
- flex: 1,
501
+ loadingTile: {
502
+ width: THUMB_SIZE,
503
+ height: THUMB_SIZE,
504
+ alignItems: 'center',
505
+ justifyContent: 'center',
282
506
  },
283
- rowLabel: {
284
- fontSize: 16,
285
- fontWeight: '500',
286
- color: '#1C1C1E',
507
+ thumbWrap: {
508
+ width: THUMB_SIZE,
509
+ height: THUMB_SIZE,
510
+ borderRadius: 16,
511
+ overflow: 'hidden',
512
+ backgroundColor: '#EFEFF2',
513
+ },
514
+ thumb: {
515
+ width: '100%',
516
+ height: '100%',
287
517
  },
288
- rowHint: {
289
- fontSize: 12,
290
- color: '#8A8A8E',
291
- marginTop: 2,
518
+ rowsDivider: {
519
+ height: StyleSheet.hairlineWidth,
520
+ backgroundColor: '#EFEFF2',
292
521
  },
293
- cancelRow: {
294
- marginTop: 8,
295
- marginHorizontal: 16,
296
- paddingVertical: 14,
297
- borderRadius: 14,
522
+ row: {
523
+ flexDirection: 'row',
298
524
  alignItems: 'center',
299
- backgroundColor: '#F2F2F7',
525
+ paddingHorizontal: 20,
526
+ paddingVertical: 16,
300
527
  },
301
- cancelLabel: {
528
+ rowIcon: {
529
+ width: 28,
530
+ alignItems: 'center',
531
+ marginRight: 12,
532
+ },
533
+ rowLabel: {
302
534
  fontSize: 16,
303
- fontWeight: '600',
535
+ fontWeight: '500',
304
536
  color: '#1C1C1E',
305
537
  },
306
538
  });