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