@ethora/chat-component-rn 26.7.3 → 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 (193) 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 +26 -4
  8. package/lib/commonjs/components/MainComponents/ChatRoom.js.map +1 -1
  9. package/lib/commonjs/components/MainComponents/MessageList.js +2 -1
  10. package/lib/commonjs/components/MainComponents/MessageList.js.map +1 -1
  11. package/lib/commonjs/components/MainComponents/ProfileImagePlaceholder.js +5 -5
  12. package/lib/commonjs/components/MainComponents/ProfileImagePlaceholder.js.map +1 -1
  13. package/lib/commonjs/components/MainComponents/RoomList.js +62 -32
  14. package/lib/commonjs/components/MainComponents/RoomList.js.map +1 -1
  15. package/lib/commonjs/components/Menu/HeaderRoomListMenu.js +208 -70
  16. package/lib/commonjs/components/Menu/HeaderRoomListMenu.js.map +1 -1
  17. package/lib/commonjs/components/Modals/AttachSheet/AttachSheet.js +307 -99
  18. package/lib/commonjs/components/Modals/AttachSheet/AttachSheet.js.map +1 -1
  19. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js +552 -213
  20. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  21. package/lib/commonjs/components/Modals/ChatProfileModal/ReportChatModal.js +226 -0
  22. package/lib/commonjs/components/Modals/ChatProfileModal/ReportChatModal.js.map +1 -0
  23. package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js +6 -2
  24. package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
  25. package/lib/commonjs/components/Modals/Modal/Modal.js +8 -0
  26. package/lib/commonjs/components/Modals/Modal/Modal.js.map +1 -1
  27. package/lib/commonjs/components/Modals/NewChatModal/NewChatModal.js +202 -104
  28. package/lib/commonjs/components/Modals/NewChatModal/NewChatModal.js.map +1 -1
  29. package/lib/commonjs/components/Modals/ProfileHeader/ProfileHeader.js +556 -0
  30. package/lib/commonjs/components/Modals/ProfileHeader/ProfileHeader.js.map +1 -0
  31. package/lib/commonjs/components/Modals/ProfileHeader/ProfileMenu.js +210 -0
  32. package/lib/commonjs/components/Modals/ProfileHeader/ProfileMenu.js.map +1 -0
  33. package/lib/commonjs/components/Modals/SelectUsersModal/SelectUsersModal.js +4 -2
  34. package/lib/commonjs/components/Modals/SelectUsersModal/SelectUsersModal.js.map +1 -1
  35. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js +507 -244
  36. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  37. package/lib/commonjs/components/Modals/UserSettingsModal/UserSettingsModal.js +74 -46
  38. package/lib/commonjs/components/Modals/UserSettingsModal/UserSettingsModal.js.map +1 -1
  39. package/lib/commonjs/components/Modals/styledModalComponents.js +9 -2
  40. package/lib/commonjs/components/Modals/styledModalComponents.js.map +1 -1
  41. package/lib/commonjs/components/RoomComponents/ChatRoomItem.js +17 -7
  42. package/lib/commonjs/components/RoomComponents/ChatRoomItem.js.map +1 -1
  43. package/lib/commonjs/components/styled/SendInput.js +80 -1
  44. package/lib/commonjs/components/styled/SendInput.js.map +1 -1
  45. package/lib/commonjs/components/styled/StyledComponents.js +9 -3
  46. package/lib/commonjs/components/styled/StyledComponents.js.map +1 -1
  47. package/lib/commonjs/components/styled/StyledInputComponents/StyledInputComponents.js +11 -4
  48. package/lib/commonjs/components/styled/StyledInputComponents/StyledInputComponents.js.map +1 -1
  49. package/lib/commonjs/helpers/getChatBackground.js +23 -0
  50. package/lib/commonjs/helpers/getChatBackground.js.map +1 -0
  51. package/lib/commonjs/helpers/mediaLibraryRuntime.js +31 -0
  52. package/lib/commonjs/helpers/mediaLibraryRuntime.js.map +1 -0
  53. package/lib/commonjs/helpers/sheetGestures.js +32 -0
  54. package/lib/commonjs/helpers/sheetGestures.js.map +1 -0
  55. package/lib/commonjs/i18n/strings.js +121 -1
  56. package/lib/commonjs/i18n/strings.js.map +1 -1
  57. package/lib/commonjs/networking/api-requests/user.api.js +59 -0
  58. package/lib/commonjs/networking/api-requests/user.api.js.map +1 -1
  59. package/lib/commonjs/roomStore/chatSettingsSlice.js +3 -1
  60. package/lib/commonjs/roomStore/chatSettingsSlice.js.map +1 -1
  61. package/lib/module/assets/icons.js +6 -1
  62. package/lib/module/assets/icons.js.map +1 -1
  63. package/lib/module/components/Header/HeaderRoomList.js +20 -6
  64. package/lib/module/components/Header/HeaderRoomList.js.map +1 -1
  65. package/lib/module/components/MainComponents/ChatRoom.js +26 -4
  66. package/lib/module/components/MainComponents/ChatRoom.js.map +1 -1
  67. package/lib/module/components/MainComponents/MessageList.js +2 -1
  68. package/lib/module/components/MainComponents/MessageList.js.map +1 -1
  69. package/lib/module/components/MainComponents/ProfileImagePlaceholder.js +5 -5
  70. package/lib/module/components/MainComponents/ProfileImagePlaceholder.js.map +1 -1
  71. package/lib/module/components/MainComponents/RoomList.js +62 -32
  72. package/lib/module/components/MainComponents/RoomList.js.map +1 -1
  73. package/lib/module/components/Menu/HeaderRoomListMenu.js +211 -73
  74. package/lib/module/components/Menu/HeaderRoomListMenu.js.map +1 -1
  75. package/lib/module/components/Modals/AttachSheet/AttachSheet.js +298 -102
  76. package/lib/module/components/Modals/AttachSheet/AttachSheet.js.map +1 -1
  77. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js +555 -217
  78. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  79. package/lib/module/components/Modals/ChatProfileModal/ReportChatModal.js +223 -0
  80. package/lib/module/components/Modals/ChatProfileModal/ReportChatModal.js.map +1 -0
  81. package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js +6 -2
  82. package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
  83. package/lib/module/components/Modals/Modal/Modal.js +9 -1
  84. package/lib/module/components/Modals/Modal/Modal.js.map +1 -1
  85. package/lib/module/components/Modals/NewChatModal/NewChatModal.js +205 -106
  86. package/lib/module/components/Modals/NewChatModal/NewChatModal.js.map +1 -1
  87. package/lib/module/components/Modals/ProfileHeader/ProfileHeader.js +548 -0
  88. package/lib/module/components/Modals/ProfileHeader/ProfileHeader.js.map +1 -0
  89. package/lib/module/components/Modals/ProfileHeader/ProfileMenu.js +205 -0
  90. package/lib/module/components/Modals/ProfileHeader/ProfileMenu.js.map +1 -0
  91. package/lib/module/components/Modals/SelectUsersModal/SelectUsersModal.js +4 -2
  92. package/lib/module/components/Modals/SelectUsersModal/SelectUsersModal.js.map +1 -1
  93. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js +509 -248
  94. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  95. package/lib/module/components/Modals/UserSettingsModal/UserSettingsModal.js +76 -48
  96. package/lib/module/components/Modals/UserSettingsModal/UserSettingsModal.js.map +1 -1
  97. package/lib/module/components/Modals/styledModalComponents.js +9 -2
  98. package/lib/module/components/Modals/styledModalComponents.js.map +1 -1
  99. package/lib/module/components/RoomComponents/ChatRoomItem.js +17 -7
  100. package/lib/module/components/RoomComponents/ChatRoomItem.js.map +1 -1
  101. package/lib/module/components/styled/SendInput.js +80 -1
  102. package/lib/module/components/styled/SendInput.js.map +1 -1
  103. package/lib/module/components/styled/StyledComponents.js +9 -3
  104. package/lib/module/components/styled/StyledComponents.js.map +1 -1
  105. package/lib/module/components/styled/StyledInputComponents/StyledInputComponents.js +11 -4
  106. package/lib/module/components/styled/StyledInputComponents/StyledInputComponents.js.map +1 -1
  107. package/lib/module/helpers/getChatBackground.js +18 -0
  108. package/lib/module/helpers/getChatBackground.js.map +1 -0
  109. package/lib/module/helpers/mediaLibraryRuntime.js +25 -0
  110. package/lib/module/helpers/mediaLibraryRuntime.js.map +1 -0
  111. package/lib/module/helpers/sheetGestures.js +26 -0
  112. package/lib/module/helpers/sheetGestures.js.map +1 -0
  113. package/lib/module/i18n/strings.js +121 -1
  114. package/lib/module/i18n/strings.js.map +1 -1
  115. package/lib/module/networking/api-requests/user.api.js +56 -0
  116. package/lib/module/networking/api-requests/user.api.js.map +1 -1
  117. package/lib/module/roomStore/chatSettingsSlice.js +3 -1
  118. package/lib/module/roomStore/chatSettingsSlice.js.map +1 -1
  119. package/lib/typescript/assets/icons.d.ts +12 -0
  120. package/lib/typescript/assets/icons.d.ts.map +1 -1
  121. package/lib/typescript/components/Header/HeaderRoomList.d.ts.map +1 -1
  122. package/lib/typescript/components/MainComponents/ChatRoom.d.ts.map +1 -1
  123. package/lib/typescript/components/MainComponents/MessageList.d.ts.map +1 -1
  124. package/lib/typescript/components/MainComponents/RoomList.d.ts.map +1 -1
  125. package/lib/typescript/components/Menu/HeaderRoomListMenu.d.ts.map +1 -1
  126. package/lib/typescript/components/Modals/AttachSheet/AttachSheet.d.ts +9 -0
  127. package/lib/typescript/components/Modals/AttachSheet/AttachSheet.d.ts.map +1 -1
  128. package/lib/typescript/components/Modals/ChatProfileModal/ChatProfileModal.d.ts +10 -0
  129. package/lib/typescript/components/Modals/ChatProfileModal/ChatProfileModal.d.ts.map +1 -1
  130. package/lib/typescript/components/Modals/ChatProfileModal/ReportChatModal.d.ts +9 -0
  131. package/lib/typescript/components/Modals/ChatProfileModal/ReportChatModal.d.ts.map +1 -0
  132. package/lib/typescript/components/Modals/FilePreviewModal/FilePreviewModal.d.ts.map +1 -1
  133. package/lib/typescript/components/Modals/Modal/Modal.d.ts.map +1 -1
  134. package/lib/typescript/components/Modals/NewChatModal/NewChatModal.d.ts +1 -1
  135. package/lib/typescript/components/Modals/NewChatModal/NewChatModal.d.ts.map +1 -1
  136. package/lib/typescript/components/Modals/ProfileHeader/ProfileHeader.d.ts +75 -0
  137. package/lib/typescript/components/Modals/ProfileHeader/ProfileHeader.d.ts.map +1 -0
  138. package/lib/typescript/components/Modals/ProfileHeader/ProfileMenu.d.ts +25 -0
  139. package/lib/typescript/components/Modals/ProfileHeader/ProfileMenu.d.ts.map +1 -0
  140. package/lib/typescript/components/Modals/SelectUsersModal/SelectUsersModal.d.ts +6 -1
  141. package/lib/typescript/components/Modals/SelectUsersModal/SelectUsersModal.d.ts.map +1 -1
  142. package/lib/typescript/components/Modals/UserProfileModal/UserProfileModal.d.ts +16 -0
  143. package/lib/typescript/components/Modals/UserProfileModal/UserProfileModal.d.ts.map +1 -1
  144. package/lib/typescript/components/Modals/UserSettingsModal/UserSettingsModal.d.ts +1 -0
  145. package/lib/typescript/components/Modals/UserSettingsModal/UserSettingsModal.d.ts.map +1 -1
  146. package/lib/typescript/components/Modals/styledModalComponents.d.ts.map +1 -1
  147. package/lib/typescript/components/RoomComponents/ChatRoomItem.d.ts.map +1 -1
  148. package/lib/typescript/components/styled/SendInput.d.ts.map +1 -1
  149. package/lib/typescript/components/styled/StyledComponents.d.ts.map +1 -1
  150. package/lib/typescript/components/styled/StyledInputComponents/StyledInputComponents.d.ts.map +1 -1
  151. package/lib/typescript/helpers/getChatBackground.d.ts +15 -0
  152. package/lib/typescript/helpers/getChatBackground.d.ts.map +1 -0
  153. package/lib/typescript/helpers/mediaLibraryRuntime.d.ts +3 -0
  154. package/lib/typescript/helpers/mediaLibraryRuntime.d.ts.map +1 -0
  155. package/lib/typescript/helpers/sheetGestures.d.ts +20 -0
  156. package/lib/typescript/helpers/sheetGestures.d.ts.map +1 -0
  157. package/lib/typescript/i18n/strings.d.ts.map +1 -1
  158. package/lib/typescript/networking/api-requests/user.api.d.ts +27 -0
  159. package/lib/typescript/networking/api-requests/user.api.d.ts.map +1 -1
  160. package/lib/typescript/roomStore/chatSettingsSlice.d.ts.map +1 -1
  161. package/lib/typescript/types/types.d.ts +8 -5
  162. package/lib/typescript/types/types.d.ts.map +1 -1
  163. package/package.json +6 -1
  164. package/src/assets/icons.tsx +29 -9
  165. package/src/components/Header/HeaderRoomList.tsx +20 -5
  166. package/src/components/MainComponents/ChatRoom.tsx +29 -7
  167. package/src/components/MainComponents/MessageList.tsx +2 -1
  168. package/src/components/MainComponents/ProfileImagePlaceholder.tsx +5 -5
  169. package/src/components/MainComponents/RoomList.tsx +65 -34
  170. package/src/components/Menu/HeaderRoomListMenu.tsx +209 -64
  171. package/src/components/Modals/AttachSheet/AttachSheet.tsx +318 -86
  172. package/src/components/Modals/ChatProfileModal/ChatProfileModal.tsx +598 -279
  173. package/src/components/Modals/ChatProfileModal/ReportChatModal.tsx +244 -0
  174. package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +4 -2
  175. package/src/components/Modals/Modal/Modal.tsx +7 -0
  176. package/src/components/Modals/NewChatModal/NewChatModal.tsx +219 -117
  177. package/src/components/Modals/ProfileHeader/ProfileHeader.tsx +615 -0
  178. package/src/components/Modals/ProfileHeader/ProfileMenu.tsx +217 -0
  179. package/src/components/Modals/SelectUsersModal/SelectUsersModal.tsx +14 -4
  180. package/src/components/Modals/UserProfileModal/UserProfileModal.tsx +617 -307
  181. package/src/components/Modals/UserSettingsModal/UserSettingsModal.tsx +75 -58
  182. package/src/components/Modals/styledModalComponents.tsx +9 -2
  183. package/src/components/RoomComponents/ChatRoomItem.tsx +14 -7
  184. package/src/components/styled/SendInput.tsx +83 -2
  185. package/src/components/styled/StyledComponents.ts +9 -3
  186. package/src/components/styled/StyledInputComponents/StyledInputComponents.tsx +11 -4
  187. package/src/helpers/getChatBackground.ts +18 -0
  188. package/src/helpers/mediaLibraryRuntime.ts +25 -0
  189. package/src/helpers/sheetGestures.ts +25 -0
  190. package/src/i18n/strings.ts +121 -1
  191. package/src/networking/api-requests/user.api.ts +71 -0
  192. package/src/roomStore/chatSettingsSlice.ts +3 -1
  193. package/src/types/types.ts +8 -5
@@ -3,55 +3,72 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.default = void 0;
6
+ exports.searchScrollTarget = exports.default = exports.chatInitials = void 0;
7
7
  var _react = _interopRequireWildcard(require("react"));
8
+ var _reactNative = require("react-native");
9
+ var _reactRedux = require("react-redux");
10
+ var ImagePicker = _interopRequireWildcard(require("expo-image-picker"));
8
11
  var _useFileToken = require("../../../hooks/useFileToken");
9
12
  var _secureFileUrl = require("../../../helpers/secureFileUrl");
10
13
  var _styledModalComponents = require("../styledModalComponents");
11
- var _reactNative = require("react-native");
12
- var _ModalHeaderComponent = _interopRequireDefault(require("../ModalHeaderComponent"));
13
14
  var _ProfileImagePlaceholder = require("../../MainComponents/ProfileImagePlaceholder");
14
- var _reactRedux = require("react-redux");
15
15
  var _roomStore = require("../../../roomStore");
16
16
  var _auth = require("../../../networking/api-requests/auth.api");
17
17
  var _xmppProvider = require("../../../context/xmppProvider");
18
18
  var _roomsSlice = require("../../../roomStore/roomsSlice");
19
- var ImagePicker = _interopRequireWildcard(require("expo-image-picker"));
20
19
  var _Loader = _interopRequireDefault(require("../../styled/Loader"));
21
- var _Button = _interopRequireDefault(require("../../styled/Button"));
22
20
  var _icons = require("../../../assets/icons");
23
21
  var _useChatSettingState = require("../../../hooks/useChatSettingState");
24
22
  var _typography = require("../../../helpers/typography");
25
23
  var _getElementFont = require("../../../helpers/getElementFont");
24
+ var _getIconColor = require("../../../helpers/getIconColor");
26
25
  var _rooms = require("../../../networking/api-requests/rooms.api");
27
26
  var _chatSettingsSlice = require("../../../roomStore/chatSettingsSlice");
28
27
  var _MODAL_TYPES = require("../../../helpers/constants/MODAL_TYPES");
29
- var _DropdownMenu = _interopRequireDefault(require("../../DropdownMenu/DropdownMenu"));
30
28
  var _SelectUsersModal = _interopRequireDefault(require("../SelectUsersModal/SelectUsersModal"));
31
29
  var _ToastContext = require("../../../context/ToastContext");
30
+ var _useT = require("../../../i18n/useT");
32
31
  var _DeleteChatModal = _interopRequireDefault(require("./DeleteChatModal"));
32
+ var _ReportChatModal = _interopRequireDefault(require("./ReportChatModal"));
33
+ var _ProfileHeader = require("../ProfileHeader/ProfileHeader");
34
+ var _ProfileMenu = _interopRequireDefault(require("../ProfileHeader/ProfileMenu"));
33
35
  var _jsxRuntime = require("react/jsx-runtime");
34
36
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
35
37
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
38
+ /** @format */
39
+
40
+ /**
41
+ * Where the header should sit when search opens: tucked away at the
42
+ * collapsed offset when the picture is still expanded, and left exactly
43
+ * where it is when the user already scrolled it away.
44
+ */
45
+ const searchScrollTarget = (isCollapsed, collapseDistance) => isCollapsed ? null : collapseDistance;
46
+
47
+ /** Two uppercase letters for a chat without a picture, same rule the
48
+ * avatar placeholder uses for people. */
49
+ exports.searchScrollTarget = searchScrollTarget;
50
+ const chatInitials = name => {
51
+ const words = (name || '').trim().split(/\s+/).filter(Boolean);
52
+ const letters = (words.length >= 2 ? [words[0][0], words[1][0]] : [words[0]?.[0], words[0]?.[1]]).filter(c => !!c && /[\p{L}\p{N}]/u.test(c));
53
+ return letters.join('').toUpperCase();
54
+ };
55
+ exports.chatInitials = chatInitials;
36
56
  const ChatProfileModal = ({
37
57
  handleCloseModal
38
58
  }) => {
59
+ const t = (0, _useT.useT)();
39
60
  const [loading, setLoading] = (0, _react.useState)(false);
40
- const [visible, setVisible] = (0, _react.useState)(false);
41
- const [isModalOpen, setIsModalOpen] = (0, _react.useState)(false);
61
+ const [isDeleteOpen, setIsDeleteOpen] = (0, _react.useState)(false);
62
+ const [isReportOpen, setIsReportOpen] = (0, _react.useState)(false);
63
+ const [memberQuery, setMemberQuery] = (0, _react.useState)('');
64
+ const [isSearchOpen, setIsSearchOpen] = (0, _react.useState)(false);
65
+ // Measured, not guessed: this screen renders inside whatever frame the
66
+ // host gives it (the demo app keeps its own tab bar above), so the
67
+ // window height would overshoot.
68
+ const [viewportHeight, setViewportHeight] = (0, _react.useState)(0);
42
69
  const {
43
70
  showToast
44
71
  } = (0, _ToastContext.useToast)();
45
- const chatMenuOptions = (0, _react.useMemo)(() => [{
46
- label: 'Delete chat',
47
- icon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.DeleteIcon, {}),
48
- onClick: () => {
49
- setIsModalOpen(true);
50
- },
51
- styles: {
52
- color: 'red'
53
- }
54
- }], []);
55
72
  const dispatch = (0, _reactRedux.useDispatch)();
56
73
  const {
57
74
  client
@@ -63,6 +80,22 @@ const ChatProfileModal = ({
63
80
  const activeRoom = (0, _reactRedux.useSelector)(state => (0, _roomStore.getActiveRoom)(state));
64
81
  const fileToken = (0, _useFileToken.useFileToken)();
65
82
 
83
+ // Drives both the hero's parallax and the collapsed bar's fade-in. One
84
+ // shared value, written natively by the ScrollView.
85
+ const scrollY = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
86
+ const scrollRef = (0, _react.useRef)(null);
87
+ const searchInputRef = (0, _react.useRef)(null);
88
+ const {
89
+ collapseDistance
90
+ } = (0, _ProfileHeader.useHeaderMetrics)();
91
+ // `scrollY` is driven natively, so its value can't be read back with
92
+ // addListener. The Animated.event's `listener` still fires on the JS
93
+ // side, which is enough to know which state the header is in.
94
+ const [isCollapsed, setIsCollapsed] = (0, _react.useState)(false);
95
+ // Set when search collapsed the header itself, so only then does
96
+ // dismissing search (or the keyboard) expand it again.
97
+ const collapsedForSearchRef = (0, _react.useRef)(false);
98
+
66
99
  // Pull live member list from the MUC when the modal opens — REST
67
100
  // hydration only fills the *count* (item.participants /
68
101
  // item.members.length); the actual roster comes from a `getRoomMembers`
@@ -76,6 +109,11 @@ const ChatProfileModal = ({
76
109
  }
77
110
  }
78
111
  }, [client, activeRoom?.jid]);
112
+ (0, _react.useEffect)(() => {
113
+ if (!activeRoom) {
114
+ dispatch((0, _chatSettingsSlice.setActiveModal)(undefined));
115
+ }
116
+ }, [activeRoom, dispatch]);
79
117
  const onUpload = async () => {
80
118
  let loadingSet = false;
81
119
  try {
@@ -88,7 +126,7 @@ const ChatProfileModal = ({
88
126
  const perm = await ImagePicker.requestMediaLibraryPermissionsAsync();
89
127
  if (!perm.granted) {
90
128
  _reactNative.Alert.alert('Permission required', 'Photo library permission is needed to select images.', [{
91
- text: 'Cancel',
129
+ text: t('action.cancel'),
92
130
  onPress: () => setLoading(false),
93
131
  style: 'cancel'
94
132
  }, {
@@ -150,7 +188,17 @@ const ChatProfileModal = ({
150
188
  }
151
189
  }
152
190
  };
153
- const handleDeleteUser = async userId => {
191
+ const onRemoveIcon = () => {
192
+ client?.setRoomImageStanza(activeRoom?.jid || '', '', 'icon', 'none');
193
+ dispatch((0, _roomsSlice.updateRoom)({
194
+ jid: activeRoom?.jid || '',
195
+ updates: {
196
+ icon: null
197
+ }
198
+ }));
199
+ };
200
+ const handleDeleteUser = async user => {
201
+ const userId = user.xmppUsername;
154
202
  try {
155
203
  await (0, _rooms.deleteRoomMember)({
156
204
  roomId: activeRoom?.jid?.split('@')[0] || '',
@@ -159,7 +207,7 @@ const ChatProfileModal = ({
159
207
  dispatch((0, _roomsSlice.updateRoom)({
160
208
  jid: activeRoom?.jid || '',
161
209
  updates: {
162
- roomMembers: activeRoom?.roomMembers?.filter(user => user.xmppUsername !== userId)
210
+ roomMembers: activeRoom?.roomMembers?.filter(member => member.xmppUsername !== userId)
163
211
  }
164
212
  }));
165
213
  showToast({
@@ -178,14 +226,15 @@ const ChatProfileModal = ({
178
226
  });
179
227
  }
180
228
  };
181
- const onRemoveClick = async () => {
182
- client?.setRoomImageStanza(activeRoom?.jid || '', '', 'icon', 'none');
183
- dispatch((0, _roomsSlice.updateRoom)({
184
- jid: activeRoom?.jid || '',
185
- updates: {
186
- icon: null
187
- }
188
- }));
229
+ const confirmRemoveMember = user => {
230
+ _reactNative.Alert.alert(t('modal.chatProfile.removeMember'), `${user.firstName} ${user.lastName}`.trim(), [{
231
+ text: t('action.cancel'),
232
+ style: 'cancel'
233
+ }, {
234
+ text: t('action.delete'),
235
+ style: 'destructive',
236
+ onPress: () => handleDeleteUser(user)
237
+ }]);
189
238
  };
190
239
  const handleUserAvatarClick = user => {
191
240
  dispatch((0, _chatSettingsSlice.setActiveModal)(_MODAL_TYPES.MODAL_TYPES.PROFILE));
@@ -199,209 +248,499 @@ const ChatProfileModal = ({
199
248
  refreshToken: ''
200
249
  }));
201
250
  };
202
- const menuOptions = (0, _react.useMemo)(() => userId => [{
203
- label: 'Appoint as an admin',
204
- icon: null,
205
- onClick: () => {
206
- dispatch((0, _chatSettingsSlice.setActiveModal)(_MODAL_TYPES.MODAL_TYPES.PROFILE));
207
- console.log('Profile clicked');
251
+
252
+ // Leaving is XMPP-side (an unavailable presence to the MUC); dropping the
253
+ // room from the store is what actually takes the user out of the chat in
254
+ // the UI, and the `!activeRoom` effect above then closes this modal.
255
+ const handleLeave = () => {
256
+ _reactNative.Alert.alert(t('modal.leaveChat.title'), t('modal.leaveChat.description'), [{
257
+ text: t('action.cancel'),
258
+ style: 'cancel'
259
+ }, {
260
+ text: t('action.leave'),
261
+ style: 'destructive',
262
+ onPress: () => {
263
+ try {
264
+ client?.leaveTheRoomStanza?.(activeRoom?.jid || '');
265
+ } catch (error) {
266
+ console.error('Failed to leave the room:', error);
267
+ }
268
+ dispatch((0, _roomsSlice.deleteRoom)({
269
+ jid: activeRoom?.jid || ''
270
+ }));
271
+ handleCloseModal?.();
272
+ }
273
+ }]);
274
+ };
275
+ const isModerator = activeRoom?.role === 'moderator';
276
+ const canEditIcon = !config?.disableChatInfo?.disableIconEdit && activeRoom?.role !== 'participant';
277
+ const canAddMembers = isModerator && activeRoom?.type === 'group';
278
+ const canDeleteChat = isModerator && activeRoom?.type !== 'private';
279
+ const hasIcon = !!activeRoom?.icon;
280
+ const scrollHeaderTo = y => scrollRef.current?.scrollTo?.({
281
+ y,
282
+ animated: true
283
+ });
284
+
285
+ // Opening search from an expanded header tucks the picture away so the
286
+ // results and the keyboard have room; from an already-collapsed header
287
+ // it just opens the field. Whatever it collapsed, it puts back.
288
+ const openMemberSearch = () => {
289
+ const target = searchScrollTarget(isCollapsed, collapseDistance);
290
+ if (target !== null) {
291
+ collapsedForSearchRef.current = true;
292
+ scrollHeaderTo(target);
208
293
  }
209
- }, {
210
- label: 'Unban',
211
- icon: null,
212
- onClick: () => {
213
- dispatch((0, _chatSettingsSlice.setActiveModal)(_MODAL_TYPES.MODAL_TYPES.SETTINGS));
214
- console.log('Settings clicked');
294
+ setIsSearchOpen(true);
295
+ setTimeout(() => searchInputRef.current?.focus(), 50);
296
+ };
297
+ const restoreHeader = () => {
298
+ if (!collapsedForSearchRef.current) {
299
+ return;
300
+ }
301
+ collapsedForSearchRef.current = false;
302
+ scrollHeaderTo(0);
303
+ };
304
+ const closeMemberSearch = () => {
305
+ setMemberQuery('');
306
+ setIsSearchOpen(false);
307
+ _reactNative.Keyboard.dismiss();
308
+ restoreHeader();
309
+ };
310
+
311
+ // Putting the keyboard away is the other way out of search, so it
312
+ // restores the header too — the field stays, results and all.
313
+ (0, _react.useEffect)(() => {
314
+ if (!isSearchOpen) {
315
+ return undefined;
215
316
  }
317
+ const sub = _reactNative.Keyboard.addListener('keyboardDidHide', restoreHeader);
318
+ return () => sub.remove();
319
+ }, [isSearchOpen]); // eslint-disable-line react-hooks/exhaustive-deps
320
+
321
+ const heroActions = (0, _react.useMemo)(() => [{
322
+ key: 'search',
323
+ label: t('action.search'),
324
+ icon: color => /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.SearchIcon, {
325
+ color: color
326
+ }),
327
+ onPress: openMemberSearch
328
+ }, {
329
+ key: 'leave',
330
+ label: t('action.leave'),
331
+ icon: color => /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.LeaveIcon, {
332
+ color: color
333
+ }),
334
+ onPress: handleLeave
216
335
  }, {
217
- label: 'Delete',
218
- icon: null,
219
- onClick: e => {
220
- e?.preventDefault();
221
- handleDeleteUser(userId);
336
+ key: 'report',
337
+ label: t('action.report'),
338
+ icon: color => /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.ReportIcon, {
339
+ color: color
340
+ }),
341
+ onPress: () => setIsReportOpen(true)
342
+ }], [t, activeRoom?.jid] // eslint-disable-line react-hooks/exhaustive-deps
343
+ );
344
+
345
+ // "…" menu. Edit is the chat picture — the only chat detail this SDK can
346
+ // actually change (there is no update-chat endpoint for title/description),
347
+ // so it is only offered when the picture is editable.
348
+ const menuItems = (0, _react.useMemo)(() => {
349
+ const items = [];
350
+ if (canEditIcon) {
351
+ items.push({
352
+ key: 'edit',
353
+ label: t('action.edit'),
354
+ icon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.EditIcon, {
355
+ color: "#141414",
356
+ width: 18,
357
+ height: 18
358
+ }),
359
+ onPress: onUpload
360
+ });
361
+ if (hasIcon) {
362
+ items.push({
363
+ key: 'remove-photo',
364
+ label: t('modal.chatProfile.removePhoto'),
365
+ icon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.DeleteIcon, {
366
+ width: 18,
367
+ height: 18
368
+ }),
369
+ onPress: onRemoveIcon
370
+ });
371
+ }
222
372
  }
223
- }], []);
224
- (0, _react.useEffect)(() => {
225
- if (!activeRoom) {
226
- dispatch((0, _chatSettingsSlice.setActiveModal)(undefined));
373
+ // The hero's Search button is off-screen once collapsed, so the menu
374
+ // carries it — the design's collapsed menu does the same.
375
+ if (isCollapsed) {
376
+ items.push({
377
+ key: 'search',
378
+ label: t('action.search'),
379
+ icon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.SearchIcon, {
380
+ color: "#141414",
381
+ width: 18,
382
+ height: 18
383
+ }),
384
+ onPress: openMemberSearch
385
+ });
227
386
  }
228
- }, [activeRoom, dispatch]);
387
+ items.push({
388
+ key: 'report',
389
+ label: t('action.report'),
390
+ icon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.ReportIcon, {
391
+ color: "#141414",
392
+ width: 18,
393
+ height: 18
394
+ }),
395
+ onPress: () => setIsReportOpen(true)
396
+ });
397
+ if (canDeleteChat) {
398
+ items.push({
399
+ key: 'delete-and-leave',
400
+ label: t('action.deleteAndLeave'),
401
+ icon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.DeleteIcon, {
402
+ width: 18,
403
+ height: 18
404
+ }),
405
+ destructive: true,
406
+ onPress: () => setIsDeleteOpen(true)
407
+ });
408
+ }
409
+ return items;
410
+ }, [canEditIcon, canDeleteChat, hasIcon, isCollapsed, t]); // eslint-disable-line react-hooks/exhaustive-deps
411
+
229
412
  if (!activeRoom) {
230
413
  return null;
231
414
  }
415
+ const title = activeRoom.title || activeRoom.name;
416
+ const memberCount = activeRoom.usersCnt ?? 0;
417
+ const subtitle = t(memberCount === 1 ? 'modal.chatProfile.memberCountSingular' : 'modal.chatProfile.memberCountPlural', {
418
+ count: memberCount
419
+ });
420
+ const heroImage = (0, _secureFileUrl.appendFileToken)(activeRoom.icon, fileToken) || null;
421
+ // A single flat colour behind the initials — the config's avatar colour
422
+ // when set, otherwise the primary. (The old per-name hash colour is what
423
+ // made the header read as two different greens next to the members' own
424
+ // avatars.)
425
+ const heroColor = config?.colors?.avatar || (0, _getIconColor.getIconColor)(config);
426
+ const members = activeRoom.roomMembers ?? [];
427
+ const query = memberQuery.trim().toLowerCase();
428
+ const visibleMembers = query ? members.filter(user => `${user.firstName ?? ''} ${user.lastName ?? ''}`.toLowerCase().includes(query)) : members;
232
429
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styledModalComponents.ModalContainerFullScreen, {
233
- style: {
234
- position: 'relative'
235
- },
236
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_ModalHeaderComponent.default, {
237
- handleCloseModal: handleCloseModal,
238
- headerTitle: 'Chat Profile',
239
- titleStyle: (0, _typography.chatTextStyle)(config?.typography?.profile?.screenTitle),
240
- rightMenu: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
241
- children: [activeRoom?.type === 'public' && /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button.default, {
242
- EndIcon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.QrIcon, {}),
243
- onPress: () => setVisible(true)
244
- }), activeRoom.role === 'moderator' && activeRoom.type !== 'private' && /*#__PURE__*/(0, _jsxRuntime.jsx)(_DropdownMenu.default, {
245
- position: "right",
246
- options: chatMenuOptions,
247
- openButton: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button.default, {
248
- style: {
249
- padding: 8,
250
- height: 40
251
- },
252
- EndIcon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.MoreIcon, {}),
253
- unstyled: true
254
- })
255
- })]
256
- })
257
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
258
- style: {
259
- width: '100%',
260
- flex: 1
261
- },
262
- contentContainerStyle: {
263
- flexGrow: 1
264
- },
265
- showsVerticalScrollIndicator: true,
266
- children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styledModalComponents.CenterContainer, {
267
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_ProfileImagePlaceholder.ProfileImagePlaceholder, {
268
- name: activeRoom.title || activeRoom.name,
269
- icon: (0, _secureFileUrl.appendFileToken)(activeRoom.icon, fileToken)
270
- // Two gates control whether the icon is editable:
271
- // 1. role — only non-participants could edit before.
272
- // 2. NEW: `disableChatInfo.disableIconEdit` — hard kill from
273
- // config so a consumer can make the icon read-only for
274
- // everyone (e.g. branded rooms, patient-facing apps).
275
- // Both must allow it for the picker/remove affordances to show.
276
- ,
277
- upload: {
278
- onUpload,
279
- active: !config?.disableChatInfo?.disableIconEdit && activeRoom?.role !== 'participant'
280
- },
281
- remove: {
282
- enabled: !config?.disableChatInfo?.disableIconEdit,
283
- onRemoveClick
284
- },
285
- role: activeRoom?.role,
286
- size: 128
287
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styledModalComponents.UserInfo, {
288
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_styledModalComponents.UserName, {
289
- fontSize: config?.typography?.profile?.title?.fontSize,
290
- fontWeight: config?.typography?.profile?.title?.fontWeight,
291
- children: activeRoom.title || activeRoom.name
292
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styledModalComponents.UserStatus, {
293
- style: (0, _getElementFont.getElementFont)(config, 'profileStatus'),
294
- children: [activeRoom.usersCnt, ' ', activeRoom.usersCnt > 1 ? 'members' : 'member']
295
- })]
296
- }), activeRoom.role === 'moderator' && activeRoom.type === 'group' && /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
297
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_SelectUsersModal.default, {})
298
- }), !config?.disableChatInfo?.disableDescription && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styledModalComponents.BorderedContainer, {
299
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_styledModalComponents.LabelData, {
300
- style: (0, _getElementFont.getElementFont)(config, 'profileSectionLabel'),
301
- children: "Description"
302
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_styledModalComponents.Label, {
303
- children: activeRoom?.description || '—'
430
+ style: styles.screen,
431
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Animated.ScrollView, {
432
+ ref: scrollRef,
433
+ style: styles.scroll
434
+ // The collapsed state has to be reachable even when the member list
435
+ // is short: without this the content was shorter than the viewport,
436
+ // so the scroll had nowhere to go and simply bounced back.
437
+ ,
438
+ contentContainerStyle: [styles.scrollContent, viewportHeight > 0 && {
439
+ minHeight: viewportHeight + collapseDistance
440
+ }],
441
+ onLayout: e => setViewportHeight(e.nativeEvent.layout.height),
442
+ showsVerticalScrollIndicator: false,
443
+ keyboardShouldPersistTaps: "handled",
444
+ scrollEventThrottle: 16,
445
+ onScroll: _reactNative.Animated.event([{
446
+ nativeEvent: {
447
+ contentOffset: {
448
+ y: scrollY
449
+ }
450
+ }
451
+ }], {
452
+ useNativeDriver: true,
453
+ listener: e => {
454
+ const y = e?.nativeEvent?.contentOffset?.y ?? 0;
455
+ setIsCollapsed(y >= collapseDistance - 8);
456
+ }
457
+ }),
458
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_ProfileHeader.ProfileHero, {
459
+ title: title,
460
+ subtitle: subtitle,
461
+ imageUri: heroImage,
462
+ fallbackColor: heroColor,
463
+ initials: chatInitials(title),
464
+ scrollY: scrollY,
465
+ actions: heroActions,
466
+ titleStyle: (0, _typography.chatTextStyle)(config?.typography?.profile?.title),
467
+ subtitleStyle: (0, _getElementFont.getElementFont)(config, 'profileStatus')
468
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
469
+ style: styles.body,
470
+ children: [!config?.disableChatInfo?.disableDescription && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
471
+ style: styles.card,
472
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
473
+ style: [styles.cardLabel, (0, _getElementFont.getElementFont)(config, 'profileSectionLabel')],
474
+ children: t('modal.chatProfile.description')
475
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
476
+ style: styles.cardValue,
477
+ children: activeRoom.description || '—'
304
478
  })]
305
- }), !config?.disableChatInfo?.disableType && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styledModalComponents.BorderedContainer, {
306
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_styledModalComponents.LabelData, {
307
- style: (0, _getElementFont.getElementFont)(config, 'profileSectionLabel'),
308
- children: "Chat type"
309
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_styledModalComponents.Label, {
479
+ }), !config?.disableChatInfo?.disableType && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
480
+ style: styles.card,
481
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
482
+ style: [styles.cardLabel, (0, _getElementFont.getElementFont)(config, 'profileSectionLabel')],
483
+ children: t('modal.chatProfile.chatType')
484
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
485
+ style: styles.cardValue,
310
486
  children: activeRoom.type || '—'
311
487
  })]
312
- }), !config?.disableChatInfo?.hideMembers && /*#__PURE__*/(0, _jsxRuntime.jsx)(_styledModalComponents.BorderedContainer, {
313
- style: {
314
- paddingHorizontal: 16,
315
- paddingVertical: 8
316
- },
317
- children: loading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Loader.default, {}) : (activeRoom?.roomMembers?.length ?? 0) === 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_styledModalComponents.Label, {
318
- style: {
319
- opacity: 0.6,
320
- textAlign: 'center',
321
- paddingVertical: 8
322
- },
323
- children: "Member list unavailable"
324
- }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
325
- children: activeRoom?.roomMembers?.map((user, index) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
326
- style: {
327
- flexDirection: 'column',
328
- alignItems: 'flex-start'
488
+ }), !config?.disableChatInfo?.hideMembers && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
489
+ style: styles.card,
490
+ children: [canAddMembers && /*#__PURE__*/(0, _jsxRuntime.jsx)(_SelectUsersModal.default, {
491
+ trigger: open => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
492
+ testID: "chat-profile-add-members",
493
+ activeOpacity: 0.7,
494
+ style: styles.addMembersRow,
495
+ onPress: open,
496
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.AddNewIcon, {
497
+ color: (0, _getIconColor.getIconColor)(config)
498
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
499
+ style: styles.addMembersLabel,
500
+ children: t('modal.chatProfile.addMembers')
501
+ })]
502
+ })
503
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
504
+ style: [styles.cardLabel, (0, _getElementFont.getElementFont)(config, 'profileSectionLabel')],
505
+ children: t('modal.chatProfile.membersLabel', {
506
+ count: memberCount
507
+ })
508
+ }), isSearchOpen && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
509
+ style: styles.searchRow,
510
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.SearchIcon, {
511
+ color: "#8C8C8C",
512
+ width: 18,
513
+ height: 18
514
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
515
+ testID: "chat-profile-member-search",
516
+ ref: searchInputRef,
517
+ style: styles.searchInput,
518
+ value: memberQuery,
519
+ onChangeText: setMemberQuery,
520
+ placeholder: t('search.members'),
521
+ placeholderTextColor: "#8C8C8C",
522
+ autoCorrect: false,
523
+ returnKeyType: "search"
524
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
525
+ testID: "chat-profile-search-close",
526
+ activeOpacity: 0.7,
527
+ hitSlop: {
528
+ top: 8,
529
+ bottom: 8,
530
+ left: 8,
531
+ right: 8
329
532
  },
330
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
331
- style: {
332
- flexDirection: 'row',
333
- justifyContent: 'flex-start',
334
- paddingHorizontal: 8,
335
- paddingVertical: 0,
336
- alignItems: 'center',
337
- width: '100%'
338
- },
339
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
340
- style: {
341
- flexDirection: 'row',
342
- alignItems: 'center',
343
- justifyContent: 'flex-start',
344
- gap: 16
345
- }
346
- // `disableChatInfo.disableMemberTap` blocks the
347
- // tap entirely so the user-profile popup never
348
- // opens. `disableMemberProfileActions` only hid
349
- // the action buttons INSIDE that popup, leaving
350
- // the popup itself to open on tap — this gate
351
- // closes the door. Customer-reported #16.
352
- ,
353
- onPress: config?.disableChatInfo?.disableMemberTap ? undefined : () => handleUserAvatarClick(user),
354
- disabled: !!config?.disableChatInfo?.disableMemberTap,
355
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_ProfileImagePlaceholder.ProfileImagePlaceholder, {
356
- name: `${user.firstName} ${user.lastName}`,
357
- size: 40
358
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
359
- style: {
360
- flexDirection: 'column',
361
- gap: 2,
362
- alignItems: 'flex-start',
363
- justifyContent: 'center'
364
- },
365
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_styledModalComponents.Label, {
366
- style: [{
367
- fontSize: 16,
368
- fontWeight: 600
369
- }, (0, _typography.chatTextStyle)(config?.typography?.profile?.memberName)],
370
- children: [user.firstName, " ", user.lastName]
371
- }), user.last_active && /*#__PURE__*/(0, _jsxRuntime.jsx)(_styledModalComponents.LabelData, {
372
- style: (0, _getElementFont.getElementFont)(config, 'profileSectionLabel'),
373
- children: new Date(user.last_active * 1000).toLocaleString()
374
- })]
533
+ onPress: closeMemberSearch,
534
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
535
+ style: styles.searchClose,
536
+ children: "\xD7"
537
+ })
538
+ })]
539
+ }), loading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Loader.default, {}) : visibleMembers.length === 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
540
+ testID: "chat-profile-members-empty",
541
+ style: styles.emptyMembers,
542
+ children: members.length === 0 ? t('modal.chatProfile.membersUnavailable') : t('search.noResults')
543
+ }) : visibleMembers.map((user, index) => {
544
+ const banned = user.ban_status === 'banned';
545
+ const roleLabel = user.role && user.role !== 'none' && user.role !== 'participant' ? user.role : null;
546
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
547
+ style: [styles.memberRow, index > 0 && styles.memberDivider],
548
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
549
+ style: styles.memberMain
550
+ // `disableChatInfo.disableMemberTap` blocks the tap
551
+ // entirely so the user-profile popup never opens.
552
+ // `disableMemberProfileActions` only hid the action
553
+ // buttons INSIDE that popup, leaving the popup itself
554
+ // to open on tap — this gate closes the door.
555
+ // Customer-reported #16.
556
+ ,
557
+ onPress: config?.disableChatInfo?.disableMemberTap ? undefined : () => handleUserAvatarClick(user),
558
+ disabled: !!config?.disableChatInfo?.disableMemberTap,
559
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_ProfileImagePlaceholder.ProfileImagePlaceholder, {
560
+ name: `${user.firstName} ${user.lastName}`,
561
+ size: 40
562
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
563
+ style: styles.memberText,
564
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
565
+ numberOfLines: 1,
566
+ style: [styles.memberName, (0, _typography.chatTextStyle)(config?.typography?.profile?.memberName)],
567
+ children: [user.firstName, " ", user.lastName]
568
+ }), !!user.last_active && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
569
+ numberOfLines: 1,
570
+ style: [styles.memberStatus, (0, _getElementFont.getElementFont)(config, 'profileSectionLabel')],
571
+ children: new Date(user.last_active * 1000).toLocaleString()
375
572
  })]
376
- }), user.role && user.role !== 'none' && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
377
- style: {
378
- backgroundColor: user.ban_status !== 'banned' ? '#F3F6FC' : '#FFEBEE',
379
- color: user.ban_status !== 'banned' ? '#0052CD' : '#F44336',
380
- padding: 5,
381
- borderRadius: 16,
382
- fontSize: 12
383
- },
384
- children: user.role
385
- }), stateUser.xmppUsername !== user.xmppUsername && activeRoom.role === 'moderator' && activeRoom.type !== 'private' && /*#__PURE__*/(0, _jsxRuntime.jsx)(_DropdownMenu.default, {
386
- options: menuOptions(user.xmppUsername),
387
- openButton: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button.default, {
388
- onPress: () => {},
389
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
390
- children: "More Options"
391
- })
392
- }),
393
- onClose: () => console.log('Dropdown closed')
394
573
  })]
395
- }), index < (activeRoom?.roomMembers?.length || 0) - 1 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_styledModalComponents.Divider, {})]
396
- }, user.xmppUsername))
397
- })
574
+ }), (banned || roleLabel) && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
575
+ style: [styles.memberRole, banned && styles.memberBanned],
576
+ children: banned ? t('status.banned') : roleLabel
577
+ }), isModerator && activeRoom.type !== 'private' && stateUser.xmppUsername !== user.xmppUsername && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
578
+ testID: `chat-profile-remove-${user.xmppUsername}`,
579
+ activeOpacity: 0.7,
580
+ hitSlop: {
581
+ top: 8,
582
+ bottom: 8,
583
+ left: 8,
584
+ right: 8
585
+ },
586
+ style: styles.memberRemove,
587
+ onPress: () => confirmRemoveMember(user),
588
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.DeleteIcon, {
589
+ width: 20,
590
+ height: 20
591
+ })
592
+ })]
593
+ }, user.xmppUsername);
594
+ })]
398
595
  })]
596
+ })]
597
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_ProfileHeader.ProfileTopBar, {
598
+ title: title,
599
+ subtitle: subtitle,
600
+ imageUri: heroImage,
601
+ fallbackColor: heroColor,
602
+ initials: chatInitials(title),
603
+ scrollY: scrollY,
604
+ onBack: handleCloseModal,
605
+ titleStyle: (0, _typography.chatTextStyle)(config?.typography?.profile?.screenTitle),
606
+ menu: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ProfileMenu.default, {
607
+ items: menuItems
399
608
  })
400
609
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_DeleteChatModal.default, {
401
- isModalOpen: isModalOpen,
402
- setIsModalOpen: setIsModalOpen
610
+ isModalOpen: isDeleteOpen,
611
+ setIsModalOpen: setIsDeleteOpen
612
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_ReportChatModal.default, {
613
+ visible: isReportOpen,
614
+ onClose: () => setIsReportOpen(false)
403
615
  })]
404
616
  });
405
617
  };
618
+ const styles = _reactNative.StyleSheet.create({
619
+ screen: {
620
+ position: 'relative',
621
+ backgroundColor: '#F2F3F5'
622
+ },
623
+ scroll: {
624
+ width: '100%',
625
+ flex: 1
626
+ },
627
+ scrollContent: {
628
+ paddingBottom: 32
629
+ },
630
+ body: {
631
+ padding: 12,
632
+ gap: 12
633
+ },
634
+ card: {
635
+ backgroundColor: '#FFFFFF',
636
+ borderRadius: 14,
637
+ padding: 16,
638
+ shadowColor: '#000',
639
+ shadowOffset: {
640
+ width: 0,
641
+ height: 1
642
+ },
643
+ shadowOpacity: 0.05,
644
+ shadowRadius: 4,
645
+ elevation: 1
646
+ },
647
+ cardLabel: {
648
+ color: '#8C8C8C',
649
+ fontSize: 14
650
+ },
651
+ cardValue: {
652
+ color: '#141414',
653
+ fontSize: 16,
654
+ marginTop: 4
655
+ },
656
+ addMembersRow: {
657
+ flexDirection: 'row',
658
+ alignItems: 'center',
659
+ gap: 12,
660
+ paddingBottom: 14,
661
+ marginBottom: 10,
662
+ borderBottomWidth: _reactNative.StyleSheet.hairlineWidth,
663
+ borderBottomColor: '#EFEFF2'
664
+ },
665
+ addMembersLabel: {
666
+ fontSize: 16,
667
+ fontWeight: '500',
668
+ color: '#141414'
669
+ },
670
+ searchRow: {
671
+ flexDirection: 'row',
672
+ alignItems: 'center',
673
+ gap: 8,
674
+ marginTop: 10,
675
+ paddingHorizontal: 12,
676
+ height: 40,
677
+ borderRadius: 12,
678
+ backgroundColor: '#F2F3F5'
679
+ },
680
+ searchInput: {
681
+ flex: 1,
682
+ fontSize: 15,
683
+ color: '#141414',
684
+ padding: 0
685
+ },
686
+ searchClose: {
687
+ fontSize: 22,
688
+ lineHeight: 24,
689
+ color: '#8C8C8C'
690
+ },
691
+ emptyMembers: {
692
+ opacity: 0.6,
693
+ textAlign: 'center',
694
+ paddingVertical: 8,
695
+ color: '#141414'
696
+ },
697
+ memberRow: {
698
+ flexDirection: 'row',
699
+ alignItems: 'center',
700
+ paddingVertical: 10
701
+ },
702
+ memberDivider: {
703
+ borderTopWidth: _reactNative.StyleSheet.hairlineWidth,
704
+ borderTopColor: '#EFEFF2'
705
+ },
706
+ memberMain: {
707
+ flex: 1,
708
+ flexDirection: 'row',
709
+ alignItems: 'center',
710
+ gap: 12
711
+ },
712
+ memberText: {
713
+ flex: 1
714
+ },
715
+ memberName: {
716
+ fontSize: 16,
717
+ fontWeight: '600',
718
+ color: '#141414'
719
+ },
720
+ memberStatus: {
721
+ fontSize: 13,
722
+ color: '#8C8C8C',
723
+ marginTop: 2
724
+ },
725
+ memberRole: {
726
+ fontSize: 13,
727
+ color: '#8C8C8C',
728
+ marginLeft: 8
729
+ },
730
+ memberBanned: {
731
+ color: '#E53935'
732
+ },
733
+ memberRemove: {
734
+ marginLeft: 12
735
+ },
736
+ menuButton: {
737
+ width: 40,
738
+ height: 40,
739
+ borderRadius: 20,
740
+ backgroundColor: 'rgba(0,0,0,0.28)',
741
+ alignItems: 'center',
742
+ justifyContent: 'center'
743
+ }
744
+ });
406
745
  var _default = exports.default = ChatProfileModal;
407
746
  //# sourceMappingURL=ChatProfileModal.js.map