stream-chat-react-native-core 9.4.0 → 9.5.0-beta.1
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.
- package/lib/commonjs/components/AttachmentPicker/components/AttachmentMediaPicker/AttachmentPickerItem.js +2 -1
- package/lib/commonjs/components/AttachmentPicker/components/AttachmentMediaPicker/AttachmentPickerItem.js.map +1 -1
- package/lib/commonjs/components/ChannelDetails/components/members/ChannelAddMembers.js +7 -4
- package/lib/commonjs/components/ChannelDetails/components/members/ChannelAddMembers.js.map +1 -1
- package/lib/commonjs/components/ChannelDetails/components/members/ChannelAllMembersModal.js +2 -23
- package/lib/commonjs/components/ChannelDetails/components/members/ChannelAllMembersModal.js.map +1 -1
- package/lib/commonjs/components/ChannelDetails/components/members/ChannelMemberList.js +124 -22
- package/lib/commonjs/components/ChannelDetails/components/members/ChannelMemberList.js.map +1 -1
- package/lib/commonjs/components/ChannelDetails/components/navigation-section/PinnedMessageItem.js +1 -1
- package/lib/commonjs/components/ChannelDetails/components/navigation-section/PinnedMessageItem.js.map +1 -1
- package/lib/commonjs/components/ChannelDetails/components/navigation-section/PinnedMessageList.js +1 -1
- package/lib/commonjs/components/ChannelDetails/components/navigation-section/PinnedMessageList.js.map +1 -1
- package/lib/commonjs/components/ChannelDetails/hooks/members/index.js +0 -11
- package/lib/commonjs/components/ChannelDetails/hooks/members/index.js.map +1 -1
- package/lib/commonjs/components/Message/hooks/useMessageActions.js +20 -3
- package/lib/commonjs/components/Message/hooks/useMessageActions.js.map +1 -1
- package/lib/commonjs/components/UIComponents/SearchInput.js +1 -3
- package/lib/commonjs/components/UIComponents/SearchInput.js.map +1 -1
- package/lib/commonjs/contexts/channelMemberListContext/ChannelMemberListContext.js +63 -0
- package/lib/commonjs/contexts/channelMemberListContext/ChannelMemberListContext.js.map +1 -0
- package/lib/commonjs/contexts/index.js +11 -0
- package/lib/commonjs/contexts/index.js.map +1 -1
- package/lib/commonjs/contexts/themeContext/utils/theme.js +8 -0
- package/lib/commonjs/contexts/themeContext/utils/theme.js.map +1 -1
- package/lib/commonjs/hooks/actions/useChannelActionItems.js +54 -4
- package/lib/commonjs/hooks/actions/useChannelActionItems.js.map +1 -1
- package/lib/commonjs/hooks/actions/useChannelMemberActionItems.js +22 -3
- package/lib/commonjs/hooks/actions/useChannelMemberActionItems.js.map +1 -1
- package/lib/commonjs/i18n/ar.json +9 -1
- package/lib/commonjs/i18n/en.json +8 -0
- package/lib/commonjs/i18n/es.json +9 -1
- package/lib/commonjs/i18n/fr.json +9 -1
- package/lib/commonjs/i18n/he.json +9 -1
- package/lib/commonjs/i18n/hi.json +9 -1
- package/lib/commonjs/i18n/it.json +9 -1
- package/lib/commonjs/i18n/ja.json +9 -1
- package/lib/commonjs/i18n/ko.json +9 -1
- package/lib/commonjs/i18n/nl.json +9 -1
- package/lib/commonjs/i18n/pt-br.json +9 -1
- package/lib/commonjs/i18n/ru.json +9 -1
- package/lib/commonjs/i18n/tr.json +9 -1
- package/lib/commonjs/version.json +1 -1
- package/lib/module/components/AttachmentPicker/components/AttachmentMediaPicker/AttachmentPickerItem.js +2 -1
- package/lib/module/components/AttachmentPicker/components/AttachmentMediaPicker/AttachmentPickerItem.js.map +1 -1
- package/lib/module/components/ChannelDetails/components/members/ChannelAddMembers.js +7 -4
- package/lib/module/components/ChannelDetails/components/members/ChannelAddMembers.js.map +1 -1
- package/lib/module/components/ChannelDetails/components/members/ChannelAllMembersModal.js +2 -23
- package/lib/module/components/ChannelDetails/components/members/ChannelAllMembersModal.js.map +1 -1
- package/lib/module/components/ChannelDetails/components/members/ChannelMemberList.js +124 -22
- package/lib/module/components/ChannelDetails/components/members/ChannelMemberList.js.map +1 -1
- package/lib/module/components/ChannelDetails/components/navigation-section/PinnedMessageItem.js +1 -1
- package/lib/module/components/ChannelDetails/components/navigation-section/PinnedMessageItem.js.map +1 -1
- package/lib/module/components/ChannelDetails/components/navigation-section/PinnedMessageList.js +1 -1
- package/lib/module/components/ChannelDetails/components/navigation-section/PinnedMessageList.js.map +1 -1
- package/lib/module/components/ChannelDetails/hooks/members/index.js +0 -11
- package/lib/module/components/ChannelDetails/hooks/members/index.js.map +1 -1
- package/lib/module/components/Message/hooks/useMessageActions.js +20 -3
- package/lib/module/components/Message/hooks/useMessageActions.js.map +1 -1
- package/lib/module/components/UIComponents/SearchInput.js +1 -3
- package/lib/module/components/UIComponents/SearchInput.js.map +1 -1
- package/lib/module/contexts/channelMemberListContext/ChannelMemberListContext.js +63 -0
- package/lib/module/contexts/channelMemberListContext/ChannelMemberListContext.js.map +1 -0
- package/lib/module/contexts/index.js +11 -0
- package/lib/module/contexts/index.js.map +1 -1
- package/lib/module/contexts/themeContext/utils/theme.js +8 -0
- package/lib/module/contexts/themeContext/utils/theme.js.map +1 -1
- package/lib/module/hooks/actions/useChannelActionItems.js +54 -4
- package/lib/module/hooks/actions/useChannelActionItems.js.map +1 -1
- package/lib/module/hooks/actions/useChannelMemberActionItems.js +22 -3
- package/lib/module/hooks/actions/useChannelMemberActionItems.js.map +1 -1
- package/lib/module/i18n/ar.json +9 -1
- package/lib/module/i18n/en.json +8 -0
- package/lib/module/i18n/es.json +9 -1
- package/lib/module/i18n/fr.json +9 -1
- package/lib/module/i18n/he.json +9 -1
- package/lib/module/i18n/hi.json +9 -1
- package/lib/module/i18n/it.json +9 -1
- package/lib/module/i18n/ja.json +9 -1
- package/lib/module/i18n/ko.json +9 -1
- package/lib/module/i18n/nl.json +9 -1
- package/lib/module/i18n/pt-br.json +9 -1
- package/lib/module/i18n/ru.json +9 -1
- package/lib/module/i18n/tr.json +9 -1
- package/lib/module/version.json +1 -1
- package/lib/typescript/components/ChannelDetails/components/members/ChannelAddMembers.d.ts.map +1 -1
- package/lib/typescript/components/ChannelDetails/components/members/ChannelAllMembersModal.d.ts +1 -1
- package/lib/typescript/components/ChannelDetails/components/members/ChannelAllMembersModal.d.ts.map +1 -1
- package/lib/typescript/components/ChannelDetails/components/members/ChannelMemberList.d.ts +4 -2
- package/lib/typescript/components/ChannelDetails/components/members/ChannelMemberList.d.ts.map +1 -1
- package/lib/typescript/components/ChannelDetails/hooks/members/index.d.ts +0 -1
- package/lib/typescript/components/ChannelDetails/hooks/members/index.d.ts.map +1 -1
- package/lib/typescript/components/Message/hooks/useMessageActions.d.ts.map +1 -1
- package/lib/typescript/contexts/channelMemberListContext/ChannelMemberListContext.d.ts +21 -0
- package/lib/typescript/contexts/channelMemberListContext/ChannelMemberListContext.d.ts.map +1 -0
- package/lib/typescript/contexts/componentsContext/defaultComponents.d.ts +1 -1
- package/lib/typescript/contexts/index.d.ts +1 -0
- package/lib/typescript/contexts/index.d.ts.map +1 -1
- package/lib/typescript/contexts/themeContext/ThemeContext.d.ts +8 -0
- package/lib/typescript/contexts/themeContext/ThemeContext.d.ts.map +1 -1
- package/lib/typescript/contexts/themeContext/utils/theme.d.ts +8 -0
- package/lib/typescript/contexts/themeContext/utils/theme.d.ts.map +1 -1
- package/lib/typescript/hooks/actions/useChannelActionItems.d.ts.map +1 -1
- package/lib/typescript/hooks/actions/useChannelMemberActionItems.d.ts.map +1 -1
- package/lib/typescript/i18n/ar.json +9 -1
- package/lib/typescript/i18n/en.json +8 -0
- package/lib/typescript/i18n/es.json +9 -1
- package/lib/typescript/i18n/fr.json +9 -1
- package/lib/typescript/i18n/he.json +9 -1
- package/lib/typescript/i18n/hi.json +9 -1
- package/lib/typescript/i18n/it.json +9 -1
- package/lib/typescript/i18n/ja.json +9 -1
- package/lib/typescript/i18n/ko.json +9 -1
- package/lib/typescript/i18n/nl.json +9 -1
- package/lib/typescript/i18n/pt-br.json +9 -1
- package/lib/typescript/i18n/ru.json +9 -1
- package/lib/typescript/i18n/tr.json +9 -1
- package/lib/typescript/utils/i18n/Streami18n.d.ts +8 -0
- package/lib/typescript/utils/i18n/Streami18n.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/AttachmentPicker/components/AttachmentMediaPicker/AttachmentPickerItem.tsx +2 -1
- package/src/components/ChannelDetails/__tests__/members/ChannelMemberList.test.tsx +265 -151
- package/src/components/ChannelDetails/components/members/ChannelAddMembers.tsx +10 -4
- package/src/components/ChannelDetails/components/members/ChannelAllMembersModal.tsx +4 -25
- package/src/components/ChannelDetails/components/members/ChannelMemberList.tsx +137 -23
- package/src/components/ChannelDetails/components/navigation-section/PinnedMessageItem.tsx +1 -1
- package/src/components/ChannelDetails/components/navigation-section/PinnedMessageList.tsx +1 -1
- package/src/components/ChannelDetails/hooks/members/index.ts +0 -1
- package/src/components/Message/hooks/useMessageActions.tsx +30 -6
- package/src/components/UIComponents/SearchInput.tsx +1 -3
- package/src/contexts/channelMemberListContext/ChannelMemberListContext.tsx +72 -0
- package/src/contexts/index.ts +1 -0
- package/src/contexts/themeContext/utils/theme.ts +16 -0
- package/src/hooks/actions/__tests__/useChannelActionItems.test.tsx +4 -4
- package/src/hooks/actions/__tests__/useChannelMemberActionItems.test.tsx +3 -3
- package/src/hooks/actions/useChannelActionItems.tsx +56 -2
- package/src/hooks/actions/useChannelMemberActionItems.tsx +23 -1
- package/src/i18n/ar.json +9 -1
- package/src/i18n/en.json +8 -0
- package/src/i18n/es.json +9 -1
- package/src/i18n/fr.json +9 -1
- package/src/i18n/he.json +9 -1
- package/src/i18n/hi.json +9 -1
- package/src/i18n/it.json +9 -1
- package/src/i18n/ja.json +9 -1
- package/src/i18n/ko.json +9 -1
- package/src/i18n/nl.json +9 -1
- package/src/i18n/pt-br.json +9 -1
- package/src/i18n/ru.json +9 -1
- package/src/i18n/tr.json +9 -1
- package/src/version.json +1 -1
- package/lib/commonjs/components/ChannelDetails/hooks/members/useChannelAllMembers.js +0 -139
- package/lib/commonjs/components/ChannelDetails/hooks/members/useChannelAllMembers.js.map +0 -1
- package/lib/module/components/ChannelDetails/hooks/members/useChannelAllMembers.js +0 -139
- package/lib/module/components/ChannelDetails/hooks/members/useChannelAllMembers.js.map +0 -1
- package/lib/typescript/components/ChannelDetails/hooks/members/useChannelAllMembers.d.ts +0 -14
- package/lib/typescript/components/ChannelDetails/hooks/members/useChannelAllMembers.d.ts.map +0 -1
- package/src/components/ChannelDetails/__tests__/members/useChannelAllMembers.test.tsx +0 -234
- package/src/components/ChannelDetails/hooks/members/useChannelAllMembers.ts +0 -131
|
@@ -1,16 +1,37 @@
|
|
|
1
|
-
import React, { useCallback,
|
|
2
|
-
import { ActivityIndicator, FlatList, type FlatListProps } from 'react-native';
|
|
1
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { ActivityIndicator, FlatList, type FlatListProps, StyleSheet, View } from 'react-native';
|
|
3
3
|
|
|
4
|
-
import type { ChannelMemberResponse } from 'stream-chat';
|
|
4
|
+
import type { ChannelMemberResponse, SearchSourceState } from 'stream-chat';
|
|
5
5
|
|
|
6
6
|
import { MemberListLoadingSkeleton } from './MemberListLoadingSkeleton';
|
|
7
7
|
|
|
8
8
|
import { useChannelDetailsContext } from '../../../../contexts/channelDetailsContext/channelDetailsContext';
|
|
9
|
+
import {
|
|
10
|
+
ChannelMemberListProvider,
|
|
11
|
+
useChannelMemberListContext,
|
|
12
|
+
} from '../../../../contexts/channelMemberListContext/ChannelMemberListContext';
|
|
9
13
|
import { useComponentsContext } from '../../../../contexts/componentsContext/ComponentsContext';
|
|
10
|
-
import {
|
|
14
|
+
import { useTheme } from '../../../../contexts/themeContext/ThemeContext';
|
|
15
|
+
import { useTranslationContext } from '../../../../contexts/translationContext/TranslationContext';
|
|
16
|
+
import { getNotificationErrorOptions } from '../../../../hooks/actions/useChannelActions';
|
|
17
|
+
import { useStateStore } from '../../../../hooks/useStateStore';
|
|
18
|
+
import { primitives } from '../../../../theme';
|
|
19
|
+
import { useNotificationApi } from '../../../Notifications/hooks/useNotificationApi';
|
|
20
|
+
import { NotificationList } from '../../../Notifications/NotificationList';
|
|
21
|
+
import { NotificationTargetProvider } from '../../../Notifications/NotificationTargetContext';
|
|
22
|
+
import { EmptySearchResult } from '../../../UIComponents/EmptySearchResult';
|
|
23
|
+
import { SearchInput, SearchInputProps } from '../../../UIComponents/SearchInput';
|
|
11
24
|
|
|
12
25
|
const keyExtractor = (member: ChannelMemberResponse) => member.user?.id ?? member.user_id ?? '';
|
|
13
26
|
|
|
27
|
+
const listStateSelector = (state: SearchSourceState<ChannelMemberResponse>) => ({
|
|
28
|
+
error: state.lastQueryError,
|
|
29
|
+
hasNext: state.hasNext,
|
|
30
|
+
loading: state.isLoading,
|
|
31
|
+
members: state.items,
|
|
32
|
+
searchQuery: state.searchQuery,
|
|
33
|
+
});
|
|
34
|
+
|
|
14
35
|
export type ChannelMemberListProps = {
|
|
15
36
|
/**
|
|
16
37
|
* Besides the existing default behavior of the members list, you can attach
|
|
@@ -19,18 +40,54 @@ export type ChannelMemberListProps = {
|
|
|
19
40
|
* See https://reactnative.dev/docs/flatlist#props for the full list.
|
|
20
41
|
*/
|
|
21
42
|
additionalFlatListProps?: Partial<FlatListProps<ChannelMemberResponse>>;
|
|
43
|
+
searchInputProps?: SearchInputProps;
|
|
22
44
|
};
|
|
23
45
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const {
|
|
46
|
+
const ChannelMemberListContent = ({
|
|
47
|
+
additionalFlatListProps,
|
|
48
|
+
searchInputProps,
|
|
49
|
+
}: ChannelMemberListProps) => {
|
|
50
|
+
const { t } = useTranslationContext();
|
|
51
|
+
const {
|
|
52
|
+
theme: {
|
|
53
|
+
channelDetails: { memberList },
|
|
54
|
+
},
|
|
55
|
+
} = useTheme();
|
|
56
|
+
const { onMemberPress } = useChannelDetailsContext();
|
|
30
57
|
const { ChannelMemberActionsSheet, ChannelMemberItem } = useComponentsContext();
|
|
31
|
-
const {
|
|
58
|
+
const { addNotification } = useNotificationApi();
|
|
59
|
+
|
|
60
|
+
const { channel, searchSource } = useChannelMemberListContext();
|
|
61
|
+
const { error, hasNext, loading, members, searchQuery } = useStateStore(
|
|
62
|
+
searchSource.state,
|
|
63
|
+
listStateSelector,
|
|
64
|
+
);
|
|
65
|
+
|
|
32
66
|
const [selectedMember, setSelectedMember] = useState<ChannelMemberResponse | null>(null);
|
|
33
67
|
|
|
68
|
+
const initialized = useRef(false);
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (!initialized.current) {
|
|
71
|
+
initialized.current = true;
|
|
72
|
+
searchSource.search();
|
|
73
|
+
}
|
|
74
|
+
}, [searchSource]);
|
|
75
|
+
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
if (!error) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
addNotification({
|
|
81
|
+
message: t('Failed to load members'),
|
|
82
|
+
options: {
|
|
83
|
+
...getNotificationErrorOptions(error),
|
|
84
|
+
severity: 'error',
|
|
85
|
+
type: 'api:channel:query-members:failed',
|
|
86
|
+
},
|
|
87
|
+
origin: { context: { channel }, emitter: 'ChannelMemberList' },
|
|
88
|
+
});
|
|
89
|
+
}, [error, addNotification, channel, t]);
|
|
90
|
+
|
|
34
91
|
const handleMemberActionsClose = useCallback(() => setSelectedMember(null), []);
|
|
35
92
|
|
|
36
93
|
const handleMemberPress = useCallback(
|
|
@@ -51,24 +108,46 @@ export const ChannelMemberList = ({ additionalFlatListProps }: ChannelMemberList
|
|
|
51
108
|
[ChannelMemberItem, handleMemberPress],
|
|
52
109
|
);
|
|
53
110
|
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
111
|
+
const loadMore = useCallback(() => {
|
|
112
|
+
// hasNext is true by default, !!members prevents calling search on initial load
|
|
113
|
+
if (hasNext && !!members) {
|
|
114
|
+
searchSource.search();
|
|
115
|
+
}
|
|
116
|
+
}, [hasNext, members, searchSource]);
|
|
58
117
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
118
|
+
const emptyState =
|
|
119
|
+
loading && !members ? (
|
|
120
|
+
<MemberListLoadingSkeleton />
|
|
121
|
+
) : (
|
|
122
|
+
<EmptySearchResult label={t('No members found')} />
|
|
123
|
+
);
|
|
62
124
|
|
|
63
125
|
return (
|
|
64
|
-
|
|
126
|
+
<View style={[styles.container, memberList.container]}>
|
|
127
|
+
<SearchInput
|
|
128
|
+
accessibilityLabel={t('a11y/Search members')}
|
|
129
|
+
onChangeText={(text) => {
|
|
130
|
+
searchSource.state.partialNext({ searchQuery: text });
|
|
131
|
+
searchSource.search(text);
|
|
132
|
+
}}
|
|
133
|
+
value={searchQuery}
|
|
134
|
+
{...searchInputProps}
|
|
135
|
+
/>
|
|
65
136
|
<FlatList
|
|
66
|
-
|
|
137
|
+
contentContainerStyle={[styles.listContent, memberList.listContent]}
|
|
138
|
+
data={members}
|
|
139
|
+
keyboardDismissMode='interactive'
|
|
140
|
+
keyboardShouldPersistTaps='handled'
|
|
67
141
|
keyExtractor={keyExtractor}
|
|
68
|
-
|
|
69
|
-
|
|
142
|
+
ListEmptyComponent={emptyState}
|
|
143
|
+
ListFooterComponent={
|
|
144
|
+
loading && members && members.length > 0 ? <ActivityIndicator /> : null
|
|
145
|
+
}
|
|
146
|
+
onEndReached={loadMore}
|
|
70
147
|
onEndReachedThreshold={0.2}
|
|
71
148
|
renderItem={renderItem}
|
|
149
|
+
style={[styles.list, memberList.list]}
|
|
150
|
+
testID='channel-member-list'
|
|
72
151
|
{...additionalFlatListProps}
|
|
73
152
|
/>
|
|
74
153
|
{selectedMember ? (
|
|
@@ -78,6 +157,41 @@ export const ChannelMemberList = ({ additionalFlatListProps }: ChannelMemberList
|
|
|
78
157
|
visible
|
|
79
158
|
/>
|
|
80
159
|
) : null}
|
|
81
|
-
|
|
160
|
+
<NotificationList />
|
|
161
|
+
</View>
|
|
162
|
+
);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Lists all channel members with the ability to search them.
|
|
167
|
+
* @experimental This component is experimental and is subject to change.
|
|
168
|
+
*/
|
|
169
|
+
export const ChannelMemberList = (props: ChannelMemberListProps = {}) => {
|
|
170
|
+
const { channel } = useChannelDetailsContext();
|
|
171
|
+
const notificationHostId = channel?.cid ? `channel-member-list:${channel.cid}` : undefined;
|
|
172
|
+
|
|
173
|
+
if (!notificationHostId) {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return (
|
|
178
|
+
<ChannelMemberListProvider channel={channel}>
|
|
179
|
+
<NotificationTargetProvider hostId={notificationHostId} panel='channel-details'>
|
|
180
|
+
<ChannelMemberListContent {...props} />
|
|
181
|
+
</NotificationTargetProvider>
|
|
182
|
+
</ChannelMemberListProvider>
|
|
82
183
|
);
|
|
83
184
|
};
|
|
185
|
+
|
|
186
|
+
const styles = StyleSheet.create({
|
|
187
|
+
container: {
|
|
188
|
+
flex: 1,
|
|
189
|
+
},
|
|
190
|
+
list: {
|
|
191
|
+
flex: 1,
|
|
192
|
+
},
|
|
193
|
+
listContent: {
|
|
194
|
+
flexGrow: 1,
|
|
195
|
+
paddingBottom: primitives.spacing3xl,
|
|
196
|
+
},
|
|
197
|
+
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
|
+
import { Alert } from 'react-native';
|
|
2
3
|
|
|
3
4
|
import { LocalMessage } from 'stream-chat';
|
|
4
5
|
|
|
@@ -244,18 +245,41 @@ export const useMessageActions = ({
|
|
|
244
245
|
onOpenThread();
|
|
245
246
|
});
|
|
246
247
|
|
|
248
|
+
const isMuted = useUserMuteActive(message.user);
|
|
249
|
+
const isBlocked = new Set(client.blockedUsers.getLatestValue().userIds).has(
|
|
250
|
+
message.user?.id ?? '',
|
|
251
|
+
);
|
|
252
|
+
|
|
247
253
|
const onBlockUser = useStableCallback(() => {
|
|
248
254
|
if (handleBlockUser) {
|
|
249
255
|
handleBlockUser(message.user);
|
|
250
256
|
}
|
|
251
257
|
|
|
252
|
-
|
|
253
|
-
|
|
258
|
+
if (isBlocked) {
|
|
259
|
+
handleToggleBlockUser(message.user);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
254
262
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
263
|
+
const name = message.user?.name || message.user?.id || '';
|
|
264
|
+
|
|
265
|
+
Alert.alert(
|
|
266
|
+
t('Block {{ name }}', { name }),
|
|
267
|
+
t("They won't be able to message or call you. You can unblock them later."),
|
|
268
|
+
[
|
|
269
|
+
{
|
|
270
|
+
style: 'cancel',
|
|
271
|
+
text: t('Cancel'),
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
onPress: () => {
|
|
275
|
+
handleToggleBlockUser(message.user);
|
|
276
|
+
},
|
|
277
|
+
style: 'destructive',
|
|
278
|
+
text: t('Block'),
|
|
279
|
+
},
|
|
280
|
+
],
|
|
281
|
+
);
|
|
282
|
+
});
|
|
259
283
|
|
|
260
284
|
return useMemo(() => {
|
|
261
285
|
const handleReaction =
|
|
@@ -83,9 +83,7 @@ SearchInput.displayName = 'SearchInput{searchInput}';
|
|
|
83
83
|
|
|
84
84
|
const styles = StyleSheet.create({
|
|
85
85
|
container: {
|
|
86
|
-
|
|
87
|
-
paddingHorizontal: primitives.spacingMd,
|
|
88
|
-
paddingTop: primitives.spacingXs,
|
|
86
|
+
padding: primitives.spacingMd,
|
|
89
87
|
},
|
|
90
88
|
input: {
|
|
91
89
|
borderRadius: primitives.radiusMax,
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React, { PropsWithChildren, useContext, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { Channel, ChannelMemberSearchSource } from 'stream-chat';
|
|
4
|
+
|
|
5
|
+
import { DEFAULT_BASE_CONTEXT_VALUE } from '../utils/defaultBaseContextValue';
|
|
6
|
+
import { isTestEnvironment } from '../utils/isTestEnvironment';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @experimental This API is experimental and is subject to change.
|
|
10
|
+
*/
|
|
11
|
+
export type ChannelMemberListContextValue = {
|
|
12
|
+
channel: Channel;
|
|
13
|
+
searchSource: ChannelMemberSearchSource;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const ChannelMemberListContext = React.createContext(
|
|
17
|
+
DEFAULT_BASE_CONTEXT_VALUE as ChannelMemberListContextValue,
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @experimental This API is experimental and is subject to change.
|
|
22
|
+
*/
|
|
23
|
+
export const ChannelMemberListProvider = ({
|
|
24
|
+
channel,
|
|
25
|
+
children,
|
|
26
|
+
}: PropsWithChildren<{ channel: Channel }>) => {
|
|
27
|
+
const [searchSource] = useState(() => {
|
|
28
|
+
const source = new ChannelMemberSearchSource(
|
|
29
|
+
channel,
|
|
30
|
+
{
|
|
31
|
+
allowEmptySearchString: true,
|
|
32
|
+
pageSize: 25,
|
|
33
|
+
resetOnNewSearchQuery: false,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
initialFilterConfig: {
|
|
37
|
+
$or: {
|
|
38
|
+
enabled: true,
|
|
39
|
+
generate: ({ searchQuery }) =>
|
|
40
|
+
searchQuery ? { name: { $autocomplete: searchQuery } } : {},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
);
|
|
45
|
+
source.sort = { name: 1 };
|
|
46
|
+
source.activate();
|
|
47
|
+
return source;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<ChannelMemberListContext.Provider value={{ channel, searchSource }}>
|
|
52
|
+
{children}
|
|
53
|
+
</ChannelMemberListContext.Provider>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @experimental This API is experimental and is subject to change.
|
|
59
|
+
*/
|
|
60
|
+
export const useChannelMemberListContext = () => {
|
|
61
|
+
const contextValue = useContext(
|
|
62
|
+
ChannelMemberListContext,
|
|
63
|
+
) as unknown as ChannelMemberListContextValue;
|
|
64
|
+
|
|
65
|
+
if (contextValue === DEFAULT_BASE_CONTEXT_VALUE && !isTestEnvironment()) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
'The useChannelMemberListContext hook was called outside of the ChannelMemberListContext provider. Render the member list UI inside a ChannelMemberListProvider.',
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return contextValue;
|
|
72
|
+
};
|
package/src/contexts/index.ts
CHANGED
|
@@ -34,6 +34,7 @@ export * from './channelAddMembersContext/ChannelAddMembersContext';
|
|
|
34
34
|
export * from './channelEditDetailsContext';
|
|
35
35
|
export * from './channelFileAttachmentListContext/ChannelFileAttachmentListContext';
|
|
36
36
|
export * from './channelPinnedMessageListContext/ChannelPinnedMessageListContext';
|
|
37
|
+
export * from './channelMemberListContext/ChannelMemberListContext';
|
|
37
38
|
export * from './channelMediaListContext/ChannelMediaListContext';
|
|
38
39
|
export * from './pollContext';
|
|
39
40
|
export * from './liveLocationManagerContext';
|
|
@@ -219,6 +219,11 @@ export type Theme = {
|
|
|
219
219
|
role: TextStyle;
|
|
220
220
|
status: TextStyle;
|
|
221
221
|
};
|
|
222
|
+
memberList: {
|
|
223
|
+
container: ViewStyle;
|
|
224
|
+
list: ViewStyle;
|
|
225
|
+
listContent: ViewStyle;
|
|
226
|
+
};
|
|
222
227
|
memberActionsSheet: {
|
|
223
228
|
actionsList: ViewStyle;
|
|
224
229
|
container: ViewStyle;
|
|
@@ -236,6 +241,9 @@ export type Theme = {
|
|
|
236
241
|
headerTitle: TextStyle;
|
|
237
242
|
};
|
|
238
243
|
addMembers: {
|
|
244
|
+
container: ViewStyle;
|
|
245
|
+
list: ViewStyle;
|
|
246
|
+
listContent: ViewStyle;
|
|
239
247
|
searchResultItem: {
|
|
240
248
|
alreadyMemberInfo: ViewStyle;
|
|
241
249
|
memberLabel: TextStyle;
|
|
@@ -1326,6 +1334,11 @@ export const defaultTheme: Theme = {
|
|
|
1326
1334
|
role: {},
|
|
1327
1335
|
status: {},
|
|
1328
1336
|
},
|
|
1337
|
+
memberList: {
|
|
1338
|
+
container: {},
|
|
1339
|
+
list: {},
|
|
1340
|
+
listContent: {},
|
|
1341
|
+
},
|
|
1329
1342
|
memberActionsSheet: {
|
|
1330
1343
|
actionsList: {},
|
|
1331
1344
|
container: {},
|
|
@@ -1343,6 +1356,9 @@ export const defaultTheme: Theme = {
|
|
|
1343
1356
|
headerTitle: {},
|
|
1344
1357
|
},
|
|
1345
1358
|
addMembers: {
|
|
1359
|
+
container: {},
|
|
1360
|
+
list: {},
|
|
1361
|
+
listContent: {},
|
|
1346
1362
|
searchResultItem: {
|
|
1347
1363
|
alreadyMemberInfo: {},
|
|
1348
1364
|
memberLabel: {},
|
|
@@ -105,7 +105,7 @@ describe('useChannelActionItems', () => {
|
|
|
105
105
|
expect(result.current.map((item) => item.action)).toEqual([
|
|
106
106
|
channelActions.muteChannel,
|
|
107
107
|
channelActions.pin,
|
|
108
|
-
|
|
108
|
+
expect.any(Function),
|
|
109
109
|
expect.any(Function),
|
|
110
110
|
]);
|
|
111
111
|
expect(result.current.map((item) => item.id)).toEqual([
|
|
@@ -268,7 +268,7 @@ describe('getChannelActionItems', () => {
|
|
|
268
268
|
expect(actionItems.map((item) => item.action)).toEqual([
|
|
269
269
|
channelActions.muteChannel,
|
|
270
270
|
channelActions.pin,
|
|
271
|
-
|
|
271
|
+
expect.any(Function),
|
|
272
272
|
expect.any(Function),
|
|
273
273
|
]);
|
|
274
274
|
expect(actionItems.map((item) => item.id)).toEqual(['mute', 'pin', 'leave', 'deleteChannel']);
|
|
@@ -308,7 +308,7 @@ describe('getChannelActionItems', () => {
|
|
|
308
308
|
channelActions.pin,
|
|
309
309
|
channelActions.unmuteUser,
|
|
310
310
|
channelActions.unblockUser,
|
|
311
|
-
|
|
311
|
+
expect.any(Function),
|
|
312
312
|
expect.any(Function),
|
|
313
313
|
]);
|
|
314
314
|
expect(actionItems.map((item) => item.label)).toEqual([
|
|
@@ -383,7 +383,7 @@ describe('getChannelActionItems', () => {
|
|
|
383
383
|
expect(actionItems[0].placement).toBe('swipe');
|
|
384
384
|
|
|
385
385
|
const leaveItem = actionItems.find((item) => item.id === 'leave');
|
|
386
|
-
expect(leaveItem?.action).
|
|
386
|
+
expect(leaveItem?.action).toEqual(expect.any(Function));
|
|
387
387
|
expect(leaveItem?.label).toBe('Leave Group');
|
|
388
388
|
expect(leaveItem?.placement).toBe('sheet');
|
|
389
389
|
});
|
|
@@ -76,7 +76,7 @@ describe('useChannelMemberActionItems', () => {
|
|
|
76
76
|
expect(result.current.map((item) => item.id)).toEqual(['muteUser', 'block']);
|
|
77
77
|
expect(result.current.map((item) => item.action)).toEqual([
|
|
78
78
|
userActions.muteUser,
|
|
79
|
-
|
|
79
|
+
expect.any(Function),
|
|
80
80
|
]);
|
|
81
81
|
expect(result.current.map((item) => item.type)).toEqual(['standard', 'destructive']);
|
|
82
82
|
expect(result.current.map((item) => item.label)).toEqual(['Mute User', 'Block User']);
|
|
@@ -259,7 +259,7 @@ describe('buildDefaultChannelMemberActionItems', () => {
|
|
|
259
259
|
});
|
|
260
260
|
|
|
261
261
|
expect(items.map((item) => item.id)).toEqual(['muteUser', 'block']);
|
|
262
|
-
expect(items.map((item) => item.action)).toEqual([actions.muteUser,
|
|
262
|
+
expect(items.map((item) => item.action)).toEqual([actions.muteUser, expect.any(Function)]);
|
|
263
263
|
expect(items.map((item) => item.label)).toEqual(['Mute User', 'Block User']);
|
|
264
264
|
expect(items.map((item) => item.type)).toEqual(['standard', 'destructive']);
|
|
265
265
|
});
|
|
@@ -335,7 +335,7 @@ describe('buildDefaultChannelMemberActionItems', () => {
|
|
|
335
335
|
});
|
|
336
336
|
|
|
337
337
|
expect(items.find((item) => item.id === 'muteUser')?.action).toBe(actions.unmuteUser);
|
|
338
|
-
expect(items.find((item) => item.id === 'block')?.action).
|
|
338
|
+
expect(items.find((item) => item.id === 'block')?.action).toEqual(expect.any(Function));
|
|
339
339
|
});
|
|
340
340
|
|
|
341
341
|
it('default getChannelMemberActionItems returns defaultItems unchanged', () => {
|
|
@@ -179,8 +179,31 @@ export const buildDefaultChannelActionItems: BuildDefaultChannelActionItems = (
|
|
|
179
179
|
type: 'standard',
|
|
180
180
|
});
|
|
181
181
|
|
|
182
|
+
const blockUserWithConfirmation = (...args: Parameters<ChannelActionHandler>) => {
|
|
183
|
+
const otherUser = getOtherUserInDirectChannel(channel)?.user;
|
|
184
|
+
const name = otherUser?.name || otherUser?.id || '';
|
|
185
|
+
|
|
186
|
+
Alert.alert(
|
|
187
|
+
t('Block {{ name }}', { name }),
|
|
188
|
+
t("They won't be able to message or call you. You can unblock them later."),
|
|
189
|
+
[
|
|
190
|
+
{
|
|
191
|
+
style: 'cancel',
|
|
192
|
+
text: t('Cancel'),
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
onPress: async () => {
|
|
196
|
+
await blockUser(...args);
|
|
197
|
+
},
|
|
198
|
+
style: 'destructive',
|
|
199
|
+
text: t('Block'),
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
);
|
|
203
|
+
};
|
|
204
|
+
|
|
182
205
|
actionItems.push({
|
|
183
|
-
action: isBlocked ? unblockUser :
|
|
206
|
+
action: isBlocked ? unblockUser : blockUserWithConfirmation,
|
|
184
207
|
Icon: (props) => <ChannelActionsIcon Icon={BlockUser} {...props} />,
|
|
185
208
|
id: 'block',
|
|
186
209
|
label: isBlocked ? t('Unblock User') : t('Block User'),
|
|
@@ -189,8 +212,39 @@ export const buildDefaultChannelActionItems: BuildDefaultChannelActionItems = (
|
|
|
189
212
|
});
|
|
190
213
|
}
|
|
191
214
|
|
|
215
|
+
const leaveWithConfirmation = (...args: Parameters<ChannelActionHandler>) => {
|
|
216
|
+
const channelName = channel.data?.name;
|
|
217
|
+
let name: string;
|
|
218
|
+
if (channelName) {
|
|
219
|
+
name = channelName;
|
|
220
|
+
} else if (isDirectChat) {
|
|
221
|
+
const otherUser = getOtherUserInDirectChannel(channel)?.user;
|
|
222
|
+
name = otherUser?.name || otherUser?.id || '';
|
|
223
|
+
} else {
|
|
224
|
+
name = t('group');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
Alert.alert(
|
|
228
|
+
isDirectChat ? t('Leave Chat') : t('Leave Group'),
|
|
229
|
+
t("You'll stop receiving messages from {{ name }}. You can rejoin anytime.", { name }),
|
|
230
|
+
[
|
|
231
|
+
{
|
|
232
|
+
style: 'cancel',
|
|
233
|
+
text: t('Cancel'),
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
onPress: async () => {
|
|
237
|
+
await leave(...args);
|
|
238
|
+
},
|
|
239
|
+
style: 'destructive',
|
|
240
|
+
text: t('Leave'),
|
|
241
|
+
},
|
|
242
|
+
],
|
|
243
|
+
);
|
|
244
|
+
};
|
|
245
|
+
|
|
192
246
|
actionItems.push({
|
|
193
|
-
action:
|
|
247
|
+
action: leaveWithConfirmation,
|
|
194
248
|
Icon: (props) => <ChannelActionsIcon Icon={ArrowBoxLeft} {...props} />,
|
|
195
249
|
id: 'leave',
|
|
196
250
|
label: isDirectChat ? t('Leave Chat') : t('Leave Group'),
|
|
@@ -83,7 +83,29 @@ export const buildDefaultChannelMemberActionItems: BuildDefaultChannelMemberActi
|
|
|
83
83
|
type: 'standard',
|
|
84
84
|
},
|
|
85
85
|
{
|
|
86
|
-
action: isBlocked
|
|
86
|
+
action: isBlocked
|
|
87
|
+
? unblockUser
|
|
88
|
+
: (...args: Parameters<UserActions['blockUser']>) => {
|
|
89
|
+
const name = member.user?.name || member.user?.id || '';
|
|
90
|
+
|
|
91
|
+
Alert.alert(
|
|
92
|
+
t('Block {{ name }}', { name }),
|
|
93
|
+
t("They won't be able to message or call you. You can unblock them later."),
|
|
94
|
+
[
|
|
95
|
+
{
|
|
96
|
+
style: 'cancel',
|
|
97
|
+
text: t('Cancel'),
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
onPress: async () => {
|
|
101
|
+
await blockUser(...args);
|
|
102
|
+
},
|
|
103
|
+
style: 'destructive',
|
|
104
|
+
text: t('Block'),
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
);
|
|
108
|
+
},
|
|
87
109
|
Icon: (props) => <ChannelMemberActionsIcon Icon={BlockUser} {...props} />,
|
|
88
110
|
id: 'block',
|
|
89
111
|
label: isBlocked ? t('Unblock User') : t('Block User'),
|
package/src/i18n/ar.json
CHANGED
|
@@ -457,5 +457,13 @@
|
|
|
457
457
|
"Pin Chat": "تثبيت الدردشة",
|
|
458
458
|
"Pin Group": "تثبيت المجموعة",
|
|
459
459
|
"Unpin Chat": "إلغاء تثبيت الدردشة",
|
|
460
|
-
"Unpin Group": "إلغاء تثبيت المجموعة"
|
|
460
|
+
"Unpin Group": "إلغاء تثبيت المجموعة",
|
|
461
|
+
"Block": "حظر",
|
|
462
|
+
"Block {{ name }}": "حظر {{ name }}",
|
|
463
|
+
"They won't be able to message or call you. You can unblock them later.": "لن يتمكن/تتمكن من مراسلتك أو الاتصال بك. يمكنك إلغاء الحظر لاحقًا.",
|
|
464
|
+
"Leave": "مغادرة",
|
|
465
|
+
"You'll stop receiving messages from {{ name }}. You can rejoin anytime.": "ستتوقف عن تلقي الرسائل من {{ name }}. يمكنك الانضمام مجددًا في أي وقت.",
|
|
466
|
+
"group": "المجموعة",
|
|
467
|
+
"No members found": "لم يتم العثور على أعضاء",
|
|
468
|
+
"a11y/Search members": "البحث عن الأعضاء"
|
|
461
469
|
}
|
package/src/i18n/en.json
CHANGED
|
@@ -99,6 +99,7 @@
|
|
|
99
99
|
"No chats here yet…": "No chats here yet…",
|
|
100
100
|
"No files": "No files",
|
|
101
101
|
"No items exist": "No items exist",
|
|
102
|
+
"No members found": "No members found",
|
|
102
103
|
"No photos or videos": "No photos or videos",
|
|
103
104
|
"No pinned messages": "No pinned messages",
|
|
104
105
|
"No threads here yet": "No threads here yet",
|
|
@@ -242,6 +243,12 @@
|
|
|
242
243
|
"No conversations yet": "No conversations yet",
|
|
243
244
|
"Are you sure you want to delete this group? This can't be undone.": "Are you sure you want to delete this group? This can't be undone.",
|
|
244
245
|
"Are you sure you want to delete this chat? This can't be undone.": "Are you sure you want to delete this chat? This can't be undone.",
|
|
246
|
+
"Block": "Block",
|
|
247
|
+
"Block {{ name }}": "Block {{ name }}",
|
|
248
|
+
"They won't be able to message or call you. You can unblock them later.": "They won't be able to message or call you. You can unblock them later.",
|
|
249
|
+
"Leave": "Leave",
|
|
250
|
+
"You'll stop receiving messages from {{ name }}. You can rejoin anytime.": "You'll stop receiving messages from {{ name }}. You can rejoin anytime.",
|
|
251
|
+
"group": "this group",
|
|
245
252
|
"Delete chat": "Delete chat",
|
|
246
253
|
"Delete group": "Delete group",
|
|
247
254
|
"Archive Chat": "Archive Chat",
|
|
@@ -350,6 +357,7 @@
|
|
|
350
357
|
"a11y/Select image": "Select image",
|
|
351
358
|
"a11y/Select video": "Select video",
|
|
352
359
|
"a11y/Send voice recording": "Send voice recording",
|
|
360
|
+
"a11y/Search members": "Search members",
|
|
353
361
|
"a11y/Search pinned messages": "Search pinned messages",
|
|
354
362
|
"a11y/Search users to add": "Search users to add",
|
|
355
363
|
"a11y/Select {{name}}": "Select {{name}}",
|
package/src/i18n/es.json
CHANGED
|
@@ -457,5 +457,13 @@
|
|
|
457
457
|
"Pin Chat": "Fijar chat",
|
|
458
458
|
"Pin Group": "Fijar grupo",
|
|
459
459
|
"Unpin Chat": "Desfijar chat",
|
|
460
|
-
"Unpin Group": "Desfijar grupo"
|
|
460
|
+
"Unpin Group": "Desfijar grupo",
|
|
461
|
+
"Block": "Bloquear",
|
|
462
|
+
"Block {{ name }}": "Bloquear a {{ name }}",
|
|
463
|
+
"They won't be able to message or call you. You can unblock them later.": "No podrá enviarte mensajes ni llamarte. Puedes desbloquearlo más tarde.",
|
|
464
|
+
"Leave": "Salir",
|
|
465
|
+
"You'll stop receiving messages from {{ name }}. You can rejoin anytime.": "Dejarás de recibir mensajes de {{ name }}. Puedes volver a unirte cuando quieras.",
|
|
466
|
+
"group": "el grupo",
|
|
467
|
+
"No members found": "No se encontraron miembros",
|
|
468
|
+
"a11y/Search members": "Buscar miembros"
|
|
461
469
|
}
|
package/src/i18n/fr.json
CHANGED
|
@@ -457,5 +457,13 @@
|
|
|
457
457
|
"Pin Chat": "Épingler la discussion",
|
|
458
458
|
"Pin Group": "Épingler le groupe",
|
|
459
459
|
"Unpin Chat": "Détacher la discussion",
|
|
460
|
-
"Unpin Group": "Détacher le groupe"
|
|
460
|
+
"Unpin Group": "Détacher le groupe",
|
|
461
|
+
"Block": "Bloquer",
|
|
462
|
+
"Block {{ name }}": "Bloquer {{ name }}",
|
|
463
|
+
"They won't be able to message or call you. You can unblock them later.": "Cette personne ne pourra plus vous envoyer de messages ni vous appeler. Vous pourrez la débloquer plus tard.",
|
|
464
|
+
"Leave": "Quitter",
|
|
465
|
+
"You'll stop receiving messages from {{ name }}. You can rejoin anytime.": "Vous ne recevrez plus de messages de {{ name }}. Vous pouvez rejoindre à tout moment.",
|
|
466
|
+
"group": "ce groupe",
|
|
467
|
+
"No members found": "Aucun membre trouvé",
|
|
468
|
+
"a11y/Search members": "Rechercher des membres"
|
|
461
469
|
}
|