forked from Ivasoft/mattermost-mobile
Compare commits
4 Commits
test1.0.4
...
MM-47655-a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b261d0dc16 | ||
|
|
f54bb59aef | ||
|
|
2d10f99a94 | ||
|
|
a6b51436ac |
@@ -530,6 +530,46 @@ export const fetchProfilesInTeam = async (serverUrl: string, teamId: string, pag
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const fetchProfilesNotInChannel = async (
|
||||||
|
serverUrl: string,
|
||||||
|
teamId: string,
|
||||||
|
channelId: string,
|
||||||
|
groupConstrained: boolean,
|
||||||
|
page = 0,
|
||||||
|
perPage: number = General.PROFILE_CHUNK_SIZE,
|
||||||
|
fetchOnly = false,
|
||||||
|
) => {
|
||||||
|
let client: Client;
|
||||||
|
try {
|
||||||
|
client = NetworkManager.getClient(serverUrl);
|
||||||
|
} catch (error) {
|
||||||
|
return {error};
|
||||||
|
}
|
||||||
|
|
||||||
|
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||||
|
if (!operator) {
|
||||||
|
return {error: `${serverUrl} database not found`};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const users = await client.getProfilesNotInChannel(teamId, channelId, groupConstrained, page, perPage);
|
||||||
|
|
||||||
|
if (!fetchOnly) {
|
||||||
|
const currentUserId = await getCurrentUserId(operator.database);
|
||||||
|
const toStore = removeUserFromList(currentUserId, users);
|
||||||
|
await operator.handleUsers({
|
||||||
|
users: toStore,
|
||||||
|
prepareRecordsOnly: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {users};
|
||||||
|
} catch (error) {
|
||||||
|
forceLogoutIfNecessary(serverUrl, error as ClientError);
|
||||||
|
return {error};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const searchProfiles = async (serverUrl: string, term: string, options: any = {}, fetchOnly = false) => {
|
export const searchProfiles = async (serverUrl: string, term: string, options: any = {}, fetchOnly = false) => {
|
||||||
let client: Client;
|
let client: Client;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import React, {useCallback} from 'react';
|
||||||
|
import {useIntl} from 'react-intl';
|
||||||
|
import {StyleProp, ViewStyle} from 'react-native';
|
||||||
|
|
||||||
|
import OptionBox from '@components/option_box';
|
||||||
|
import {Screens} from '@constants';
|
||||||
|
import {useTheme} from '@context/theme';
|
||||||
|
import {ChannelModel} from '@database/models/server';
|
||||||
|
import {dismissBottomSheet, goToScreen, showModal} from '@screens/navigation';
|
||||||
|
import {changeOpacity} from '@utils/theme';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
channel: ChannelModel;
|
||||||
|
containerStyle?: StyleProp<ViewStyle>;
|
||||||
|
inModal?: boolean;
|
||||||
|
testID?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AddPeopleBox = ({channel, containerStyle, inModal, testID}: Props) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const theme = useTheme();
|
||||||
|
const channelId = channel.id;
|
||||||
|
const displayName = channel.displayName;
|
||||||
|
|
||||||
|
const onAddPeople = useCallback(async () => {
|
||||||
|
const title = intl.formatMessage({id: 'mobile.channel_add_people.title', defaultMessage: 'Add Members'});
|
||||||
|
const options = {
|
||||||
|
topBar: {
|
||||||
|
subtitle: {
|
||||||
|
color: changeOpacity(theme.sidebarHeaderTextColor, 0.72),
|
||||||
|
text: displayName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (inModal) {
|
||||||
|
goToScreen(Screens.CHANNEL_ADD_PEOPLE, title, {channelId}, options);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await dismissBottomSheet();
|
||||||
|
showModal(Screens.CHANNEL_ADD_PEOPLE, title, {channelId});
|
||||||
|
}, [intl, channelId, inModal]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<OptionBox
|
||||||
|
containerStyle={containerStyle}
|
||||||
|
iconName='account-plus-outline'
|
||||||
|
onPress={onAddPeople}
|
||||||
|
testID={testID}
|
||||||
|
text={intl.formatMessage({id: 'intro.add_people', defaultMessage: 'Add People'})}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddPeopleBox;
|
||||||
@@ -1,43 +1,23 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React, {useCallback} from 'react';
|
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||||
import {useIntl} from 'react-intl';
|
import withObservables from '@nozbe/with-observables';
|
||||||
import {StyleProp, ViewStyle} from 'react-native';
|
|
||||||
|
|
||||||
import OptionBox from '@components/option_box';
|
import {observeChannel} from '@queries/servers/channel';
|
||||||
import {Screens} from '@constants';
|
|
||||||
import {dismissBottomSheet, goToScreen, showModal} from '@screens/navigation';
|
|
||||||
|
|
||||||
type Props = {
|
import AddPeopleBox from './add_people_box';
|
||||||
|
|
||||||
|
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||||
|
|
||||||
|
type Props = WithDatabaseArgs & {
|
||||||
channelId: string;
|
channelId: string;
|
||||||
containerStyle?: StyleProp<ViewStyle>;
|
|
||||||
inModal?: boolean;
|
|
||||||
testID?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const AddPeopleBox = ({channelId, containerStyle, inModal, testID}: Props) => {
|
const enhanced = withObservables(['channelId'], ({channelId, database}: Props) => {
|
||||||
const intl = useIntl();
|
return {
|
||||||
|
channel: observeChannel(database, channelId),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const onAddPeople = useCallback(async () => {
|
export default withDatabase(enhanced(AddPeopleBox));
|
||||||
const title = intl.formatMessage({id: 'intro.add_people', defaultMessage: 'Add People'});
|
|
||||||
if (inModal) {
|
|
||||||
goToScreen(Screens.CHANNEL_ADD_PEOPLE, title, {channelId});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await dismissBottomSheet();
|
|
||||||
showModal(Screens.CHANNEL_ADD_PEOPLE, title, {channelId});
|
|
||||||
}, [intl, channelId, inModal]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<OptionBox
|
|
||||||
containerStyle={containerStyle}
|
|
||||||
iconName='account-plus-outline'
|
|
||||||
onPress={onAddPeople}
|
|
||||||
testID={testID}
|
|
||||||
text={intl.formatMessage({id: 'intro.add_people', defaultMessage: 'Add People'})}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AddPeopleBox;
|
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ export default {
|
|||||||
export const MODAL_SCREENS_WITHOUT_BACK = new Set<string>([
|
export const MODAL_SCREENS_WITHOUT_BACK = new Set<string>([
|
||||||
BROWSE_CHANNELS,
|
BROWSE_CHANNELS,
|
||||||
CHANNEL_INFO,
|
CHANNEL_INFO,
|
||||||
|
CHANNEL_ADD_PEOPLE,
|
||||||
CREATE_DIRECT_MESSAGE,
|
CREATE_DIRECT_MESSAGE,
|
||||||
CREATE_TEAM,
|
CREATE_TEAM,
|
||||||
CUSTOM_STATUS,
|
CUSTOM_STATUS,
|
||||||
@@ -156,7 +157,6 @@ export const OVERLAY_SCREENS = new Set<string>([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
export const NOT_READY = [
|
export const NOT_READY = [
|
||||||
CHANNEL_ADD_PEOPLE,
|
|
||||||
CHANNEL_MENTION,
|
CHANNEL_MENTION,
|
||||||
CREATE_TEAM,
|
CREATE_TEAM,
|
||||||
INTEGRATION_SELECTOR,
|
INTEGRATION_SELECTOR,
|
||||||
|
|||||||
340
app/screens/channel_add_people/channel_add_people.tsx
Normal file
340
app/screens/channel_add_people/channel_add_people.tsx
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import React, {useCallback, useEffect, useMemo, useReducer, useRef, useState} from 'react';
|
||||||
|
import {defineMessages, useIntl} from 'react-intl';
|
||||||
|
import {Keyboard, Platform, StyleSheet, View} from 'react-native';
|
||||||
|
import {SafeAreaView} from 'react-native-safe-area-context';
|
||||||
|
|
||||||
|
import {addMembersToChannel} from '@actions/remote/channel';
|
||||||
|
import {fetchProfilesNotInChannel, searchProfiles} from '@actions/remote/user';
|
||||||
|
import Loading from '@components/loading';
|
||||||
|
import Search from '@components/search';
|
||||||
|
import SelectedUsers from '@components/selected_users_panel';
|
||||||
|
import UserList from '@components/user_list';
|
||||||
|
import {General} from '@constants';
|
||||||
|
import {useServerUrl} from '@context/server';
|
||||||
|
import {useTheme} from '@context/theme';
|
||||||
|
import {ChannelModel} from '@database/models/server';
|
||||||
|
import {debounce} from '@helpers/api/general';
|
||||||
|
import useNavButtonPressed from '@hooks/navigation_button_pressed';
|
||||||
|
import {t} from '@i18n';
|
||||||
|
import {popTopScreen, setButtons} from '@screens/navigation';
|
||||||
|
import {alertErrorWithFallback} from '@utils/draft';
|
||||||
|
import {changeOpacity, getKeyboardAppearanceFromTheme} from '@utils/theme';
|
||||||
|
import {filterProfilesMatchingTerm} from '@utils/user';
|
||||||
|
|
||||||
|
const ADD_BUTTON = 'add-button';
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
Keyboard.dismiss();
|
||||||
|
popTopScreen();
|
||||||
|
};
|
||||||
|
|
||||||
|
const style = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
searchBar: {
|
||||||
|
marginLeft: 12,
|
||||||
|
marginRight: Platform.select({ios: 4, default: 12}),
|
||||||
|
marginVertical: 12,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
error: {
|
||||||
|
id: t('mobile.channel_add_people.error'),
|
||||||
|
defaultMessage: 'We could not add those users to the channel. Please check your connection and try again.',
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
id: t('mobile.channel_add_people.title'),
|
||||||
|
defaultMessage: 'Add Members',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function reduceProfiles(state: UserProfile[], action: {type: 'add'; values?: UserProfile[]}) {
|
||||||
|
if (action.type === 'add' && action.values?.length) {
|
||||||
|
return [...state, ...action.values];
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
componentId: string;
|
||||||
|
currentChannel: ChannelModel;
|
||||||
|
currentTeamId: string;
|
||||||
|
currentUserId: string;
|
||||||
|
restrictDirectMessage: boolean;
|
||||||
|
teammateNameDisplay: string;
|
||||||
|
tutorialWatched: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ChannelAddPeople({
|
||||||
|
componentId,
|
||||||
|
currentChannel,
|
||||||
|
currentTeamId,
|
||||||
|
currentUserId,
|
||||||
|
restrictDirectMessage,
|
||||||
|
teammateNameDisplay,
|
||||||
|
tutorialWatched,
|
||||||
|
}: Props) {
|
||||||
|
const serverUrl = useServerUrl();
|
||||||
|
const intl = useIntl();
|
||||||
|
const theme = useTheme();
|
||||||
|
const {formatMessage} = intl;
|
||||||
|
|
||||||
|
const searchTimeoutId = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
const next = useRef(true);
|
||||||
|
const page = useRef(-1);
|
||||||
|
const mounted = useRef(false);
|
||||||
|
|
||||||
|
const [profiles, dispatchProfiles] = useReducer(reduceProfiles, []);
|
||||||
|
const [searchResults, setSearchResults] = useState<UserProfile[]>([]);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [term, setTerm] = useState('');
|
||||||
|
const [startingConversation, setStartingConversation] = useState(false);
|
||||||
|
const [selectedIds, setSelectedIds] = useState<{[id: string]: UserProfile}>({});
|
||||||
|
const selectedCount = Object.keys(selectedIds).length;
|
||||||
|
const groupConstrained = currentChannel.isGroupConstrained;
|
||||||
|
const currentChannelId = currentChannel.id;
|
||||||
|
|
||||||
|
const isSearch = Boolean(term);
|
||||||
|
|
||||||
|
const loadedProfiles = ({users}: {users?: UserProfile[]}) => {
|
||||||
|
if (mounted.current) {
|
||||||
|
if (users && !users.length) {
|
||||||
|
next.current = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
page.current += 1;
|
||||||
|
setLoading(false);
|
||||||
|
dispatchProfiles({type: 'add', values: users});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getProfiles = useCallback(debounce(() => {
|
||||||
|
if (next.current && !loading && !term && mounted.current) {
|
||||||
|
setLoading(true);
|
||||||
|
fetchProfilesNotInChannel(serverUrl,
|
||||||
|
currentTeamId,
|
||||||
|
currentChannelId,
|
||||||
|
groupConstrained,
|
||||||
|
page.current + 1,
|
||||||
|
General.PROFILE_CHUNK_SIZE).then(loadedProfiles);
|
||||||
|
}
|
||||||
|
}, 100), [loading, isSearch, serverUrl, currentTeamId]);
|
||||||
|
|
||||||
|
const handleRemoveProfile = useCallback((id: string) => {
|
||||||
|
const newSelectedIds = Object.assign({}, selectedIds);
|
||||||
|
|
||||||
|
Reflect.deleteProperty(newSelectedIds, id);
|
||||||
|
|
||||||
|
setSelectedIds(newSelectedIds);
|
||||||
|
}, [selectedIds]);
|
||||||
|
|
||||||
|
const addPeopleToChannel = useCallback(async (ids: string[]): Promise<boolean> => {
|
||||||
|
const result = await addMembersToChannel(serverUrl, currentChannelId, ids, '', false);
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
alertErrorWithFallback(intl, result.error, messages.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !result.error;
|
||||||
|
}, [serverUrl]);
|
||||||
|
|
||||||
|
const clearSearch = useCallback(() => {
|
||||||
|
setTerm('');
|
||||||
|
setSearchResults([]);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const startConversation = useCallback(async (selectedId?: {[id: string]: boolean}) => {
|
||||||
|
if (startingConversation) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setStartingConversation(true);
|
||||||
|
|
||||||
|
const idsToUse = selectedId ? Object.keys(selectedId) : Object.keys(selectedIds);
|
||||||
|
let success;
|
||||||
|
if (idsToUse.length === 0) {
|
||||||
|
success = false;
|
||||||
|
} else {
|
||||||
|
success = await addPeopleToChannel(idsToUse);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
close();
|
||||||
|
} else {
|
||||||
|
setStartingConversation(false);
|
||||||
|
}
|
||||||
|
}, [startingConversation, selectedIds, addPeopleToChannel]);
|
||||||
|
|
||||||
|
const handleSelectProfile = useCallback((user: UserProfile) => {
|
||||||
|
if (selectedIds[user.id]) {
|
||||||
|
handleRemoveProfile(user.id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.id === currentUserId) {
|
||||||
|
const selectedId = {
|
||||||
|
[currentUserId]: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
startConversation(selectedId);
|
||||||
|
} else {
|
||||||
|
const wasSelected = selectedIds[user.id];
|
||||||
|
if (!wasSelected && selectedCount >= General.MAX_USERS_IN_GM) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newSelectedIds = Object.assign({}, selectedIds);
|
||||||
|
if (!wasSelected) {
|
||||||
|
newSelectedIds[user.id] = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedIds(newSelectedIds);
|
||||||
|
clearSearch();
|
||||||
|
}
|
||||||
|
}, [selectedIds, currentUserId, handleRemoveProfile, startConversation, clearSearch]);
|
||||||
|
|
||||||
|
const searchUsers = useCallback(async (searchTerm: string) => {
|
||||||
|
const lowerCasedTerm = searchTerm.toLowerCase();
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
const results = await searchProfiles(serverUrl, lowerCasedTerm, {
|
||||||
|
team_id: currentTeamId,
|
||||||
|
allow_inactive: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
let data: UserProfile[] = [];
|
||||||
|
if (results.data) {
|
||||||
|
data = results.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSearchResults(data);
|
||||||
|
setLoading(false);
|
||||||
|
}, [restrictDirectMessage, serverUrl, currentTeamId]);
|
||||||
|
|
||||||
|
const search = useCallback(() => {
|
||||||
|
searchUsers(term);
|
||||||
|
}, [searchUsers, term]);
|
||||||
|
|
||||||
|
const onSearch = useCallback((text: string) => {
|
||||||
|
if (text) {
|
||||||
|
setTerm(text);
|
||||||
|
if (searchTimeoutId.current) {
|
||||||
|
clearTimeout(searchTimeoutId.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
searchTimeoutId.current = setTimeout(() => {
|
||||||
|
searchUsers(text);
|
||||||
|
}, General.SEARCH_TIMEOUT_MILLISECONDS);
|
||||||
|
} else {
|
||||||
|
clearSearch();
|
||||||
|
}
|
||||||
|
}, [searchUsers, clearSearch]);
|
||||||
|
|
||||||
|
const updateNavigationButtons = useCallback(async (startEnabled: boolean) => {
|
||||||
|
setButtons(componentId, {
|
||||||
|
rightButtons: [{
|
||||||
|
color: theme.sidebarHeaderTextColor,
|
||||||
|
id: ADD_BUTTON,
|
||||||
|
text: formatMessage({id: 'mobile.channel_add_people.button', defaultMessage: 'Add'}),
|
||||||
|
showAsAction: 'always',
|
||||||
|
enabled: startEnabled,
|
||||||
|
testID: 'add_members.start.button',
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
}, [intl.locale, theme]);
|
||||||
|
|
||||||
|
useNavButtonPressed(ADD_BUTTON, componentId, startConversation, [startConversation]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
mounted.current = true;
|
||||||
|
updateNavigationButtons(false);
|
||||||
|
getProfiles();
|
||||||
|
return () => {
|
||||||
|
mounted.current = false;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const canStart = selectedCount > 0 && !startingConversation;
|
||||||
|
updateNavigationButtons(canStart);
|
||||||
|
}, [selectedCount > 0, startingConversation, updateNavigationButtons]);
|
||||||
|
|
||||||
|
const data = useMemo(() => {
|
||||||
|
if (term) {
|
||||||
|
const exactMatches: UserProfile[] = [];
|
||||||
|
const filterByTerm = (p: UserProfile) => {
|
||||||
|
if (selectedCount > 0 && p.id === currentUserId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.username === term || p.username.startsWith(term)) {
|
||||||
|
exactMatches.push(p);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const results = filterProfilesMatchingTerm(searchResults, term).filter(filterByTerm);
|
||||||
|
return [...exactMatches, ...results];
|
||||||
|
}
|
||||||
|
return profiles;
|
||||||
|
}, [term, isSearch && selectedCount, isSearch && searchResults, profiles]);
|
||||||
|
|
||||||
|
if (startingConversation) {
|
||||||
|
return (
|
||||||
|
<View style={style.container}>
|
||||||
|
<Loading color={theme.centerChannelColor}/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaView
|
||||||
|
style={style.container}
|
||||||
|
testID='add_members.screen'
|
||||||
|
>
|
||||||
|
<View style={style.searchBar}>
|
||||||
|
<Search
|
||||||
|
testID='add_members.search_bar'
|
||||||
|
placeholder={intl.formatMessage({id: 'search_bar.search', defaultMessage: 'Search'})}
|
||||||
|
cancelButtonTitle={intl.formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'})}
|
||||||
|
placeholderTextColor={changeOpacity(theme.centerChannelColor, 0.5)}
|
||||||
|
onChangeText={onSearch}
|
||||||
|
onSubmitEditing={search}
|
||||||
|
onCancel={clearSearch}
|
||||||
|
autoCapitalize='none'
|
||||||
|
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
|
||||||
|
value={term}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
{selectedCount > 0 &&
|
||||||
|
<SelectedUsers
|
||||||
|
selectedIds={selectedIds}
|
||||||
|
warnCount={General.MAX_USERS_IN_GM - 2}
|
||||||
|
maxCount={General.MAX_USERS_IN_GM}
|
||||||
|
onRemove={handleRemoveProfile}
|
||||||
|
teammateNameDisplay={teammateNameDisplay}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
<UserList
|
||||||
|
currentUserId={currentUserId}
|
||||||
|
handleSelectProfile={handleSelectProfile}
|
||||||
|
loading={loading}
|
||||||
|
profiles={data}
|
||||||
|
selectedIds={selectedIds}
|
||||||
|
showNoResults={!loading && page.current !== -1}
|
||||||
|
teammateNameDisplay={teammateNameDisplay}
|
||||||
|
fetchMore={getProfiles}
|
||||||
|
term={term}
|
||||||
|
testID='add_members.user_list'
|
||||||
|
tutorialWatched={tutorialWatched}
|
||||||
|
/>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
}
|
||||||
25
app/screens/channel_add_people/index.tsx
Normal file
25
app/screens/channel_add_people/index.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||||
|
import withObservables from '@nozbe/with-observables';
|
||||||
|
|
||||||
|
import {observeCurrentTeamId} from '@app/queries/servers/system';
|
||||||
|
import {observeProfileLongPresTutorial} from '@queries/app/global';
|
||||||
|
import {observeCurrentChannel} from '@queries/servers/channel';
|
||||||
|
import {observeTeammateNameDisplay} from '@queries/servers/user';
|
||||||
|
|
||||||
|
import ChannelAddPeople from './channel_add_people';
|
||||||
|
|
||||||
|
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||||
|
|
||||||
|
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
||||||
|
return {
|
||||||
|
currentChannel: observeCurrentChannel(database),
|
||||||
|
currentTeamId: observeCurrentTeamId(database),
|
||||||
|
teammateNameDisplay: observeTeammateNameDisplay(database),
|
||||||
|
tutorialWatched: observeProfileLongPresTutorial(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
export default withDatabase(enhanced(ChannelAddPeople));
|
||||||
@@ -11,6 +11,8 @@ import {fetchProfiles, fetchProfilesInTeam, searchProfiles} from '@actions/remot
|
|||||||
import CompassIcon from '@components/compass_icon';
|
import CompassIcon from '@components/compass_icon';
|
||||||
import Loading from '@components/loading';
|
import Loading from '@components/loading';
|
||||||
import Search from '@components/search';
|
import Search from '@components/search';
|
||||||
|
import SelectedUsers from '@components/selected_users_panel';
|
||||||
|
import UserList from '@components/user_list';
|
||||||
import {General} from '@constants';
|
import {General} from '@constants';
|
||||||
import {useServerUrl} from '@context/server';
|
import {useServerUrl} from '@context/server';
|
||||||
import {useTheme} from '@context/theme';
|
import {useTheme} from '@context/theme';
|
||||||
@@ -22,9 +24,6 @@ import {alertErrorWithFallback} from '@utils/draft';
|
|||||||
import {changeOpacity, getKeyboardAppearanceFromTheme, makeStyleSheetFromTheme} from '@utils/theme';
|
import {changeOpacity, getKeyboardAppearanceFromTheme, makeStyleSheetFromTheme} from '@utils/theme';
|
||||||
import {displayUsername, filterProfilesMatchingTerm} from '@utils/user';
|
import {displayUsername, filterProfilesMatchingTerm} from '@utils/user';
|
||||||
|
|
||||||
import SelectedUsers from './selected_users';
|
|
||||||
import UserList from './user_list';
|
|
||||||
|
|
||||||
const START_BUTTON = 'start-conversation';
|
const START_BUTTON = 'start-conversation';
|
||||||
const CLOSE_BUTTON = 'close-dms';
|
const CLOSE_BUTTON = 'close-dms';
|
||||||
|
|
||||||
|
|||||||
@@ -97,6 +97,9 @@ Navigation.setLazyComponentRegistrator((screenName) => {
|
|||||||
case Screens.CREATE_DIRECT_MESSAGE:
|
case Screens.CREATE_DIRECT_MESSAGE:
|
||||||
screen = withServerDatabase(require('@screens/create_direct_message').default);
|
screen = withServerDatabase(require('@screens/create_direct_message').default);
|
||||||
break;
|
break;
|
||||||
|
case Screens.CHANNEL_ADD_PEOPLE:
|
||||||
|
screen = withServerDatabase(require('@screens/channel_add_people').default);
|
||||||
|
break;
|
||||||
case Screens.EDIT_POST:
|
case Screens.EDIT_POST:
|
||||||
screen = withServerDatabase(require('@screens/edit_post').default);
|
screen = withServerDatabase(require('@screens/edit_post').default);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -398,6 +398,9 @@
|
|||||||
"mobile.calls_you": "(you)",
|
"mobile.calls_you": "(you)",
|
||||||
"mobile.camera_photo_permission_denied_description": "Take photos and upload them to your server or save them to your device. Open Settings to grant {applicationName} read and write access to your camera.",
|
"mobile.camera_photo_permission_denied_description": "Take photos and upload them to your server or save them to your device. Open Settings to grant {applicationName} read and write access to your camera.",
|
||||||
"mobile.camera_photo_permission_denied_title": "{applicationName} would like to access your camera",
|
"mobile.camera_photo_permission_denied_title": "{applicationName} would like to access your camera",
|
||||||
|
"mobile.channel_add_people.error": "We could not add those users to the channel. Please check your connection and try again.",
|
||||||
|
"mobile.channel_add_people.title": "Add Members",
|
||||||
|
"mobile.channel_add_people.button": "Add",
|
||||||
"mobile.channel_info.alertNo": "No",
|
"mobile.channel_info.alertNo": "No",
|
||||||
"mobile.channel_info.alertYes": "Yes",
|
"mobile.channel_info.alertYes": "Yes",
|
||||||
"mobile.channel_list.recent": "Recent",
|
"mobile.channel_list.recent": "Recent",
|
||||||
|
|||||||
Reference in New Issue
Block a user