[NEW] Direct Message between multiple users (#1958)
* [WIP] DM between multiple users * [WIP][NEW] Create new DM between multiple users * [IMPROVEMENT] Improve createChannel Sagas * [IMPROVEMENT] Selected Users view * [IMPROVEMENT] Room Actions of Group DM * [NEW] Create new DM between multiple users * [NEW] Group DM avatar * [FIX] Directory border * [IMPROVEMENT] Use isGroupChat * [CHORE] Remove legacy getRoomMemberId * [NEW] RoomTypeIcon * [FIX] No use legacy method on RoomInfoView * [FIX] Blink header when create new DM * [FIX] Only show create direct message option when allowed * [FIX] RoomInfoView * pt-BR * Few fixes * Create button name * Show create button only after a user is selected * Fix max users issues Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
parent
ece8f44f5a
commit
076e5e87c6
|
@ -50,6 +50,9 @@ export default {
|
|||
CROWD_Enable: {
|
||||
type: 'valueAsBoolean'
|
||||
},
|
||||
DirectMesssage_maxUsers: {
|
||||
type: 'valueAsNumber'
|
||||
},
|
||||
Accounts_Directory_DefaultView: {
|
||||
type: 'valueAsString'
|
||||
},
|
||||
|
|
|
@ -15,7 +15,7 @@ const styles = StyleSheet.create({
|
|||
});
|
||||
|
||||
const RoomTypeIcon = React.memo(({
|
||||
type, size, style, theme
|
||||
type, size, isGroupChat, style, theme
|
||||
}) => {
|
||||
if (!type) {
|
||||
return null;
|
||||
|
@ -31,6 +31,9 @@ const RoomTypeIcon = React.memo(({
|
|||
if (type === 'c') {
|
||||
return <Image source={{ uri: 'hashtag' }} style={[styles.style, style, { width: size, height: size, tintColor: color }]} />;
|
||||
} if (type === 'd') {
|
||||
if (isGroupChat) {
|
||||
return <CustomIcon name='team' size={13} style={[styles.style, styles.discussion, { color }]} />;
|
||||
}
|
||||
return <CustomIcon name='at' size={13} style={[styles.style, styles.discussion, { color }]} />;
|
||||
} if (type === 'l') {
|
||||
return <CustomIcon name='livechat' size={13} style={[styles.style, styles.discussion, { color }]} />;
|
||||
|
@ -41,6 +44,7 @@ const RoomTypeIcon = React.memo(({
|
|||
RoomTypeIcon.propTypes = {
|
||||
theme: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
isGroupChat: PropTypes.bool,
|
||||
size: PropTypes.number,
|
||||
style: PropTypes.object
|
||||
};
|
||||
|
|
|
@ -156,6 +156,7 @@ export default {
|
|||
Whats_the_password_for_your_certificate: 'What\'s the password for your certificate?',
|
||||
Create_account: 'Create an account',
|
||||
Create_Channel: 'Create Channel',
|
||||
Create_Direct_Messages: 'Create Direct Messages',
|
||||
Create_Discussion: 'Create Discussion',
|
||||
Created_snippet: 'Created a snippet',
|
||||
Create_a_new_workspace: 'Create a new workspace',
|
||||
|
@ -263,6 +264,7 @@ export default {
|
|||
Logging_out: 'Logging out.',
|
||||
Logout: 'Logout',
|
||||
Max_number_of_uses: 'Max number of uses',
|
||||
Max_number_of_users_allowed_is_number: 'Max number of users allowed is {{maxUsers}}',
|
||||
members: 'members',
|
||||
Members: 'Members',
|
||||
Mentioned_Messages: 'Mentioned Messages',
|
||||
|
|
|
@ -153,6 +153,7 @@ export default {
|
|||
Permalink: 'Link-Permanente',
|
||||
Create_account: 'Criar conta',
|
||||
Create_Channel: 'Criar Canal',
|
||||
Create_Direct_Messages: 'Criar Mensagens Diretas',
|
||||
Create_Discussion: 'Criar Discussão',
|
||||
Created_snippet: 'Criou um snippet',
|
||||
Create_a_new_workspace: 'Criar nova área de trabalho',
|
||||
|
@ -247,6 +248,7 @@ export default {
|
|||
Logout: 'Sair',
|
||||
Logging_out: 'Saindo.',
|
||||
Max_number_of_uses: 'Número máximo de usos',
|
||||
Max_number_of_users_allowed_is_number: 'Número máximo de usuários é {{maxUsers}}',
|
||||
Members: 'Membros',
|
||||
Mentioned_Messages: 'Mensagens mencionadas',
|
||||
mentioned: 'mencionado',
|
||||
|
|
|
@ -91,4 +91,8 @@ export default class Subscription extends Model {
|
|||
@field('hide_unread_status') hideUnreadStatus;
|
||||
|
||||
@json('sys_mes', sanitizer) sysMes;
|
||||
|
||||
@json('uids', sanitizer) uids;
|
||||
|
||||
@json('usernames', sanitizer) usernames;
|
||||
}
|
||||
|
|
|
@ -62,6 +62,18 @@ export default schemaMigrations({
|
|||
]
|
||||
})
|
||||
]
|
||||
},
|
||||
{
|
||||
toVersion: 7,
|
||||
steps: [
|
||||
addColumns({
|
||||
table: 'subscriptions',
|
||||
columns: [
|
||||
{ name: 'uids', type: 'string', isOptional: true },
|
||||
{ name: 'usernames', type: 'string', isOptional: true }
|
||||
]
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { appSchema, tableSchema } from '@nozbe/watermelondb';
|
||||
|
||||
export default appSchema({
|
||||
version: 6,
|
||||
version: 7,
|
||||
tables: [
|
||||
tableSchema({
|
||||
name: 'subscriptions',
|
||||
|
@ -40,7 +40,9 @@ export default appSchema({
|
|||
{ name: 'auto_translate', type: 'boolean', isOptional: true },
|
||||
{ name: 'auto_translate_language', type: 'string' },
|
||||
{ name: 'hide_unread_status', type: 'boolean', isOptional: true },
|
||||
{ name: 'sys_mes', type: 'string', isOptional: true }
|
||||
{ name: 'sys_mes', type: 'string', isOptional: true },
|
||||
{ name: 'uids', type: 'string', isOptional: true },
|
||||
{ name: 'usernames', type: 'string', isOptional: true }
|
||||
]
|
||||
}),
|
||||
tableSchema({
|
||||
|
|
|
@ -21,6 +21,8 @@ export const merge = (subscription, room) => {
|
|||
subscription.archived = room.archived || false;
|
||||
subscription.joinCodeRequired = room.joinCodeRequired;
|
||||
subscription.jitsiTimeout = room.jitsiTimeout;
|
||||
subscription.usernames = room.usernames;
|
||||
subscription.uids = room.uids;
|
||||
}
|
||||
subscription.ro = room.ro;
|
||||
subscription.broadcast = room.broadcast;
|
||||
|
|
|
@ -71,7 +71,9 @@ const createOrUpdateSubscription = async(subscription, room) => {
|
|||
jitsiTimeout: s.jitsiTimeout,
|
||||
autoTranslate: s.autoTranslate,
|
||||
autoTranslateLanguage: s.autoTranslateLanguage,
|
||||
lastMessage: s.lastMessage
|
||||
lastMessage: s.lastMessage,
|
||||
usernames: s.usernames,
|
||||
uids: s.uids
|
||||
};
|
||||
} catch (error) {
|
||||
try {
|
||||
|
|
|
@ -607,6 +607,14 @@ const RocketChat = {
|
|||
return this.sdk.post('im.create', { username });
|
||||
},
|
||||
|
||||
createGroupChat() {
|
||||
let { users } = reduxStore.getState().selectedUsers;
|
||||
users = users.map(u => u.name);
|
||||
|
||||
// RC 3.1.0
|
||||
return this.sdk.methodCall('createDirectMessage', ...users);
|
||||
},
|
||||
|
||||
createDiscussion({
|
||||
prid, pmid, t_name, reply, users
|
||||
}) {
|
||||
|
@ -784,12 +792,27 @@ const RocketChat = {
|
|||
// RC 0.72.0
|
||||
return this.sdk.get('rooms.info', { roomId });
|
||||
},
|
||||
getRoomMemberId(rid, currentUserId) {
|
||||
if (rid === `${ currentUserId }${ currentUserId }`) {
|
||||
return currentUserId;
|
||||
|
||||
getUidDirectMessage(room, userId) {
|
||||
// legacy method
|
||||
if (!room.uids && room.rid && room.t === 'd') {
|
||||
return room.rid.replace(userId, '').trim();
|
||||
}
|
||||
return rid.replace(currentUserId, '').trim();
|
||||
|
||||
if (RocketChat.isGroupChat(room)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const me = room && room.uids && room.uids.find(uid => uid === userId);
|
||||
const other = room && room.uids && room.uids.filter(uid => uid !== userId);
|
||||
|
||||
return other && other.length ? other[0] : me;
|
||||
},
|
||||
|
||||
isGroupChat(room) {
|
||||
return (room.uids && room.uids.length > 2) || (room.usernames && room.usernames.length > 2);
|
||||
},
|
||||
|
||||
toggleBlockUser(rid, blocked, block) {
|
||||
if (block) {
|
||||
// RC 0.49.0
|
||||
|
@ -1121,9 +1144,16 @@ const RocketChat = {
|
|||
},
|
||||
getRoomTitle(room) {
|
||||
const { UI_Use_Real_Name: useRealName } = reduxStore.getState().settings;
|
||||
const { username } = reduxStore.getState().login.user;
|
||||
if (RocketChat.isGroupChat(room) && !(room.name && room.name.length)) {
|
||||
return room.usernames.filter(u => u !== username).sort((u1, u2) => u1.localeCompare(u2)).join(', ');
|
||||
}
|
||||
return ((room.prid || useRealName) && room.fname) || room.name;
|
||||
},
|
||||
getRoomAvatar(room) {
|
||||
if (RocketChat.isGroupChat(room)) {
|
||||
return room.uids.length + room.usernames.join();
|
||||
}
|
||||
return room.prid ? room.fname : room.name;
|
||||
},
|
||||
|
||||
|
|
|
@ -6,19 +6,20 @@ import RoomTypeIcon from '../../containers/RoomTypeIcon';
|
|||
import styles from './styles';
|
||||
|
||||
const TypeIcon = React.memo(({
|
||||
theme, type, prid, status
|
||||
theme, type, prid, status, isGroupChat
|
||||
}) => {
|
||||
if (type === 'd') {
|
||||
if (type === 'd' && !isGroupChat) {
|
||||
return <Status style={styles.status} size={10} status={status} />;
|
||||
}
|
||||
return <RoomTypeIcon theme={theme} type={prid ? 'discussion' : type} />;
|
||||
return <RoomTypeIcon theme={theme} type={prid ? 'discussion' : type} isGroupChat={isGroupChat} />;
|
||||
});
|
||||
|
||||
TypeIcon.propTypes = {
|
||||
theme: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
status: PropTypes.string,
|
||||
prid: PropTypes.string
|
||||
prid: PropTypes.string,
|
||||
isGroupChat: PropTypes.bool
|
||||
};
|
||||
|
||||
export default TypeIcon;
|
||||
|
|
|
@ -40,12 +40,11 @@ const arePropsEqual = (oldProps, newProps) => {
|
|||
};
|
||||
|
||||
const RoomItem = React.memo(({
|
||||
onPress, width, favorite, toggleFav, isRead, rid, toggleRead, hideChannel, testID, unread, userMentions, name, _updatedAt, alert, type, avatarSize, baseUrl, userId, username, token, id, prid, showLastMessage, hideUnreadStatus, lastMessage, status, avatar, useRealName, getUserPresence, theme
|
||||
onPress, width, favorite, toggleFav, isRead, rid, toggleRead, hideChannel, testID, unread, userMentions, name, _updatedAt, alert, type, avatarSize, baseUrl, userId, username, token, id, prid, showLastMessage, hideUnreadStatus, lastMessage, status, avatar, useRealName, getUserPresence, isGroupChat, theme
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
if (type === 'd' && rid) {
|
||||
const uid = rid.replace(userId, '');
|
||||
getUserPresence(uid);
|
||||
if (type === 'd') {
|
||||
getUserPresence(id);
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
@ -104,9 +103,9 @@ const RoomItem = React.memo(({
|
|||
<View style={styles.titleContainer}>
|
||||
<TypeIcon
|
||||
type={type}
|
||||
id={id}
|
||||
prid={prid}
|
||||
status={status}
|
||||
isGroupChat={isGroupChat}
|
||||
theme={theme}
|
||||
/>
|
||||
<Text
|
||||
|
@ -198,6 +197,7 @@ RoomItem.propTypes = {
|
|||
hideUnreadStatus: PropTypes.bool,
|
||||
useRealName: PropTypes.bool,
|
||||
getUserPresence: PropTypes.func,
|
||||
isGroupChat: PropTypes.bool,
|
||||
theme: PropTypes.string
|
||||
};
|
||||
|
||||
|
|
|
@ -64,8 +64,8 @@ const UserItem = ({
|
|||
<View style={[styles.container, styles.button, style]}>
|
||||
<Avatar text={username} size={30} type='d' style={styles.avatar} baseUrl={baseUrl} userId={user.id} token={user.token} />
|
||||
<View style={styles.textContainer}>
|
||||
<Text style={[styles.name, { color: themes[theme].titleText }]}>{name}</Text>
|
||||
<Text style={[styles.username, { color: themes[theme].auxiliaryText }]}>@{username}</Text>
|
||||
<Text style={[styles.name, { color: themes[theme].titleText }]} numberOfLines={1}>{name}</Text>
|
||||
<Text style={[styles.username, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>@{username}</Text>
|
||||
</View>
|
||||
{icon ? <CustomIcon name={icon} size={22} style={[styles.icon, { color: themes[theme].actionTintColor }]} /> : null}
|
||||
</View>
|
||||
|
|
|
@ -5,11 +5,18 @@ import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
|
|||
|
||||
import { CREATE_CHANNEL, LOGIN } from '../actions/actionsTypes';
|
||||
import { createChannelSuccess, createChannelFailure } from '../actions/createChannel';
|
||||
import { showErrorAlert } from '../utils/info';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import Navigation from '../lib/Navigation';
|
||||
import database from '../lib/database';
|
||||
import I18n from '../i18n';
|
||||
|
||||
const create = function* create(data) {
|
||||
return yield RocketChat.createChannel(data);
|
||||
const createChannel = function createChannel(data) {
|
||||
return RocketChat.createChannel(data);
|
||||
};
|
||||
|
||||
const createGroupChat = function createGroupChat() {
|
||||
return RocketChat.createGroupChat();
|
||||
};
|
||||
|
||||
const handleRequest = function* handleRequest({ data }) {
|
||||
|
@ -18,7 +25,13 @@ const handleRequest = function* handleRequest({ data }) {
|
|||
if (!auth) {
|
||||
yield take(LOGIN.SUCCESS);
|
||||
}
|
||||
const sub = yield call(create, data);
|
||||
|
||||
let sub;
|
||||
if (data.group) {
|
||||
sub = yield call(createGroupChat);
|
||||
} else {
|
||||
sub = yield call(createChannel, data);
|
||||
}
|
||||
|
||||
try {
|
||||
const db = database.active;
|
||||
|
@ -39,8 +52,22 @@ const handleRequest = function* handleRequest({ data }) {
|
|||
}
|
||||
};
|
||||
|
||||
const handleSuccess = function handleSuccess({ data }) {
|
||||
const { rid, t } = data;
|
||||
Navigation.navigate('RoomView', { rid, t, name: RocketChat.getRoomTitle(data) });
|
||||
};
|
||||
|
||||
const handleFailure = function handleFailure({ err }) {
|
||||
setTimeout(() => {
|
||||
const msg = err.reason || I18n.t('There_was_an_error_while_action', { action: I18n.t('creating_channel') });
|
||||
showErrorAlert(msg);
|
||||
}, 300);
|
||||
};
|
||||
|
||||
const root = function* root() {
|
||||
yield takeLatest(CREATE_CHANNEL.REQUEST, handleRequest);
|
||||
yield takeLatest(CREATE_CHANNEL.SUCCESS, handleSuccess);
|
||||
yield takeLatest(CREATE_CHANNEL.FAILURE, handleFailure);
|
||||
};
|
||||
|
||||
export default root;
|
||||
|
|
|
@ -16,7 +16,6 @@ import KeyboardView from '../presentation/KeyboardView';
|
|||
import scrollPersistTaps from '../utils/scrollPersistTaps';
|
||||
import I18n from '../i18n';
|
||||
import UserItem from '../presentation/UserItem';
|
||||
import { showErrorAlert } from '../utils/info';
|
||||
import { CustomHeaderButtons, Item } from '../containers/HeaderButton';
|
||||
import StatusBar from '../containers/StatusBar';
|
||||
import { SWITCH_TRACK_COLOR, themes } from '../constants/colors';
|
||||
|
@ -100,7 +99,6 @@ class CreateChannelView extends React.Component {
|
|||
error: PropTypes.object,
|
||||
failure: PropTypes.bool,
|
||||
isFetching: PropTypes.bool,
|
||||
result: PropTypes.object,
|
||||
users: PropTypes.array.isRequired,
|
||||
user: PropTypes.shape({
|
||||
id: PropTypes.string,
|
||||
|
@ -125,9 +123,7 @@ class CreateChannelView extends React.Component {
|
|||
const {
|
||||
channelName, type, readOnly, broadcast
|
||||
} = this.state;
|
||||
const {
|
||||
error, failure, isFetching, result, users, theme
|
||||
} = this.props;
|
||||
const { users, isFetching, theme } = this.props;
|
||||
if (nextProps.theme !== theme) {
|
||||
return true;
|
||||
}
|
||||
|
@ -143,43 +139,15 @@ class CreateChannelView extends React.Component {
|
|||
if (nextState.broadcast !== broadcast) {
|
||||
return true;
|
||||
}
|
||||
if (nextProps.failure !== failure) {
|
||||
return true;
|
||||
}
|
||||
if (nextProps.isFetching !== isFetching) {
|
||||
return true;
|
||||
}
|
||||
if (!equal(nextProps.error, error)) {
|
||||
return true;
|
||||
}
|
||||
if (!equal(nextProps.result, result)) {
|
||||
return true;
|
||||
}
|
||||
if (!equal(nextProps.users, users)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const {
|
||||
isFetching, failure, error, result, navigation
|
||||
} = this.props;
|
||||
|
||||
if (!isFetching && isFetching !== prevProps.isFetching) {
|
||||
setTimeout(() => {
|
||||
if (failure) {
|
||||
const msg = error.reason || I18n.t('There_was_an_error_while_action', { action: I18n.t('creating_channel') });
|
||||
showErrorAlert(msg);
|
||||
} else {
|
||||
const { type } = this.state;
|
||||
const { rid, name } = result;
|
||||
navigation.navigate('RoomView', { rid, name, t: type ? 'p' : 'c' });
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
onChangeText = (channelName) => {
|
||||
const { navigation } = this.props;
|
||||
navigation.setParams({ showSubmit: channelName.trim().length > 0 });
|
||||
|
@ -365,10 +333,7 @@ class CreateChannelView extends React.Component {
|
|||
|
||||
const mapStateToProps = state => ({
|
||||
baseUrl: state.server.server,
|
||||
error: state.createChannel.error,
|
||||
failure: state.createChannel.failure,
|
||||
isFetching: state.createChannel.isFetching,
|
||||
result: state.createChannel.result,
|
||||
users: state.selectedUsers.users,
|
||||
user: getUserSelector(state)
|
||||
});
|
||||
|
|
|
@ -180,7 +180,10 @@ class DirectoryView extends React.Component {
|
|||
|
||||
let style;
|
||||
if (index === data.length - 1) {
|
||||
style = sharedStyles.separatorBottom;
|
||||
style = {
|
||||
...sharedStyles.separatorBottom,
|
||||
borderColor: themes[theme].separatorColor
|
||||
};
|
||||
}
|
||||
|
||||
const commonProps = {
|
||||
|
|
|
@ -25,6 +25,7 @@ import { withTheme } from '../theme';
|
|||
import { themedHeader } from '../utils/navigation';
|
||||
import { getUserSelector } from '../selectors/login';
|
||||
import Navigation from '../lib/Navigation';
|
||||
import { createChannelRequest } from '../actions/createChannel';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
safeAreaView: {
|
||||
|
@ -33,24 +34,21 @@ const styles = StyleSheet.create({
|
|||
separator: {
|
||||
marginLeft: 60
|
||||
},
|
||||
createChannelButton: {
|
||||
marginTop: 25
|
||||
},
|
||||
createDiscussionButton: {
|
||||
marginBottom: 25
|
||||
},
|
||||
createChannelContainer: {
|
||||
button: {
|
||||
height: 46,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
},
|
||||
createChannelIcon: {
|
||||
buttonIcon: {
|
||||
marginLeft: 18,
|
||||
marginRight: 16
|
||||
},
|
||||
createChannelText: {
|
||||
buttonText: {
|
||||
fontSize: 17,
|
||||
...sharedStyles.textRegular
|
||||
},
|
||||
buttonContainer: {
|
||||
paddingVertical: 25
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -68,6 +66,8 @@ class NewMessageView extends React.Component {
|
|||
id: PropTypes.string,
|
||||
token: PropTypes.string
|
||||
}),
|
||||
createChannel: PropTypes.func,
|
||||
maxUsers: PropTypes.number,
|
||||
theme: PropTypes.string
|
||||
};
|
||||
|
||||
|
@ -143,7 +143,35 @@ class NewMessageView extends React.Component {
|
|||
|
||||
createChannel = () => {
|
||||
const { navigation } = this.props;
|
||||
navigation.navigate('SelectedUsersViewCreateChannel', { nextActionID: 'CREATE_CHANNEL', title: I18n.t('Select_Users') });
|
||||
navigation.navigate('SelectedUsersViewCreateChannel', { nextAction: () => navigation.navigate('CreateChannelView') });
|
||||
}
|
||||
|
||||
createGroupChat = () => {
|
||||
const { createChannel, maxUsers, navigation } = this.props;
|
||||
navigation.navigate('SelectedUsersViewCreateChannel', {
|
||||
nextAction: () => createChannel({ group: true }),
|
||||
buttonText: I18n.t('Create'),
|
||||
maxUsers
|
||||
});
|
||||
}
|
||||
|
||||
renderButton = ({
|
||||
onPress, testID, title, icon, first
|
||||
}) => {
|
||||
const { theme } = this.props;
|
||||
return (
|
||||
<Touch
|
||||
onPress={onPress}
|
||||
style={{ backgroundColor: themes[theme].backgroundColor }}
|
||||
testID={testID}
|
||||
theme={theme}
|
||||
>
|
||||
<View style={[first ? sharedStyles.separatorVertical : sharedStyles.separatorBottom, styles.button, { borderColor: themes[theme].separatorColor }]}>
|
||||
<CustomIcon style={[styles.buttonIcon, { color: themes[theme].tintColor }]} size={24} name={icon} />
|
||||
<Text style={[styles.buttonText, { color: themes[theme].tintColor }]}>{title}</Text>
|
||||
</View>
|
||||
</Touch>
|
||||
);
|
||||
}
|
||||
|
||||
createDiscussion = () => {
|
||||
|
@ -151,32 +179,31 @@ class NewMessageView extends React.Component {
|
|||
}
|
||||
|
||||
renderHeader = () => {
|
||||
const { theme } = this.props;
|
||||
const { maxUsers, theme } = this.props;
|
||||
return (
|
||||
<View style={{ backgroundColor: themes[theme].auxiliaryBackground }}>
|
||||
<SearchBox onChangeText={text => this.onSearchChangeText(text)} testID='new-message-view-search' />
|
||||
<Touch
|
||||
onPress={this.createChannel}
|
||||
style={[styles.createChannelButton, { backgroundColor: themes[theme].backgroundColor }]}
|
||||
testID='new-message-view-create-channel'
|
||||
theme={theme}
|
||||
>
|
||||
<View style={[sharedStyles.separatorVertical, styles.createChannelContainer, { borderColor: themes[theme].separatorColor }]}>
|
||||
<CustomIcon style={[styles.createChannelIcon, { color: themes[theme].tintColor }]} size={24} name='hashtag' />
|
||||
<Text style={[styles.createChannelText, { color: themes[theme].tintColor }]}>{I18n.t('Create_Channel')}</Text>
|
||||
</View>
|
||||
</Touch>
|
||||
<Touch
|
||||
onPress={this.createDiscussion}
|
||||
style={[styles.createDiscussionButton, { backgroundColor: themes[theme].backgroundColor }]}
|
||||
testID='new-message-view-create-discussion'
|
||||
theme={theme}
|
||||
>
|
||||
<View style={[sharedStyles.separatorBottom, styles.createChannelContainer, { borderColor: themes[theme].separatorColor }]}>
|
||||
<CustomIcon style={[styles.createChannelIcon, { color: themes[theme].tintColor }]} size={24} name='chat' />
|
||||
<Text style={[styles.createChannelText, { color: themes[theme].tintColor }]}>{I18n.t('Create_Discussion')}</Text>
|
||||
</View>
|
||||
</Touch>
|
||||
<View style={styles.buttonContainer}>
|
||||
{this.renderButton({
|
||||
onPress: this.createChannel,
|
||||
title: I18n.t('Create_Channel'),
|
||||
icon: 'hashtag',
|
||||
testID: 'new-message-view-create-channel',
|
||||
first: true
|
||||
})}
|
||||
{maxUsers > 2 ? this.renderButton({
|
||||
onPress: this.createGroupChat,
|
||||
title: I18n.t('Create_Direct_Messages'),
|
||||
icon: 'team',
|
||||
testID: 'new-message-view-create-direct-message'
|
||||
}) : null}
|
||||
{this.renderButton({
|
||||
onPress: this.createDiscussion,
|
||||
title: I18n.t('Create_Discussion'),
|
||||
icon: 'chat',
|
||||
testID: 'new-message-view-create-discussion'
|
||||
})}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
@ -248,7 +275,12 @@ class NewMessageView extends React.Component {
|
|||
|
||||
const mapStateToProps = state => ({
|
||||
baseUrl: state.server.server,
|
||||
maxUsers: state.settings.DirectMesssage_maxUsers || 1,
|
||||
user: getUserSelector(state)
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(withTheme(NewMessageView));
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
createChannel: params => dispatch(createChannelRequest(params))
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(NewMessageView));
|
||||
|
|
|
@ -8,6 +8,7 @@ import { SafeAreaView } from 'react-navigation';
|
|||
import _ from 'lodash';
|
||||
|
||||
import Touch from '../../utils/touch';
|
||||
import { setLoading as setLoadingAction } from '../../actions/selectedUsers';
|
||||
import { leaveRoom as leaveRoomAction } from '../../actions/room';
|
||||
import styles from './styles';
|
||||
import sharedStyles from '../Styles';
|
||||
|
@ -49,6 +50,7 @@ class RoomActionsView extends React.Component {
|
|||
}),
|
||||
leaveRoom: PropTypes.func,
|
||||
jitsiEnabled: PropTypes.bool,
|
||||
setLoadingInvite: PropTypes.func,
|
||||
theme: PropTypes.string
|
||||
}
|
||||
|
||||
|
@ -190,6 +192,7 @@ class RoomActionsView extends React.Component {
|
|||
const {
|
||||
rid, t, blocker
|
||||
} = room;
|
||||
const isGroupChat = RocketChat.isGroupChat(room);
|
||||
|
||||
const notificationsAction = {
|
||||
icon: 'bell',
|
||||
|
@ -223,6 +226,7 @@ class RoomActionsView extends React.Component {
|
|||
params: {
|
||||
rid, t, room, member
|
||||
},
|
||||
disabled: isGroupChat,
|
||||
testID: 'room-actions-info'
|
||||
}],
|
||||
renderItem: this.renderRoomInfo
|
||||
|
@ -286,7 +290,18 @@ class RoomActionsView extends React.Component {
|
|||
});
|
||||
}
|
||||
|
||||
if (t === 'd') {
|
||||
if (isGroupChat) {
|
||||
sections[2].data.unshift({
|
||||
icon: 'team',
|
||||
name: I18n.t('Members'),
|
||||
description: membersCount > 0 ? `${ membersCount } ${ I18n.t('members') }` : null,
|
||||
route: 'RoomMembersView',
|
||||
params: { rid, room },
|
||||
testID: 'room-actions-members'
|
||||
});
|
||||
}
|
||||
|
||||
if (t === 'd' && !isGroupChat) {
|
||||
sections.push({
|
||||
data: [
|
||||
{
|
||||
|
@ -320,9 +335,9 @@ class RoomActionsView extends React.Component {
|
|||
name: I18n.t('Add_users'),
|
||||
route: 'SelectedUsersView',
|
||||
params: {
|
||||
nextActionID: 'ADD_USER',
|
||||
rid,
|
||||
title: I18n.t('Add_users')
|
||||
title: I18n.t('Add_users'),
|
||||
nextAction: this.addUser
|
||||
},
|
||||
testID: 'room-actions-add-user'
|
||||
});
|
||||
|
@ -369,14 +384,15 @@ class RoomActionsView extends React.Component {
|
|||
|
||||
updateRoomMember = async() => {
|
||||
const { room } = this.state;
|
||||
const { rid } = room;
|
||||
const { user } = this.props;
|
||||
|
||||
try {
|
||||
const roomUserId = RocketChat.getRoomMemberId(rid, user.id);
|
||||
const result = await RocketChat.getUserInfo(roomUserId);
|
||||
if (result.success) {
|
||||
this.setState({ member: result.user });
|
||||
if (!RocketChat.isGroupChat(room)) {
|
||||
const roomUserId = RocketChat.getUidDirectMessage(room, user.id);
|
||||
const result = await RocketChat.getUserInfo(roomUserId);
|
||||
if (result.success) {
|
||||
this.setState({ member: result.user });
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
log(e);
|
||||
|
@ -384,6 +400,21 @@ class RoomActionsView extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
addUser = async() => {
|
||||
const { room } = this.state;
|
||||
const { setLoadingInvite, navigation } = this.props;
|
||||
const { rid } = room;
|
||||
try {
|
||||
setLoadingInvite(true);
|
||||
await RocketChat.addUsersToRoom(rid);
|
||||
navigation.pop();
|
||||
} catch (e) {
|
||||
log(e);
|
||||
} finally {
|
||||
setLoadingInvite(false);
|
||||
}
|
||||
}
|
||||
|
||||
toggleBlockUser = () => {
|
||||
const { room } = this.state;
|
||||
const { rid, blocker } = room;
|
||||
|
@ -432,41 +463,44 @@ class RoomActionsView extends React.Component {
|
|||
const { name, t, topic } = room;
|
||||
const { baseUrl, user, theme } = this.props;
|
||||
|
||||
const avatar = RocketChat.getRoomAvatar(room);
|
||||
|
||||
return (
|
||||
this.renderTouchableItem([
|
||||
<Avatar
|
||||
key='avatar'
|
||||
text={name}
|
||||
size={50}
|
||||
style={styles.avatar}
|
||||
type={t}
|
||||
baseUrl={baseUrl}
|
||||
userId={user.id}
|
||||
token={user.token}
|
||||
>
|
||||
{t === 'd' && member._id ? <Status style={sharedStyles.status} id={member._id} /> : null }
|
||||
</Avatar>,
|
||||
<View key='name' style={styles.roomTitleContainer}>
|
||||
{room.t === 'd'
|
||||
? <Text style={[styles.roomTitle, { color: themes[theme].titleText }]} numberOfLines={1}>{room.fname}</Text>
|
||||
: (
|
||||
<View style={styles.roomTitleRow}>
|
||||
<RoomTypeIcon type={room.prid ? 'discussion' : room.t} theme={theme} />
|
||||
<Text style={[styles.roomTitle, { color: themes[theme].titleText }]} numberOfLines={1}>{room.prid ? room.fname : room.name}</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
<Markdown
|
||||
preview
|
||||
msg={t === 'd' ? `@${ name }` : topic}
|
||||
style={[styles.roomDescription, { color: themes[theme].auxiliaryText }]}
|
||||
numberOfLines={1}
|
||||
theme={theme}
|
||||
/>
|
||||
{room.t === 'd' && <Markdown msg={member.statusText} style={[styles.roomDescription, { color: themes[theme].auxiliaryText }]} preview theme={theme} />}
|
||||
</View>,
|
||||
<DisclosureIndicator theme={theme} key='disclosure-indicator' />
|
||||
], item)
|
||||
this.renderTouchableItem((
|
||||
<>
|
||||
<Avatar
|
||||
text={avatar}
|
||||
size={50}
|
||||
style={styles.avatar}
|
||||
type={t}
|
||||
baseUrl={baseUrl}
|
||||
userId={user.id}
|
||||
token={user.token}
|
||||
>
|
||||
{t === 'd' && member._id ? <Status style={sharedStyles.status} id={member._id} /> : null }
|
||||
</Avatar>
|
||||
<View style={styles.roomTitleContainer}>
|
||||
{room.t === 'd'
|
||||
? <Text style={[styles.roomTitle, { color: themes[theme].titleText }]} numberOfLines={1}>{room.fname}</Text>
|
||||
: (
|
||||
<View style={styles.roomTitleRow}>
|
||||
<RoomTypeIcon type={room.prid ? 'discussion' : room.t} theme={theme} />
|
||||
<Text style={[styles.roomTitle, { color: themes[theme].titleText }]} numberOfLines={1}>{room.prid ? room.fname : room.name}</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
<Markdown
|
||||
preview
|
||||
msg={t === 'd' ? `@${ name }` : topic}
|
||||
style={[styles.roomDescription, { color: themes[theme].auxiliaryText }]}
|
||||
numberOfLines={1}
|
||||
theme={theme}
|
||||
/>
|
||||
{room.t === 'd' && <Markdown msg={member.statusText} style={[styles.roomDescription, { color: themes[theme].auxiliaryText }]} preview theme={theme} />}
|
||||
</View>
|
||||
{!item.disabled && <DisclosureIndicator theme={theme} />}
|
||||
</>
|
||||
), item)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -478,10 +512,11 @@ class RoomActionsView extends React.Component {
|
|||
style={{ backgroundColor: themes[theme].backgroundColor }}
|
||||
accessibilityLabel={item.name}
|
||||
accessibilityTraits='button'
|
||||
enabled={!item.disabled}
|
||||
testID={item.testID}
|
||||
theme={theme}
|
||||
>
|
||||
<View style={[styles.sectionItem, item.disabled && styles.sectionItemDisabled]}>
|
||||
<View style={styles.sectionItem}>
|
||||
{subview}
|
||||
</View>
|
||||
</Touch>
|
||||
|
@ -491,15 +526,19 @@ class RoomActionsView extends React.Component {
|
|||
renderItem = ({ item }) => {
|
||||
const { theme } = this.props;
|
||||
const colorDanger = { color: themes[theme].dangerColor };
|
||||
const subview = item.type === 'danger' ? [
|
||||
<CustomIcon key='icon' name={item.icon} size={24} style={[styles.sectionItemIcon, colorDanger]} />,
|
||||
<Text key='name' style={[styles.sectionItemName, colorDanger]}>{ item.name }</Text>
|
||||
] : [
|
||||
<CustomIcon key='left-icon' name={item.icon} size={24} style={[styles.sectionItemIcon, { color: themes[theme].bodyText }]} />,
|
||||
<Text key='name' style={[styles.sectionItemName, { color: themes[theme].bodyText }]}>{ item.name }</Text>,
|
||||
item.description ? <Text key='description' style={[styles.sectionItemDescription, { color: themes[theme].auxiliaryText }]}>{ item.description }</Text> : null,
|
||||
<DisclosureIndicator theme={theme} key='disclosure-indicator' />
|
||||
];
|
||||
const subview = item.type === 'danger' ? (
|
||||
<>
|
||||
<CustomIcon name={item.icon} size={24} style={[styles.sectionItemIcon, colorDanger]} />
|
||||
<Text style={[styles.sectionItemName, colorDanger]}>{ item.name }</Text>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<CustomIcon name={item.icon} size={24} style={[styles.sectionItemIcon, { color: themes[theme].bodyText }]} />
|
||||
<Text style={[styles.sectionItemName, { color: themes[theme].bodyText }]}>{ item.name }</Text>
|
||||
{item.description ? <Text style={[styles.sectionItemDescription, { color: themes[theme].auxiliaryText }]}>{ item.description }</Text> : null}
|
||||
<DisclosureIndicator theme={theme} />
|
||||
</>
|
||||
);
|
||||
return this.renderTouchableItem(subview, item);
|
||||
}
|
||||
|
||||
|
@ -542,7 +581,8 @@ const mapStateToProps = state => ({
|
|||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
leaveRoom: (rid, t) => dispatch(leaveRoomAction(rid, t))
|
||||
leaveRoom: (rid, t) => dispatch(leaveRoomAction(rid, t)),
|
||||
setLoadingInvite: loading => dispatch(setLoadingAction(loading))
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(RoomActionsView));
|
||||
|
|
|
@ -14,9 +14,6 @@ export default StyleSheet.create({
|
|||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
},
|
||||
sectionItemDisabled: {
|
||||
opacity: 0.3
|
||||
},
|
||||
sectionItemIcon: {
|
||||
width: 56,
|
||||
textAlign: 'center'
|
||||
|
|
|
@ -77,22 +77,21 @@ class RoomInfoView extends React.Component {
|
|||
this.rid = props.navigation.getParam('rid');
|
||||
this.t = props.navigation.getParam('t');
|
||||
this.state = {
|
||||
room: room || {},
|
||||
room: room || { rid: this.rid, t: this.t },
|
||||
roomUser: roomUser || {},
|
||||
parsedRoles: []
|
||||
};
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const { roomUser } = this.state;
|
||||
const { roomUser, room: roomState } = this.state;
|
||||
if (this.t === 'd' && !_.isEmpty(roomUser)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.t === 'd') {
|
||||
const { user } = this.props;
|
||||
const roomUserId = RocketChat.getRoomMemberId(this.rid, user.id);
|
||||
try {
|
||||
const roomUserId = RocketChat.getUidDirectMessage(roomState);
|
||||
const result = await RocketChat.getUserInfo(roomUserId);
|
||||
if (result.success) {
|
||||
const { roles } = result.user;
|
||||
|
@ -110,6 +109,7 @@ class RoomInfoView extends React.Component {
|
|||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const { navigation } = this.props;
|
||||
let room = navigation.getParam('room');
|
||||
if (room && room.observe) {
|
||||
|
|
|
@ -125,7 +125,7 @@ HeaderTitle.propTypes = {
|
|||
};
|
||||
|
||||
const Header = React.memo(({
|
||||
title, subtitle, type, status, usersTyping, width, height, prid, tmid, widthOffset, connecting, goRoomActionsView, theme
|
||||
title, subtitle, type, status, usersTyping, width, height, prid, tmid, widthOffset, connecting, goRoomActionsView, roomUserId, theme
|
||||
}) => {
|
||||
const portrait = height > width;
|
||||
let scale = 1;
|
||||
|
@ -146,7 +146,7 @@ const Header = React.memo(({
|
|||
disabled={tmid}
|
||||
>
|
||||
<View style={[styles.titleContainer, tmid && styles.threadContainer]}>
|
||||
<Icon type={prid ? 'discussion' : type} status={status} theme={theme} />
|
||||
<Icon type={prid ? 'discussion' : type} status={status} roomUserId={roomUserId} theme={theme} />
|
||||
<HeaderTitle
|
||||
title={title}
|
||||
tmid={tmid}
|
||||
|
@ -174,6 +174,7 @@ Header.propTypes = {
|
|||
usersTyping: PropTypes.array,
|
||||
widthOffset: PropTypes.number,
|
||||
connecting: PropTypes.bool,
|
||||
roomUserId: PropTypes.string,
|
||||
goRoomActionsView: PropTypes.func
|
||||
};
|
||||
|
||||
|
|
|
@ -21,13 +21,15 @@ const styles = StyleSheet.create({
|
|||
}
|
||||
});
|
||||
|
||||
const Icon = React.memo(({ type, status, theme }) => {
|
||||
if (type === 'd') {
|
||||
const Icon = React.memo(({
|
||||
roomUserId, type, status, theme
|
||||
}) => {
|
||||
if (type === 'd' && roomUserId) {
|
||||
return <Status size={10} style={styles.status} status={status} />;
|
||||
}
|
||||
|
||||
let colorStyle = {};
|
||||
if (type === 'd') {
|
||||
if (type === 'd' && roomUserId) {
|
||||
colorStyle = { color: STATUS_COLORS[status] };
|
||||
} else {
|
||||
colorStyle = { color: isAndroid && theme === 'light' ? themes[theme].buttonText : themes[theme].auxiliaryText };
|
||||
|
@ -42,6 +44,8 @@ const Icon = React.memo(({ type, status, theme }) => {
|
|||
icon = 'hashtag';
|
||||
} else if (type === 'l') {
|
||||
icon = 'livechat';
|
||||
} else if (type === 'd') {
|
||||
icon = 'team';
|
||||
} else {
|
||||
icon = 'lock';
|
||||
}
|
||||
|
@ -62,6 +66,7 @@ const Icon = React.memo(({ type, status, theme }) => {
|
|||
});
|
||||
|
||||
Icon.propTypes = {
|
||||
roomUserId: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
status: PropTypes.string,
|
||||
theme: PropTypes.string
|
||||
|
|
|
@ -23,6 +23,7 @@ class RoomHeaderView extends Component {
|
|||
statusText: PropTypes.string,
|
||||
connecting: PropTypes.bool,
|
||||
theme: PropTypes.string,
|
||||
roomUserId: PropTypes.string,
|
||||
widthOffset: PropTypes.number,
|
||||
goRoomActionsView: PropTypes.func
|
||||
};
|
||||
|
@ -69,7 +70,7 @@ class RoomHeaderView extends Component {
|
|||
|
||||
render() {
|
||||
const {
|
||||
window, title, subtitle, type, prid, tmid, widthOffset, status = 'offline', statusText, connecting, usersTyping, goRoomActionsView, theme
|
||||
window, title, subtitle, type, prid, tmid, widthOffset, status = 'offline', statusText, connecting, usersTyping, goRoomActionsView, roomUserId, theme
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
|
@ -85,6 +86,7 @@ class RoomHeaderView extends Component {
|
|||
theme={theme}
|
||||
usersTyping={usersTyping}
|
||||
widthOffset={widthOffset}
|
||||
roomUserId={roomUserId}
|
||||
goRoomActionsView={goRoomActionsView}
|
||||
connecting={connecting}
|
||||
/>
|
||||
|
@ -95,13 +97,12 @@ class RoomHeaderView extends Component {
|
|||
const mapStateToProps = (state, ownProps) => {
|
||||
let status;
|
||||
let statusText;
|
||||
const { rid, type } = ownProps;
|
||||
const { roomUserId, type } = ownProps;
|
||||
if (type === 'd') {
|
||||
const user = getUserSelector(state);
|
||||
if (user.id) {
|
||||
const userId = rid.replace(user.id, '').trim();
|
||||
if (state.activeUsers[userId]) {
|
||||
({ status, statusText } = state.activeUsers[userId]);
|
||||
if (state.activeUsers[roomUserId]) {
|
||||
({ status, statusText } = state.activeUsers[roomUserId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,6 +84,7 @@ class RoomView extends React.Component {
|
|||
const toggleFollowThread = navigation.getParam('toggleFollowThread', () => {});
|
||||
const goRoomActionsView = navigation.getParam('goRoomActionsView', () => {});
|
||||
const unreadsCount = navigation.getParam('unreadsCount', null);
|
||||
const roomUserId = navigation.getParam('roomUserId');
|
||||
if (!rid) {
|
||||
return {
|
||||
...themedHeader(screenProps.theme)
|
||||
|
@ -100,6 +101,7 @@ class RoomView extends React.Component {
|
|||
subtitle={subtitle}
|
||||
type={t}
|
||||
widthOffset={tmid ? 95 : 130}
|
||||
roomUserId={roomUserId}
|
||||
goRoomActionsView={goRoomActionsView}
|
||||
/>
|
||||
),
|
||||
|
@ -382,13 +384,16 @@ class RoomView extends React.Component {
|
|||
|
||||
getRoomMember = async() => {
|
||||
const { room } = this.state;
|
||||
const { rid, t } = room;
|
||||
const { t } = room;
|
||||
|
||||
if (t === 'd') {
|
||||
const { user } = this.props;
|
||||
if (t === 'd' && !RocketChat.isGroupChat(room)) {
|
||||
const { user, navigation } = this.props;
|
||||
|
||||
try {
|
||||
const roomUserId = RocketChat.getRoomMemberId(rid, user.id);
|
||||
const roomUserId = RocketChat.getUidDirectMessage(room, user.id);
|
||||
|
||||
navigation.setParams({ roomUserId });
|
||||
|
||||
const result = await RocketChat.getUserInfo(roomUserId);
|
||||
if (result.success) {
|
||||
return result.user;
|
||||
|
|
|
@ -412,7 +412,9 @@ class RoomsListView extends React.Component {
|
|||
key: item._id,
|
||||
rid: item.rid,
|
||||
type: item.t,
|
||||
prid: item.prid
|
||||
prid: item.prid,
|
||||
uids: item.uids,
|
||||
usernames: item.usernames
|
||||
}));
|
||||
|
||||
// unread
|
||||
|
@ -526,6 +528,11 @@ class RoomsListView extends React.Component {
|
|||
|
||||
getUserPresence = uid => RocketChat.getUserPresence(uid)
|
||||
|
||||
getUidDirectMessage = (room) => {
|
||||
const { user: { id } } = this.props;
|
||||
return RocketChat.getUidDirectMessage(room, id);
|
||||
}
|
||||
|
||||
goRoom = (item) => {
|
||||
const { navigation } = this.props;
|
||||
this.cancelSearch();
|
||||
|
@ -535,6 +542,7 @@ class RoomsListView extends React.Component {
|
|||
name: this.getRoomTitle(item),
|
||||
t: item.t,
|
||||
prid: item.prid,
|
||||
roomUserId: this.getUidDirectMessage(item),
|
||||
room: item
|
||||
});
|
||||
}
|
||||
|
@ -764,7 +772,8 @@ class RoomsListView extends React.Component {
|
|||
theme,
|
||||
split
|
||||
} = this.props;
|
||||
const id = item.rid.replace(userId, '').trim();
|
||||
const id = this.getUidDirectMessage(item);
|
||||
const isGroupChat = RocketChat.isGroupChat(item);
|
||||
|
||||
return (
|
||||
<RoomItem
|
||||
|
@ -797,6 +806,7 @@ class RoomsListView extends React.Component {
|
|||
hideChannel={this.hideChannel}
|
||||
useRealName={useRealName}
|
||||
getUserPresence={this.getUserPresence}
|
||||
isGroupChat={isGroupChat}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -7,14 +7,10 @@ import equal from 'deep-equal';
|
|||
import { orderBy } from 'lodash';
|
||||
import { Q } from '@nozbe/watermelondb';
|
||||
|
||||
import {
|
||||
addUser as addUserAction, removeUser as removeUserAction, reset as resetAction, setLoading as setLoadingAction
|
||||
} from '../actions/selectedUsers';
|
||||
import database from '../lib/database';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import UserItem from '../presentation/UserItem';
|
||||
import Loading from '../containers/Loading';
|
||||
import debounce from '../utils/debounce';
|
||||
import I18n from '../i18n';
|
||||
import log from '../utils/log';
|
||||
import SearchBox from '../containers/SearchBox';
|
||||
|
@ -26,6 +22,12 @@ import { animateNextTransition } from '../utils/layoutAnimation';
|
|||
import { withTheme } from '../theme';
|
||||
import { themedHeader } from '../utils/navigation';
|
||||
import { getUserSelector } from '../selectors/login';
|
||||
import {
|
||||
reset as resetAction,
|
||||
addUser as addUserAction,
|
||||
removeUser as removeUserAction
|
||||
} from '../actions/selectedUsers';
|
||||
import { showErrorAlert } from '../utils/info';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
safeAreaView: {
|
||||
|
@ -38,47 +40,55 @@ const styles = StyleSheet.create({
|
|||
|
||||
class SelectedUsersView extends React.Component {
|
||||
static navigationOptions = ({ navigation, screenProps }) => {
|
||||
const title = navigation.getParam('title');
|
||||
const title = navigation.getParam('title', I18n.t('Select_Users'));
|
||||
const buttonText = navigation.getParam('buttonText', I18n.t('Next'));
|
||||
const showButton = navigation.getParam('showButton', false);
|
||||
const maxUsers = navigation.getParam('maxUsers');
|
||||
const nextAction = navigation.getParam('nextAction', () => {});
|
||||
return {
|
||||
...themedHeader(screenProps.theme),
|
||||
title,
|
||||
headerRight: (
|
||||
<CustomHeaderButtons>
|
||||
<Item title={I18n.t('Next')} onPress={nextAction} testID='selected-users-view-submit' />
|
||||
</CustomHeaderButtons>
|
||||
(!maxUsers || showButton) && (
|
||||
<CustomHeaderButtons>
|
||||
<Item title={buttonText} onPress={nextAction} testID='selected-users-view-submit' />
|
||||
</CustomHeaderButtons>
|
||||
)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
navigation: PropTypes.object,
|
||||
baseUrl: PropTypes.string,
|
||||
addUser: PropTypes.func.isRequired,
|
||||
removeUser: PropTypes.func.isRequired,
|
||||
reset: PropTypes.func.isRequired,
|
||||
users: PropTypes.array,
|
||||
loading: PropTypes.bool,
|
||||
setLoadingInvite: PropTypes.func,
|
||||
user: PropTypes.shape({
|
||||
id: PropTypes.string,
|
||||
token: PropTypes.string
|
||||
token: PropTypes.string,
|
||||
username: PropTypes.string,
|
||||
name: PropTypes.string
|
||||
}),
|
||||
navigation: PropTypes.object,
|
||||
theme: PropTypes.string
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.init();
|
||||
|
||||
const maxUsers = props.navigation.getParam('maxUsers');
|
||||
this.state = {
|
||||
maxUsers,
|
||||
search: [],
|
||||
chats: []
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { navigation } = this.props;
|
||||
navigation.setParams({ nextAction: this.nextAction });
|
||||
const { user } = this.props;
|
||||
if (this.isGroupChat()) {
|
||||
props.addUser({ _id: user.id, name: user.username, fname: user.name });
|
||||
}
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
|
@ -102,6 +112,19 @@ class SelectedUsersView extends React.Component {
|
|||
return false;
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.isGroupChat()) {
|
||||
const { users, navigation } = this.props;
|
||||
if (prevProps.users.length !== users.length) {
|
||||
if (users.length) {
|
||||
navigation.setParams({ showButton: true });
|
||||
} else {
|
||||
navigation.setParams({ showButton: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const { reset } = this.props;
|
||||
reset();
|
||||
|
@ -132,30 +155,6 @@ class SelectedUsersView extends React.Component {
|
|||
this.search(text);
|
||||
}
|
||||
|
||||
nextAction = async() => {
|
||||
const { navigation, setLoadingInvite } = this.props;
|
||||
const nextActionID = navigation.getParam('nextActionID');
|
||||
if (nextActionID === 'CREATE_CHANNEL') {
|
||||
navigation.navigate('CreateChannelView');
|
||||
} else {
|
||||
const rid = navigation.getParam('rid');
|
||||
try {
|
||||
setLoadingInvite(true);
|
||||
await RocketChat.addUsersToRoom(rid);
|
||||
navigation.pop();
|
||||
} catch (e) {
|
||||
log(e);
|
||||
} finally {
|
||||
setLoadingInvite(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/sort-comp
|
||||
updateState = debounce(() => {
|
||||
this.forceUpdate();
|
||||
}, 1000);
|
||||
|
||||
search = async(text) => {
|
||||
const result = await RocketChat.search({ text, filterRooms: false });
|
||||
this.setState({
|
||||
|
@ -163,16 +162,33 @@ class SelectedUsersView extends React.Component {
|
|||
});
|
||||
}
|
||||
|
||||
isGroupChat = () => {
|
||||
const { maxUsers } = this.state;
|
||||
return maxUsers > 2;
|
||||
}
|
||||
|
||||
isChecked = (username) => {
|
||||
const { users } = this.props;
|
||||
return users.findIndex(el => el.name === username) !== -1;
|
||||
}
|
||||
|
||||
toggleUser = (user) => {
|
||||
const { addUser, removeUser } = this.props;
|
||||
const { maxUsers } = this.state;
|
||||
const {
|
||||
addUser, removeUser, users, user: { username }
|
||||
} = this.props;
|
||||
|
||||
// Disallow removing self user from the direct message group
|
||||
if (this.isGroupChat() && username === user.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
animateNextTransition();
|
||||
if (!this.isChecked(user.name)) {
|
||||
if (this.isGroupChat() && users.length === maxUsers) {
|
||||
return showErrorAlert(I18n.t('Max_number_of_users_allowed_is_number', { maxUsers }), I18n.t('Oops'));
|
||||
}
|
||||
|
||||
addUser(user);
|
||||
} else {
|
||||
removeUser(user);
|
||||
|
@ -274,9 +290,14 @@ class SelectedUsersView extends React.Component {
|
|||
renderList = () => {
|
||||
const { search, chats } = this.state;
|
||||
const { theme } = this.props;
|
||||
|
||||
const data = (search.length > 0 ? search : chats)
|
||||
// filter DM between multiple users
|
||||
.filter(sub => !RocketChat.isGroupChat(sub));
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
data={search.length > 0 ? search : chats}
|
||||
data={data}
|
||||
extraData={this.props}
|
||||
keyExtractor={item => item._id}
|
||||
renderItem={this.renderItem}
|
||||
|
@ -315,8 +336,7 @@ const mapStateToProps = state => ({
|
|||
const mapDispatchToProps = dispatch => ({
|
||||
addUser: user => dispatch(addUserAction(user)),
|
||||
removeUser: user => dispatch(removeUserAction(user)),
|
||||
reset: () => dispatch(resetAction()),
|
||||
setLoadingInvite: loading => dispatch(setLoadingAction(loading))
|
||||
reset: () => dispatch(resetAction())
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(SelectedUsersView));
|
||||
|
|
Loading…
Reference in New Issue