diff --git a/android/app/src/main/res/drawable-hdpi/plus.png b/android/app/src/main/res/drawable-hdpi/plus.png
new file mode 100644
index 00000000..238a8cc9
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/plus.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/textinput_search.png b/android/app/src/main/res/drawable-hdpi/textinput_search.png
new file mode 100644
index 00000000..274376fd
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/textinput_search.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/plus.png b/android/app/src/main/res/drawable-mdpi/plus.png
new file mode 100644
index 00000000..cce62241
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/plus.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/textinput_search.png b/android/app/src/main/res/drawable-mdpi/textinput_search.png
new file mode 100644
index 00000000..4eefb38c
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/textinput_search.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/plus.png b/android/app/src/main/res/drawable-xhdpi/plus.png
new file mode 100644
index 00000000..251c8d18
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/plus.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/textinput_search.png b/android/app/src/main/res/drawable-xhdpi/textinput_search.png
new file mode 100644
index 00000000..a8d16731
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/textinput_search.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/plus.png b/android/app/src/main/res/drawable-xxhdpi/plus.png
new file mode 100644
index 00000000..71db08c7
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/plus.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/textinput_search.png b/android/app/src/main/res/drawable-xxhdpi/textinput_search.png
new file mode 100644
index 00000000..1c9db2d0
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/textinput_search.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/plus.png b/android/app/src/main/res/drawable-xxxhdpi/plus.png
new file mode 100644
index 00000000..320adb8b
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/plus.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/textinput_search.png b/android/app/src/main/res/drawable-xxxhdpi/textinput_search.png
new file mode 100644
index 00000000..363ff5db
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/textinput_search.png differ
diff --git a/app/actions/actionsTypes.js b/app/actions/actionsTypes.js
index 49bd9cea..5b8f16c9 100644
--- a/app/actions/actionsTypes.js
+++ b/app/actions/actionsTypes.js
@@ -84,7 +84,9 @@ export const NAVIGATION = createRequestTypes('NAVIGATION', ['SET']);
export const SERVER = createRequestTypes('SERVER', [
...defaultTypes,
'SELECT_SUCCESS',
- 'SELECT_REQUEST'
+ 'SELECT_REQUEST',
+ 'INIT_ADD',
+ 'FINISH_ADD'
]);
export const METEOR = createRequestTypes('METEOR_CONNECT', [...defaultTypes, 'DISCONNECT', 'DISCONNECT_BY_USER']);
export const LOGOUT = 'LOGOUT'; // logout is always success
diff --git a/app/actions/server.js b/app/actions/server.js
index 51d1baff..a6bab715 100644
--- a/app/actions/server.js
+++ b/app/actions/server.js
@@ -33,3 +33,15 @@ export function serverFailure(err) {
err
};
}
+
+export function serverInitAdd() {
+ return {
+ type: SERVER.INIT_ADD
+ };
+}
+
+export function serverFinishAdd() {
+ return {
+ type: SERVER.FINISH_ADD
+ };
+}
diff --git a/app/constants/colors.js b/app/constants/colors.js
index 47c587eb..5a603742 100644
--- a/app/constants/colors.js
+++ b/app/constants/colors.js
@@ -1,8 +1,8 @@
export const AVATAR_COLORS = ['#F44336', '#E91E63', '#9C27B0', '#673AB7', '#3F51B5', '#2196F3', '#03A9F4', '#00BCD4', '#009688', '#4CAF50', '#8BC34A', '#CDDC39', '#FFC107', '#FF9800', '#FF5722', '#795548', '#9E9E9E', '#607D8B'];
-export const ESLINT_FIX = null;
export const COLOR_DANGER = '#f5455c';
export const COLOR_BUTTON_PRIMARY = '#2D6AEA';
export const COLOR_TEXT = '#292E35';
+export const COLOR_SEPARATOR = '#CBCED1';
export const STATUS_COLORS = {
online: '#2de0a5',
busy: COLOR_DANGER,
diff --git a/app/containers/SearchBox.js b/app/containers/SearchBox.js
new file mode 100644
index 00000000..5135ba19
--- /dev/null
+++ b/app/containers/SearchBox.js
@@ -0,0 +1,62 @@
+import React from 'react';
+import { View, StyleSheet, Image, TextInput, Platform } from 'react-native';
+import PropTypes from 'prop-types';
+
+import I18n from '../i18n';
+
+const styles = StyleSheet.create({
+ container: {
+ backgroundColor: Platform.OS === 'ios' ? '#F7F8FA' : '#54585E'
+ },
+ searchBox: {
+ alignItems: 'center',
+ backgroundColor: '#E1E5E8',
+ borderRadius: 10,
+ color: '#8E8E93',
+ flexDirection: 'row',
+ fontSize: 17,
+ height: 36,
+ margin: 16,
+ marginVertical: 10,
+ paddingHorizontal: 10
+ },
+ icon: {
+ width: 14,
+ height: 14
+ },
+ input: {
+ color: '#8E8E93',
+ flex: 1,
+ fontSize: 17,
+ marginLeft: 8,
+ paddingTop: 0,
+ paddingBottom: 0
+ }
+});
+
+const SearchBox = ({ onChangeText, testID }) => (
+
+
+
+
+
+
+);
+
+SearchBox.propTypes = {
+ onChangeText: PropTypes.func.isRequired,
+ testID: PropTypes.string
+};
+
+export default SearchBox;
diff --git a/app/containers/Sidebar.js b/app/containers/Sidebar.js
index 0cbf8aa3..382939ae 100644
--- a/app/containers/Sidebar.js
+++ b/app/containers/Sidebar.js
@@ -236,6 +236,7 @@ export default class Sidebar extends Component {
setTimeout(() => {
NavigationActions.push({
screen: 'NewServerView',
+ backButtonTitle: '',
passProps: {
server: item.id
},
diff --git a/app/i18n/locales/en.js b/app/i18n/locales/en.js
index 44261479..8af6fa33 100644
--- a/app/i18n/locales/en.js
+++ b/app/i18n/locales/en.js
@@ -1,6 +1,7 @@
export default {
'1_online_member': '1 online member',
'1_person_reacted': '1 person reacted',
+ '1_user': '1 user',
'error-action-not-allowed': '{{action}} is not allowed',
'error-application-not-found': 'Application not found',
'error-archived-duplicate-name': 'There\'s an archived channel with name {{room_name}}',
@@ -110,6 +111,7 @@ export default {
Cancel_recording: 'Cancel recording',
Cancel: 'Cancel',
changing_avatar: 'changing avatar',
+ creating_channel: 'creating channel',
Channel_Name: 'Channel Name',
Channels: 'Channels',
Chats: 'Chats',
@@ -160,6 +162,7 @@ export default {
Has_left_the_channel: 'Has left the channel',
I_have_an_account: 'I have an account',
Invisible: 'Invisible',
+ Invite: 'Invite',
is_a_valid_RocketChat_instance: 'is a valid Rocket.Chat instance',
is_not_a_valid_RocketChat_instance: 'is not a valid Rocket.Chat instance',
is_typing: 'is typing',
@@ -188,13 +191,15 @@ export default {
muted: 'muted',
My_servers: 'My servers',
N_online_members: '{{n}} online members',
- N_person_reacted: '{{n}} people reacted',
+ N_people_reacted: '{{n}} people reacted',
+ N_users: '{{n}} users',
name: 'name',
Name: 'Name',
New_in_RocketChat_question_mark: 'New in Rocket.Chat?',
New_Message: 'New Message',
New_Password: 'New Password',
New_Server: 'New Server',
+ Next: 'Next',
No_files: 'No files',
No_mentioned_messages: 'No mentioned messages',
No_pinned_messages: 'No pinned messages',
diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js
index ed2b0289..711d5d99 100644
--- a/app/lib/rocketchat.js
+++ b/app/lib/rocketchat.js
@@ -528,6 +528,56 @@ const RocketChat = {
return _sendMessageCall(JSON.parse(JSON.stringify(message)));
},
+ async search({ text, filterUsers = true, filterRooms = true }) {
+ const searchText = text.trim();
+ if (searchText === '') {
+ delete this.oldPromise;
+ return [];
+ }
+
+ let data = database.objects('subscriptions').filtered('name CONTAINS[c] $0', searchText);
+
+ if (filterUsers && !filterRooms) {
+ data = data.filtered('t = $0', 'd');
+ } else if (!filterUsers && filterRooms) {
+ data = data.filtered('t != $0', 'd');
+ }
+ data = data.slice(0, 7);
+
+ const usernames = data.map(sub => sub.name);
+ try {
+ if (data.length < 7) {
+ if (this.oldPromise) {
+ this.oldPromise('cancel');
+ }
+
+ const { users, rooms } = await Promise.race([
+ RocketChat.spotlight(searchText, usernames, { users: filterUsers, rooms: filterRooms }),
+ new Promise((resolve, reject) => this.oldPromise = reject)
+ ]);
+
+ data = data.concat(users.map(user => ({
+ ...user,
+ rid: user.username,
+ name: user.username,
+ t: 'd',
+ search: true
+ })), rooms.map(room => ({
+ rid: room._id,
+ ...room,
+ search: true
+ })));
+
+ delete this.oldPromise;
+ }
+
+ return data;
+ } catch (e) {
+ console.warn(e);
+ return [];
+ }
+ },
+
spotlight(search, usernames, type) {
return call('spotlight', search, usernames, type);
},
diff --git a/app/presentation/UserItem.js b/app/presentation/UserItem.js
index cf8c07de..f4e03dbd 100644
--- a/app/presentation/UserItem.js
+++ b/app/presentation/UserItem.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { Text, View, StyleSheet, Platform } from 'react-native';
+import { Text, View, StyleSheet, Platform, ViewPropTypes, Image } from 'react-native';
import PropTypes from 'prop-types';
import Avatar from '../containers/Avatar';
@@ -7,7 +7,8 @@ import Touch from '../utils/touch';
const styles = StyleSheet.create({
button: {
- height: 54
+ height: 54,
+ backgroundColor: '#fff'
},
container: {
flexDirection: 'row'
@@ -24,24 +25,33 @@ const styles = StyleSheet.create({
fontSize: 18,
color: '#0C0D0F',
marginTop: Platform.OS === 'ios' ? 6 : 3,
- marginBottom: 1
+ marginBottom: 1,
+ textAlign: 'left'
},
username: {
fontSize: 14,
color: '#9EA2A8'
+ },
+ icon: {
+ width: 20,
+ height: 20,
+ marginHorizontal: 15,
+ resizeMode: 'contain',
+ alignSelf: 'center'
}
});
const UserItem = ({
- name, username, onPress, testID, onLongPress
+ name, username, onPress, testID, onLongPress, style, icon
}) => (
-
+
{name}
@{username}
+ {icon ? : null}
);
@@ -51,7 +61,9 @@ UserItem.propTypes = {
username: PropTypes.string.isRequired,
onPress: PropTypes.func.isRequired,
testID: PropTypes.string.isRequired,
- onLongPress: PropTypes.func
+ onLongPress: PropTypes.func,
+ style: ViewPropTypes.style,
+ icon: PropTypes.string
};
export default UserItem;
diff --git a/app/reducers/server.js b/app/reducers/server.js
index db6d5911..a423033e 100644
--- a/app/reducers/server.js
+++ b/app/reducers/server.js
@@ -6,7 +6,7 @@ const initialState = {
failure: false,
server: '',
loading: true,
- adding: true
+ adding: false
};
@@ -16,16 +16,14 @@ export default function server(state = initialState, action) {
return {
...state,
connecting: true,
- failure: false,
- adding: true
+ failure: false
};
case SERVER.FAILURE:
return {
...state,
connecting: false,
connected: false,
- failure: true,
- adding: false
+ failure: true
};
case SERVER.SELECT_REQUEST:
return {
@@ -43,6 +41,16 @@ export default function server(state = initialState, action) {
connected: true,
loading: false
};
+ case SERVER.INIT_ADD:
+ return {
+ ...state,
+ adding: true
+ };
+ case SERVER.FINISH_ADD:
+ return {
+ ...state,
+ adding: false
+ };
default:
return state;
}
diff --git a/app/sagas/createChannel.js b/app/sagas/createChannel.js
index 9f64e3b4..bd5ff2d9 100644
--- a/app/sagas/createChannel.js
+++ b/app/sagas/createChannel.js
@@ -12,25 +12,26 @@ const create = function* create(data) {
const handleRequest = function* handleRequest({ data }) {
try {
- // yield delay(1000);
const auth = yield select(state => state.login.isAuthenticated);
if (!auth) {
yield take(LOGIN.SUCCESS);
}
const result = yield call(create, data);
+ yield put(createChannelSuccess(result));
+ yield delay(300);
const { rid, name } = result;
- NavigationActions.popToRoot();
- yield delay(1000);
+ NavigationActions.dismissModal();
+ yield delay(600);
NavigationActions.push({
screen: 'RoomView',
title: name,
+ backButtonTitle: '',
passProps: {
room: { rid, name },
rid,
name
}
});
- yield put(createChannelSuccess(result));
} catch (err) {
yield put(createChannelFailure(err));
}
diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js
index ce21d94b..8f3bf1d6 100644
--- a/app/sagas/deepLinking.js
+++ b/app/sagas/deepLinking.js
@@ -17,6 +17,7 @@ const navigate = function* go({ params, sameServer = true }) {
if (canOpenRoom) {
return NavigationActions.push({
screen: 'RoomView',
+ backButtonTitle: '',
passProps: {
rid: params.rid
}
diff --git a/app/sagas/login.js b/app/sagas/login.js
index 12f1b01e..93c1ce86 100644
--- a/app/sagas/login.js
+++ b/app/sagas/login.js
@@ -4,6 +4,7 @@ import { put, call, take, takeLatest, select, all } from 'redux-saga/effects';
import * as types from '../actions/actionsTypes';
import { appStart } from '../actions';
+import { serverFinishAdd } from '../actions/server';
import {
// loginRequest,
// loginSubmit,
@@ -38,13 +39,18 @@ const forgotPasswordCall = args => RocketChat.forgotPassword(args);
const handleLoginSuccess = function* handleLoginSuccess() {
try {
const user = yield select(getUser);
+ const adding = yield select(state => state.server.adding);
yield AsyncStorage.setItem(RocketChat.TOKEN_KEY, user.token);
if (!user.username || user.isRegistering) {
yield put(registerIncomplete());
} else {
yield delay(300);
- NavigationActions.dismissModal();
- yield put(appStart('inside'));
+ if (adding) {
+ NavigationActions.dismissModal();
+ } else {
+ yield put(appStart('inside'));
+ }
+ yield put(serverFinishAdd());
}
} catch (e) {
log('handleLoginSuccess', e);
diff --git a/app/sagas/messages.js b/app/sagas/messages.js
index f959fd18..556b66a7 100644
--- a/app/sagas/messages.js
+++ b/app/sagas/messages.js
@@ -80,6 +80,7 @@ const goRoom = function* goRoom({ rid, name }) {
yield delay(1000);
NavigationActions.push({
screen: 'RoomView',
+ backButtonTitle: '',
passProps: {
room: { rid, name },
rid,
diff --git a/app/sagas/selectServer.js b/app/sagas/selectServer.js
index 9a1e4f24..be132c44 100644
--- a/app/sagas/selectServer.js
+++ b/app/sagas/selectServer.js
@@ -44,7 +44,7 @@ const handleSelectServer = function* handleSelectServer({ server }) {
const handleServerRequest = function* handleServerRequest({ server }) {
try {
yield call(validate, server);
- yield call(NavigationActions.push, { screen: 'LoginSignupView', title: server });
+ yield call(NavigationActions.push, { screen: 'LoginSignupView', title: server, backButtonTitle: '' });
database.databases.serversDB.write(() => {
database.databases.serversDB.create('servers', { id: server, current: false }, true);
});
diff --git a/app/views/CreateChannelView.js b/app/views/CreateChannelView.js
index 9f9a62ca..5ce8e870 100644
--- a/app/views/CreateChannelView.js
+++ b/app/views/CreateChannelView.js
@@ -1,29 +1,85 @@
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
-import { View, Text, Switch, SafeAreaView, ScrollView, Platform } from 'react-native';
+import { View, Text, Switch, SafeAreaView, ScrollView, TextInput, StyleSheet, FlatList, Platform } from 'react-native';
-import RCTextInput from '../containers/TextInput';
import Loading from '../containers/Loading';
import LoggedView from './View';
import { createChannelRequest } from '../actions/createChannel';
-import styles from './Styles';
+import { removeUser } from '../actions/selectedUsers';
+import sharedStyles from './Styles';
import KeyboardView from '../presentation/KeyboardView';
import scrollPersistTaps from '../utils/scrollPersistTaps';
-import Button from '../containers/Button';
import I18n from '../i18n';
+import UserItem from '../presentation/UserItem';
+import { showErrorAlert } from '../utils/info';
+
+const styles = StyleSheet.create({
+ container: {
+ backgroundColor: '#f7f8fa'
+ },
+ list: {
+ width: '100%',
+ backgroundColor: '#FFFFFF'
+ },
+ separator: {
+ marginLeft: 60
+ },
+ formSeparator: {
+ marginLeft: 15
+ },
+ input: {
+ height: 54,
+ paddingHorizontal: 18,
+ color: '#9EA2A8',
+ backgroundColor: '#fff',
+ fontSize: 18
+ },
+ swithContainer: {
+ height: 54,
+ backgroundColor: '#fff',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ flexDirection: 'row',
+ paddingHorizontal: 18
+ },
+ label: {
+ color: '#0C0D0F',
+ fontSize: 18,
+ fontWeight: '500'
+ },
+ invitedHeader: {
+ marginTop: 18,
+ marginHorizontal: 15,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center'
+ },
+ invitedTitle: {
+ color: '#2F343D',
+ fontSize: 22,
+ fontWeight: 'bold',
+ lineHeight: 41
+ },
+ invitedCount: {
+ color: '#9EA2A8',
+ fontSize: 15
+ }
+});
@connect(state => ({
createChannel: state.createChannel,
users: state.selectedUsers.users
}), dispatch => ({
- create: data => dispatch(createChannelRequest(data))
+ create: data => dispatch(createChannelRequest(data)),
+ removeUser: user => dispatch(removeUser(user))
}))
/** @extends React.Component */
export default class CreateChannelView extends LoggedView {
static propTypes = {
navigator: PropTypes.object,
create: PropTypes.func.isRequired,
+ removeUser: PropTypes.func.isRequired,
createChannel: PropTypes.object.isRequired,
users: PropTypes.array.isRequired
};
@@ -36,6 +92,7 @@ export default class CreateChannelView extends LoggedView {
readOnly: false,
broadcast: false
};
+ props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
}
componentDidMount() {
@@ -44,6 +101,36 @@ export default class CreateChannelView extends LoggedView {
}, 600);
}
+ componentDidUpdate(prevProps) {
+ if (this.props.createChannel.error && prevProps.createChannel.error !== this.props.createChannel.error) {
+ setTimeout(() => {
+ const msg = this.props.createChannel.error.reason || I18n.t('There_was_an_error_while_action', { action: I18n.t('creating_channel') });
+ showErrorAlert(msg);
+ }, 300);
+ }
+ }
+
+ onChangeText = (channelName) => {
+ const rightButtons = [];
+ if (channelName.trim().length > 0) {
+ rightButtons.push({
+ id: 'create',
+ title: 'Create',
+ testID: 'create-channel-submit'
+ });
+ }
+ this.props.navigator.setButtons({ rightButtons });
+ this.setState({ channelName });
+ }
+
+ async onNavigatorEvent(event) {
+ if (event.type === 'NavBarButtonPress') {
+ if (event.id === 'create') {
+ this.submit();
+ }
+ }
+ }
+
submit = () => {
if (!this.state.channelName.trim() || this.props.createChannel.isFetching) {
return;
@@ -62,47 +149,35 @@ export default class CreateChannelView extends LoggedView {
});
}
- renderChannelNameError() {
- if (
- !this.props.createChannel.failure ||
- this.props.createChannel.error.error !== 'error-duplicate-channel-name'
- ) {
- return null;
+ removeUser = (user) => {
+ if (this.props.users.length === 1) {
+ return;
}
-
- return (
-
- {this.props.createChannel.error.reason}
-
- );
+ this.props.removeUser(user);
}
renderSwitch = ({
- id, value, label, description, onValueChange, disabled = false
+ id, value, label, onValueChange, disabled = false
}) => (
-
-
-
- {label}
-
- {description}
+
+ {I18n.t(label)}
+
- );
+ )
renderType() {
const { type } = this.state;
return this.renderSwitch({
id: 'type',
value: type,
- label: type ? I18n.t('Private_Channel') : I18n.t('Public_Channel'),
- description: type ? I18n.t('Just_invited_people_can_access_this_channel') : I18n.t('Everyone_can_access_this_channel'),
+ label: 'Private_Channel',
onValueChange: value => this.setState({ type: value })
});
}
@@ -112,8 +187,7 @@ export default class CreateChannelView extends LoggedView {
return this.renderSwitch({
id: 'readonly',
value: readOnly,
- label: I18n.t('Read_Only_Channel'),
- description: readOnly ? I18n.t('Only_authorized_users_can_write_new_messages') : I18n.t('All_users_in_the_channel_can_write_new_messages'),
+ label: 'Read_Only_Channel',
onValueChange: value => this.setState({ readOnly: value }),
disabled: broadcast
});
@@ -124,8 +198,7 @@ export default class CreateChannelView extends LoggedView {
return this.renderSwitch({
id: 'broadcast',
value: broadcast,
- label: I18n.t('Broadcast_Channel'),
- description: I18n.t('Broadcast_channel_Description'),
+ label: 'Broadcast_Channel',
onValueChange: (value) => {
this.setState({
broadcast: value,
@@ -135,39 +208,70 @@ export default class CreateChannelView extends LoggedView {
});
}
+ renderSeparator = () =>
+
+ renderFormSeparator = () =>
+
+ renderItem = ({ item }) => (
+ this.removeUser(item)}
+ testID={`create-channel-view-item-${ item.name }`}
+ />
+ )
+
+ renderInvitedList = () => (
+ item._id}
+ style={[styles.list, sharedStyles.separatorVertical]}
+ renderItem={this.renderItem}
+ ItemSeparatorComponent={this.renderSeparator}
+ enableEmptySections
+ keyboardShouldPersistTaps='always'
+ />
+ )
+
render() {
+ const userCount = this.props.users.length;
return (
-
-
- this.channelNameRef = ref}
- label={I18n.t('Channel_Name')}
- value={this.state.channelName}
- onChangeText={channelName => this.setState({ channelName })}
- placeholder={I18n.t('Type_the_channel_name_here')}
- returnKeyType='done'
- testID='create-channel-name'
- />
- {this.renderChannelNameError()}
- {this.renderType()}
- {this.renderReadOnly()}
- {this.renderBroadcast()}
-
-
-
+
+
);
}
diff --git a/app/views/LoginSignupView.js b/app/views/LoginSignupView.js
index 9560021f..a9e86e17 100644
--- a/app/views/LoginSignupView.js
+++ b/app/views/LoginSignupView.js
@@ -182,7 +182,7 @@ export default class LoginSignupView extends LoggedView {
this.props.navigator.push({
screen: 'LoginView',
title: this.props.server,
- backButtonTitle: I18n.t('Welcome')
+ backButtonTitle: ''
});
}
@@ -190,7 +190,7 @@ export default class LoginSignupView extends LoggedView {
this.props.navigator.push({
screen: 'RegisterView',
title: this.props.server,
- backButtonTitle: I18n.t('Welcome')
+ backButtonTitle: ''
});
}
diff --git a/app/views/LoginView.js b/app/views/LoginView.js
index 0050d559..aa8c6110 100644
--- a/app/views/LoginView.js
+++ b/app/views/LoginView.js
@@ -68,7 +68,7 @@ export default class LoginView extends LoggedView {
this.props.navigator.push({
screen: 'RegisterView',
title: this.props.server,
- backButtonTitle: I18n.t('Login')
+ backButtonTitle: ''
});
}
@@ -76,7 +76,7 @@ export default class LoginView extends LoggedView {
this.props.navigator.push({
screen: 'ForgotPasswordView',
title: I18n.t('Forgot_Password'),
- backButtonTitle: I18n.t('Login')
+ backButtonTitle: ''
});
}
diff --git a/app/views/NewMessageView.js b/app/views/NewMessageView.js
new file mode 100644
index 00000000..13ac5864
--- /dev/null
+++ b/app/views/NewMessageView.js
@@ -0,0 +1,166 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { View, StyleSheet, SafeAreaView, FlatList, Text, Platform, Image } from 'react-native';
+
+import database from '../lib/realm';
+import RocketChat from '../lib/rocketchat';
+import UserItem from '../presentation/UserItem';
+import debounce from '../utils/debounce';
+import LoggedView from './View';
+import sharedStyles from './Styles';
+import I18n from '../i18n';
+import Touch from '../utils/touch';
+import SearchBox from '../containers/SearchBox';
+
+const styles = StyleSheet.create({
+ safeAreaView: {
+ flex: 1,
+ backgroundColor: Platform.OS === 'ios' ? '#F7F8FA' : '#E1E5E8'
+ },
+ separator: {
+ marginLeft: 60
+ },
+ createChannelButton: {
+ marginVertical: 25
+ },
+ createChannelContainer: {
+ height: 47,
+ backgroundColor: '#fff',
+ flexDirection: 'row',
+ alignItems: 'center'
+ },
+ createChannelIcon: {
+ width: 24,
+ height: 24,
+ marginHorizontal: 18
+ },
+ createChannelText: {
+ color: '#1D74F5',
+ fontSize: 18
+ }
+});
+
+/** @extends React.Component */
+export default class SelectedUsersView extends LoggedView {
+ static navigatorButtons = {
+ leftButtons: [{
+ id: 'cancel',
+ title: I18n.t('Cancel')
+ }]
+ }
+
+ static propTypes = {
+ navigator: PropTypes.object,
+ onPressItem: PropTypes.func.isRequired
+ };
+
+ constructor(props) {
+ super('NewMessageView', props);
+ this.data = database.objects('subscriptions').filtered('t = $0', 'd').sorted('roomUpdatedAt', true);
+ this.state = {
+ search: []
+ };
+ this.data.addListener(this.updateState);
+ props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
+ }
+
+ componentWillUnmount() {
+ this.updateState.stop();
+ this.data.removeAllListeners();
+ }
+
+ async onNavigatorEvent(event) {
+ if (event.type === 'NavBarButtonPress') {
+ if (event.id === 'cancel') {
+ this.props.navigator.dismissModal();
+ }
+ }
+ }
+
+ onSearchChangeText(text) {
+ this.search(text);
+ }
+
+ onPressItem = (item) => {
+ this.props.navigator.dismissModal();
+ setTimeout(() => {
+ this.props.onPressItem(item);
+ }, 600);
+ }
+
+ updateState = debounce(() => {
+ this.forceUpdate();
+ }, 1000);
+
+ search = async(text) => {
+ const result = await RocketChat.search({ text, filterRooms: false });
+ this.setState({
+ search: result
+ });
+ }
+
+ createChannel = () => {
+ this.props.navigator.push({
+ screen: 'SelectedUsersView',
+ title: I18n.t('Select_Users'),
+ backButtonTitle: '',
+ passProps: {
+ nextAction: 'CREATE_CHANNEL'
+ }
+ });
+ }
+
+ renderHeader = () => (
+
+ this.onSearchChangeText(text)} testID='new-message-view-search' />
+
+
+
+ {I18n.t('Create_Channel')}
+
+
+
+ )
+
+ renderSeparator = () => ;
+
+ renderItem = ({ item, index }) => {
+ let style = {};
+ if (index === 0) {
+ style = { ...sharedStyles.separatorTop };
+ }
+ if (this.state.search.length > 0 && index === this.state.search.length - 1) {
+ style = { ...style, ...sharedStyles.separatorBottom };
+ }
+ if (this.state.search.length === 0 && index === this.data.length - 1) {
+ style = { ...style, ...sharedStyles.separatorBottom };
+ }
+ return (
+ this.onPressItem(item)}
+ testID={`new-message-view-item-${ item.name }`}
+ style={style}
+ />
+ );
+ }
+
+ renderList = () => (
+ 0 ? this.state.search : this.data}
+ extraData={this.state}
+ keyExtractor={item => item._id}
+ ListHeaderComponent={this.renderHeader}
+ renderItem={this.renderItem}
+ ItemSeparatorComponent={this.renderSeparator}
+ keyboardShouldPersistTaps='always'
+ />
+ )
+
+ render = () => (
+
+ {this.renderList()}
+
+ );
+}
diff --git a/app/views/NewServerView.js b/app/views/NewServerView.js
index 66e14a7b..cbff4379 100644
--- a/app/views/NewServerView.js
+++ b/app/views/NewServerView.js
@@ -1,9 +1,9 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { Text, ScrollView, Keyboard, SafeAreaView, Image, Alert, StyleSheet, Platform } from 'react-native';
+import { Text, ScrollView, Keyboard, SafeAreaView, Image, Alert, StyleSheet } from 'react-native';
import { connect } from 'react-redux';
-import { serverRequest } from '../actions/server';
+import { serverRequest, selectServerRequest, serverInitAdd, serverFinishAdd } from '../actions/server';
import sharedStyles from './Styles';
import scrollPersistTaps from '../utils/scrollPersistTaps';
import Button from '../containers/Button';
@@ -12,7 +12,6 @@ import LoggedView from './View';
import I18n from '../i18n';
import { scale, verticalScale, moderateScale } from '../utils/scaling';
import KeyboardView from '../presentation/KeyboardView';
-import { iconsMap } from '../Icons';
const styles = StyleSheet.create({
image: {
@@ -45,9 +44,13 @@ const defaultServer = 'https://open.rocket.chat';
@connect(state => ({
connecting: state.server.connecting,
failure: state.server.failure,
- currentServer: state.server.server
+ currentServer: state.server.server,
+ adding: state.server.adding
}), dispatch => ({
- connectServer: url => dispatch(serverRequest(url))
+ initAdd: () => dispatch(serverInitAdd()),
+ finishAdd: () => dispatch(serverFinishAdd()),
+ connectServer: server => dispatch(serverRequest(server)),
+ selectServer: server => dispatch(selectServerRequest(server))
}))
/** @extends React.Component */
export default class NewServerView extends LoggedView {
@@ -55,10 +58,14 @@ export default class NewServerView extends LoggedView {
navigator: PropTypes.object,
server: PropTypes.string,
connecting: PropTypes.bool.isRequired,
+ adding: PropTypes.bool,
failure: PropTypes.bool.isRequired,
connectServer: PropTypes.func.isRequired,
+ selectServer: PropTypes.func.isRequired,
previousServer: PropTypes.string,
- currentServer: PropTypes.string
+ currentServer: PropTypes.string,
+ initAdd: PropTypes.func,
+ finishAdd: PropTypes.func
}
constructor(props) {
@@ -69,25 +76,8 @@ export default class NewServerView extends LoggedView {
props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
}
- componentWillMount() {
- // if previousServer exists, New Server View is a modal
- if (this.props.previousServer) {
- const closeButton = {
- id: 'close',
- testID: 'new-server-close',
- title: I18n.t('Close')
- };
- if (Platform.OS === 'android') {
- closeButton.icon = iconsMap.close;
- }
- this.props.navigator.setButtons({
- leftButtons: [closeButton]
- });
- }
- }
-
componentDidMount() {
- const { server } = this.props;
+ const { server, previousServer } = this.props;
if (server) {
this.props.connectServer(server);
this.setState({ text: server });
@@ -96,6 +86,9 @@ export default class NewServerView extends LoggedView {
this.input.focus();
}, 600);
}
+ if (previousServer) {
+ this.props.initAdd();
+ }
}
componentWillReceiveProps(nextProps) {
@@ -104,16 +97,22 @@ export default class NewServerView extends LoggedView {
}
}
+ componentWillUnmount() {
+ const {
+ selectServer, previousServer, currentServer, adding, finishAdd
+ } = this.props;
+ if (adding) {
+ if (previousServer !== currentServer) {
+ selectServer(previousServer);
+ }
+ finishAdd();
+ }
+ }
+
onNavigatorEvent(event) {
if (event.type === 'NavBarButtonPress') {
- if (event.id === 'close') {
- const {
- navigator, connectServer, previousServer, currentServer
- } = this.props;
- navigator.dismissModal();
- if (previousServer !== currentServer) {
- connectServer(previousServer);
- }
+ if (event.id === 'cancel') {
+ this.props.navigator.dismissModal();
}
}
}
diff --git a/app/views/OAuthView.js b/app/views/OAuthView.js
index e2c73547..c054955e 100644
--- a/app/views/OAuthView.js
+++ b/app/views/OAuthView.js
@@ -13,7 +13,7 @@ const userAgent = Platform.OS === 'ios' ? 'UserAgent' : userAgentAndroid;
@connect(state => ({
server: state.server.server
}))
-export default class TermsServiceView extends React.PureComponent {
+export default class OAuthView extends React.PureComponent {
static navigatorButtons = {
leftButtons: [{
id: 'close',
diff --git a/app/views/OnboardingView/index.js b/app/views/OnboardingView/index.js
index 5d5ed15b..0a9501a6 100644
--- a/app/views/OnboardingView/index.js
+++ b/app/views/OnboardingView/index.js
@@ -21,6 +21,7 @@ export default class OnboardingView extends LoggedView {
connectServer = () => {
this.props.navigator.push({
screen: 'NewServerView',
+ backButtonTitle: '',
navigatorStyle: {
navBarHidden: true
}
@@ -30,6 +31,7 @@ export default class OnboardingView extends LoggedView {
joinCommunity = () => {
this.props.navigator.push({
screen: 'NewServerView',
+ backButtonTitle: '',
passProps: {
server: 'https://open.rocket.chat'
},
diff --git a/app/views/ProfileView/index.js b/app/views/ProfileView/index.js
index 0b2fb65e..94454f23 100644
--- a/app/views/ProfileView/index.js
+++ b/app/views/ProfileView/index.js
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { View, ScrollView, SafeAreaView, Keyboard, Platform, Dimensions } from 'react-native';
+import { View, ScrollView, SafeAreaView, Keyboard, Dimensions } from 'react-native';
import { connect } from 'react-redux';
import Dialog from 'react-native-dialog';
import SHA256 from 'js-sha256';
@@ -61,7 +61,7 @@ export default class ProfileView extends LoggedView {
componentWillMount() {
this.props.navigator.setButtons({
leftButtons: [{
- id: 'sideMenu',
+ id: 'settings',
icon: { uri: 'settings', scale: Dimensions.get('window').scale }
}]
});
@@ -91,7 +91,7 @@ export default class ProfileView extends LoggedView {
onNavigatorEvent(event) {
if (event.type === 'NavBarButtonPress') {
- if (event.id === 'sideMenu' && Platform.OS === 'ios') {
+ if (event.id === 'settings') {
this.props.navigator.toggleDrawer({
side: 'left'
});
diff --git a/app/views/RegisterView.js b/app/views/RegisterView.js
index ff7e65b0..e5e91483 100644
--- a/app/views/RegisterView.js
+++ b/app/views/RegisterView.js
@@ -93,7 +93,7 @@ export default class RegisterView extends LoggedView {
this.props.navigator.push({
screen: 'TermsServiceView',
title: I18n.t('Terms_of_Service'),
- backButtonTitle: I18n.t('Sign_Up')
+ backButtonTitle: ''
});
}
@@ -101,7 +101,7 @@ export default class RegisterView extends LoggedView {
this.props.navigator.push({
screen: 'PrivacyPolicyView',
title: I18n.t('Privacy_Policy'),
- backButtonTitle: I18n.t('Sign_Up')
+ backButtonTitle: ''
});
}
diff --git a/app/views/RoomActionsView/index.js b/app/views/RoomActionsView/index.js
index 93af3e2b..f17dc543 100644
--- a/app/views/RoomActionsView/index.js
+++ b/app/views/RoomActionsView/index.js
@@ -65,7 +65,8 @@ export default class RoomActionsView extends LoggedView {
this.props.navigator.push({
screen: item.route,
title: item.name,
- passProps: item.params
+ passProps: item.params,
+ backButtonTitle: ''
});
}
if (item.event) {
diff --git a/app/views/RoomInfoView/index.js b/app/views/RoomInfoView/index.js
index 69343c69..89a382a1 100644
--- a/app/views/RoomInfoView/index.js
+++ b/app/views/RoomInfoView/index.js
@@ -118,6 +118,7 @@ export default class RoomInfoView extends LoggedView {
this.props.navigator.push({
screen: 'RoomInfoEditView',
title: I18n.t('Room_Info_Edit'),
+ backButtonTitle: '',
passProps: {
rid: this.props.rid
}
diff --git a/app/views/RoomMembersView/index.js b/app/views/RoomMembersView/index.js
index 65e8a235..1e6427c8 100644
--- a/app/views/RoomMembersView/index.js
+++ b/app/views/RoomMembersView/index.js
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { FlatList, View, TextInput, Vibration, SafeAreaView } from 'react-native';
+import { FlatList, View, Vibration, SafeAreaView } from 'react-native';
import ActionSheet from 'react-native-actionsheet';
import LoggedView from '../View';
@@ -12,6 +12,7 @@ import database from '../../lib/realm';
import { showToast } from '../../utils/info';
import log from '../../utils/log';
import I18n from '../../i18n';
+import SearchBox from '../../containers/SearchBox';
/** @extends React.Component */
export default class RoomMembersView extends LoggedView {
@@ -132,6 +133,7 @@ export default class RoomMembersView extends LoggedView {
this.props.navigator.push({
screen: 'RoomView',
title: name,
+ backButtonTitle: '',
passProps: {
room: { rid, name },
rid,
@@ -162,20 +164,7 @@ export default class RoomMembersView extends LoggedView {
}
renderSearchBar = () => (
-
- this.onSearchChangeText(text)}
- returnKeyType='search'
- placeholder={I18n.t('Search')}
- clearButtonMode='while-editing'
- blurOnSubmit
- autoCorrect={false}
- autoCapitalize='none'
- testID='room-members-view-search'
- />
-
+ this.onSearchChangeText(text)} testID='room-members-view-search' />
)
renderSeparator = () => ;
diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js
index a754e573..b739e938 100644
--- a/app/views/RoomView/index.js
+++ b/app/views/RoomView/index.js
@@ -127,6 +127,7 @@ export default class RoomView extends LoggedView {
this.props.navigator.push({
screen: 'RoomActionsView',
title: I18n.t('Actions'),
+ backButtonTitle: '',
passProps: {
rid: this.state.room.rid
}
diff --git a/app/views/RoomsListView/ServerDropdown.js b/app/views/RoomsListView/ServerDropdown.js
index b399780e..92508ceb 100644
--- a/app/views/RoomsListView/ServerDropdown.js
+++ b/app/views/RoomsListView/ServerDropdown.js
@@ -86,6 +86,13 @@ export default class ServerDropdown extends Component {
title: I18n.t('Add_Server'),
passProps: {
previousServer: this.props.server
+ },
+ navigatorButtons: {
+ leftButtons: [{
+ id: 'cancel',
+ testID: 'new-server-close',
+ title: I18n.t('Close')
+ }]
}
});
}, ANIMATION_DURATION);
@@ -101,6 +108,7 @@ export default class ServerDropdown extends Component {
setTimeout(() => {
this.props.navigator.push({
screen: 'NewServerView',
+ backButtonTitle: '',
passProps: {
server
},
diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js
index f82dd681..bc11cacf 100644
--- a/app/views/RoomsListView/index.js
+++ b/app/views/RoomsListView/index.js
@@ -1,9 +1,10 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { Platform, View, TextInput, FlatList, BackHandler, ActivityIndicator, SafeAreaView, Text, Image, Dimensions, ScrollView, Keyboard } from 'react-native';
+import { Platform, View, FlatList, BackHandler, ActivityIndicator, SafeAreaView, Text, Image, Dimensions, ScrollView, Keyboard } from 'react-native';
import { connect } from 'react-redux';
import { isEqual } from 'lodash';
+import SearchBox from '../../containers/SearchBox';
import database from '../../lib/realm';
import RocketChat from '../../lib/rocketchat';
import RoomItem from '../../presentation/RoomItem';
@@ -26,7 +27,7 @@ const leftButtons = [{
testID: 'rooms-list-view-sidebar'
}];
const rightButtons = [{
- id: 'createChannel',
+ id: 'newMessage',
icon: { uri: 'new_channel', scale: Dimensions.get('window').scale },
testID: 'rooms-list-view-create-channel'
}];
@@ -38,7 +39,6 @@ if (Platform.OS === 'android') {
});
}
-
@connect((state) => {
let result = {
userId: state.login.user && state.login.user.id,
@@ -74,7 +74,6 @@ export default class RoomsListView extends LoggedView {
static navigatorStyle = {
navBarCustomView: 'RoomsListHeaderView',
- navBarComponentAlignment: 'fill',
navBarBackgroundColor: isAndroid() ? '#2F343D' : undefined,
navBarTextColor: isAndroid() ? '#FFF' : undefined,
navBarButtonColor: isAndroid() ? '#FFF' : undefined
@@ -157,6 +156,10 @@ export default class RoomsListView extends LoggedView {
this.removeListener(this.direct);
this.removeListener(this.livechat);
+ if (database && database.deleteAll) {
+ database.deleteAll();
+ }
+
if (this.timeout) {
clearTimeout(this.timeout);
}
@@ -165,12 +168,12 @@ export default class RoomsListView extends LoggedView {
onNavigatorEvent(event) {
const { navigator } = this.props;
if (event.type === 'NavBarButtonPress') {
- if (event.id === 'createChannel') {
- this.props.navigator.push({
- screen: 'SelectedUsersView',
- title: I18n.t('Select_Users'),
+ if (event.id === 'newMessage') {
+ this.props.navigator.showModal({
+ screen: 'NewMessageView',
+ title: I18n.t('New_Message'),
passProps: {
- nextAction: 'CREATE_CHANNEL'
+ onPressItem: this._onPressItem
}
});
} else if (event.id === 'settings') {
@@ -190,10 +193,6 @@ export default class RoomsListView extends LoggedView {
}
}
- onSearchChangeText(text) {
- this.search(text);
- }
-
getSubscriptions = () => {
if (this.props.server && this.hasActiveDB()) {
if (this.props.sidebarSortby === 'alphabetical') {
@@ -285,7 +284,6 @@ export default class RoomsListView extends LoggedView {
navigator.setButtons({ leftButtons, rightButtons });
navigator.setStyle({
navBarCustomView: 'RoomsListHeaderView',
- navBarComponentAlignment: 'fill',
navBarBackgroundColor: isAndroid() ? '#2F343D' : undefined,
navBarTextColor: isAndroid() ? '#FFF' : undefined,
navBarButtonColor: isAndroid() ? '#FFF' : undefined
@@ -327,55 +325,18 @@ export default class RoomsListView extends LoggedView {
_isUnread = item => item.unread > 0 || item.alert
- async search(text) {
- const searchText = text.trim();
- if (searchText === '') {
- delete this.oldPromise;
- return this.setState({
- search: []
- });
- }
-
- let data = database.objects('subscriptions').filtered('name CONTAINS[c] $0', searchText).slice(0, 7);
-
- const usernames = data.map(sub => sub.name);
- try {
- if (data.length < 7) {
- if (this.oldPromise) {
- this.oldPromise('cancel');
- }
-
- const { users, rooms } = await Promise.race([
- RocketChat.spotlight(searchText, usernames, { users: true, rooms: true }),
- new Promise((resolve, reject) => this.oldPromise = reject)
- ]);
-
- data = data.concat(users.map(user => ({
- ...user,
- rid: user.username,
- name: user.username,
- t: 'd',
- search: true
- })), rooms.map(room => ({
- rid: room._id,
- ...room,
- search: true
- })));
-
- delete this.oldPromise;
- }
- this.setState({
- search: data
- });
- } catch (e) {
- // alert(JSON.stringify(e));
- }
+ search = async(text) => {
+ const result = await RocketChat.search({ text });
+ this.setState({
+ search: result
+ });
}
goRoom = (rid, name) => {
this.props.navigator.push({
screen: 'RoomView',
title: name,
+ backButtonTitle: '',
passProps: {
room: { rid, name },
rid,
@@ -429,22 +390,7 @@ export default class RoomsListView extends LoggedView {
renderSearchBar = () => {
if (Platform.OS === 'ios') {
- return (
-
- this.onSearchChangeText(text)}
- returnKeyType='search'
- placeholder={I18n.t('Search')}
- clearButtonMode='while-editing'
- blurOnSubmit
- autoCorrect={false}
- autoCapitalize='none'
- testID='rooms-list-view-search'
- />
-
- );
+ return this.search(text)} testID='rooms-list-view-search' />;
}
}
@@ -537,7 +483,7 @@ export default class RoomsListView extends LoggedView {
return (
diff --git a/app/views/RoomsListView/styles.js b/app/views/RoomsListView/styles.js
index 1f5afa5e..433289e7 100644
--- a/app/views/RoomsListView/styles.js
+++ b/app/views/RoomsListView/styles.js
@@ -31,17 +31,6 @@ export default StyleSheet.create({
height: 22,
color: 'white'
},
- searchBoxView: {
- backgroundColor: '#eee'
- },
- searchBox: {
- backgroundColor: '#fff',
- margin: 5,
- borderRadius: 5,
- padding: 5,
- paddingLeft: 10,
- color: '#aaa'
- },
loading: {
flex: 1
},
diff --git a/app/views/SelectedUsersView.js b/app/views/SelectedUsersView.js
index beb94b16..45c091a3 100644
--- a/app/views/SelectedUsersView.js
+++ b/app/views/SelectedUsersView.js
@@ -1,56 +1,29 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { View, StyleSheet, TextInput, Text, TouchableOpacity, SafeAreaView, FlatList, LayoutAnimation } from 'react-native';
+import { View, StyleSheet, SafeAreaView, FlatList, LayoutAnimation, Platform } from 'react-native';
import { connect } from 'react-redux';
import { addUser, removeUser, reset, setLoading } from '../actions/selectedUsers';
import database from '../lib/realm';
import RocketChat from '../lib/rocketchat';
import UserItem from '../presentation/UserItem';
-import Avatar from '../containers/Avatar';
import Loading from '../containers/Loading';
import debounce from '../utils/debounce';
import LoggedView from './View';
import I18n from '../i18n';
import log from '../utils/log';
-import { iconsMap } from '../Icons';
+import SearchBox from '../containers/SearchBox';
+import sharedStyles from './Styles';
const styles = StyleSheet.create({
- container: {
- flex: 1,
- alignItems: 'stretch',
- justifyContent: 'center'
- },
safeAreaView: {
flex: 1,
- backgroundColor: '#FFFFFF'
+ backgroundColor: Platform.OS === 'ios' ? '#F7F8FA' : '#E1E5E8'
},
- list: {
- width: '100%',
- backgroundColor: '#FFFFFF'
- },
- searchBoxView: {
- backgroundColor: '#eee'
- },
- searchBox: {
- backgroundColor: '#fff',
- margin: 5,
- borderRadius: 5,
- padding: 5,
- paddingLeft: 10,
- color: '#aaa'
- },
- selectItemView: {
- width: 80,
- height: 80,
- padding: 8,
- flexDirection: 'column',
- justifyContent: 'center',
- alignItems: 'center'
+ header: {
+ backgroundColor: '#fff'
},
separator: {
- height: StyleSheet.hairlineWidth,
- backgroundColor: '#E1E5E8',
marginLeft: 60
}
});
@@ -95,15 +68,19 @@ export default class SelectedUsersView extends LoggedView {
});
}
- componentWillReceiveProps(nextProps) {
- if (nextProps.users.length !== this.props.users.length) {
- const { length } = nextProps.users;
+ async componentDidUpdate(prevProps) {
+ const isVisible = await this.props.navigator.screenIsCurrentlyVisible();
+ if (!isVisible) {
+ return;
+ }
+ if (prevProps.users.length !== this.props.users.length) {
+ const { length } = this.props.users;
const rightButtons = [];
if (length > 0) {
rightButtons.push({
id: 'create',
- testID: 'selected-users-view-submit',
- icon: iconsMap.add
+ title: I18n.t('Next'),
+ testID: 'selected-users-view-submit'
});
}
this.props.navigator.setButtons({ rightButtons });
@@ -123,7 +100,8 @@ export default class SelectedUsersView extends LoggedView {
if (nextAction === 'CREATE_CHANNEL') {
this.props.navigator.push({
screen: 'CreateChannelView',
- title: I18n.t('Create_Channel')
+ title: I18n.t('Create_Channel'),
+ backButtonTitle: ''
});
} else {
try {
@@ -148,90 +126,40 @@ export default class SelectedUsersView extends LoggedView {
this.forceUpdate();
}, 1000);
- async search(text) {
- const searchText = text.trim();
- if (searchText === '') {
- delete this.oldPromise;
- return this.setState({
- search: []
- });
- }
-
- let data = this.data.filtered('name CONTAINS[c] $0 AND t = $1', searchText, 'd').slice(0, 7);
-
- const usernames = data.map(sub => sub.map);
- try {
- if (data.length < 7) {
- if (this.oldPromise) {
- this.oldPromise('cancel');
- }
-
- const { users } = await Promise.race([
- RocketChat.spotlight(searchText, usernames, { users: true, rooms: false }),
- new Promise((resolve, reject) => this.oldPromise = reject)
- ]);
-
- data = users.map(user => ({
- ...user,
- rid: user.username,
- name: user.username,
- t: 'd',
- search: true
- }));
-
- delete this.oldPromise;
- }
- this.setState({
- search: data
- });
- } catch (e) {
- // alert(JSON.stringify(e));
- }
+ search = async(text) => {
+ const result = await RocketChat.search({ text, filterRooms: false });
+ this.setState({
+ search: result
+ });
}
+ isChecked = username => this.props.users.findIndex(el => el.name === username) !== -1;
+
toggleUser = (user) => {
LayoutAnimation.easeInEaseOut();
- const index = this.props.users.findIndex(el => el.name === user.name);
- if (index === -1) {
+ if (!this.isChecked(user.name)) {
this.props.addUser(user);
} else {
this.props.removeUser(user);
}
- };
+ }
_onPressItem = (id, item = {}) => {
if (item.search) {
- this.toggleUser({ _id: item._id, name: item.username });
+ this.toggleUser({ _id: item._id, name: item.username, fname: item.name });
} else {
- this.toggleUser({ _id: item._id, name: item.name });
+ this.toggleUser({ _id: item._id, name: item.name, fname: item.fname });
}
- };
+ }
_onPressSelectedItem = item => this.toggleUser(item);
renderHeader = () => (
-
- {this.renderSearchBar()}
+
+ this.onSearchChangeText(text)} testID='select-users-view-search' />
{this.renderSelected()}
- );
-
- renderSearchBar = () => (
-
- this.onSearchChangeText(text)}
- returnKeyType='search'
- placeholder={I18n.t('Search')}
- clearButtonMode='while-editing'
- blurOnSubmit
- testID='select-users-view-search'
- autoCorrect={false}
- autoCapitalize='none'
- />
-
- );
+ )
renderSelected = () => {
if (this.props.users.length === 0) {
@@ -241,57 +169,70 @@ export default class SelectedUsersView extends LoggedView {
item._id}
- style={styles.list}
+ style={[styles.list, sharedStyles.separatorTop]}
+ contentContainerStyle={{ marginVertical: 5 }}
renderItem={this.renderSelectedItem}
enableEmptySections
keyboardShouldPersistTaps='always'
horizontal
/>
);
- };
+ }
renderSelectedItem = ({ item }) => (
- this._onPressSelectedItem(item)}
testID={`selected-user-${ item.name }`}
- >
-
-
- {item.name}
-
-
- );
-
- renderSeparator = () => ;
-
- renderItem = ({ item }) => (
- this._onPressItem(item._id, item)}
- testID={`select-users-view-item-${ item.name }`}
+ style={{ paddingRight: 15 }}
/>
)
+ renderSeparator = () =>
+
+ renderItem = ({ item, index }) => {
+ const name = item.search ? item.name : item.fname;
+ const username = item.search ? item.username : item.name;
+ let style = {};
+ if (index === 0) {
+ style = { ...sharedStyles.separatorTop };
+ }
+ if (this.state.search.length > 0 && index === this.state.search.length - 1) {
+ style = { ...style, ...sharedStyles.separatorBottom };
+ }
+ if (this.state.search.length === 0 && index === this.data.length - 1) {
+ style = { ...style, ...sharedStyles.separatorBottom };
+ }
+ return (
+ this._onPressItem(item._id, item)}
+ testID={`select-users-view-item-${ item.name }`}
+ icon={this.isChecked(username) ? 'check' : null}
+ style={style}
+ />
+ );
+ }
+
renderList = () => (
0 ? this.state.search : this.data}
extraData={this.props}
keyExtractor={item => item._id}
- style={styles.list}
renderItem={this.renderItem}
- ListHeaderComponent={this.renderHeader}
ItemSeparatorComponent={this.renderSeparator}
+ ListHeaderComponent={this.renderHeader}
enableEmptySections
keyboardShouldPersistTaps='always'
/>
- );
+ )
+
render = () => (
{this.renderList()}
- );
+ )
}
diff --git a/app/views/SettingsView/index.js b/app/views/SettingsView/index.js
index bf3b0691..2de1393d 100644
--- a/app/views/SettingsView/index.js
+++ b/app/views/SettingsView/index.js
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { View, ScrollView, SafeAreaView, Platform, Dimensions } from 'react-native';
+import { View, ScrollView, SafeAreaView, Dimensions } from 'react-native';
import RNPickerSelect from 'react-native-picker-select';
import { connect } from 'react-redux';
@@ -50,7 +50,7 @@ export default class SettingsView extends LoggedView {
componentWillMount() {
this.props.navigator.setButtons({
leftButtons: [{
- id: 'sideMenu',
+ id: 'settings',
icon: { uri: 'settings', scale: Dimensions.get('window').scale }
}]
});
@@ -65,7 +65,7 @@ export default class SettingsView extends LoggedView {
onNavigatorEvent(event) {
if (event.type === 'NavBarButtonPress') {
- if (event.id === 'sideMenu' && Platform.OS === 'ios') {
+ if (event.id === 'settings') {
this.props.navigator.toggleDrawer({
side: 'left'
});
diff --git a/app/views/Styles.js b/app/views/Styles.js
index c0fe421f..facf01c6 100644
--- a/app/views/Styles.js
+++ b/app/views/Styles.js
@@ -1,6 +1,6 @@
import { StyleSheet, Platform } from 'react-native';
-import { COLOR_DANGER, COLOR_BUTTON_PRIMARY, COLOR_TEXT } from '../constants/colors';
+import { COLOR_DANGER, COLOR_BUTTON_PRIMARY, COLOR_TEXT, COLOR_SEPARATOR } from '../constants/colors';
export default StyleSheet.create({
container: {
@@ -197,5 +197,22 @@ export default StyleSheet.create({
width: 44,
alignItems: 'center',
justifyContent: 'center'
+ },
+ separator: {
+ height: StyleSheet.hairlineWidth,
+ backgroundColor: COLOR_SEPARATOR
+ },
+ separatorTop: {
+ borderColor: COLOR_SEPARATOR,
+ borderTopWidth: StyleSheet.hairlineWidth
+ },
+ separatorBottom: {
+ borderColor: COLOR_SEPARATOR,
+ borderBottomWidth: StyleSheet.hairlineWidth
+ },
+ separatorVertical: {
+ borderColor: COLOR_SEPARATOR,
+ borderTopWidth: StyleSheet.hairlineWidth,
+ borderBottomWidth: StyleSheet.hairlineWidth
}
});
diff --git a/app/views/index.js b/app/views/index.js
index 1b47ee6c..85e1e7a5 100644
--- a/app/views/index.js
+++ b/app/views/index.js
@@ -6,6 +6,7 @@ import ForgotPasswordView from './ForgotPasswordView';
import LoginSignupView from './LoginSignupView';
import LoginView from './LoginView';
import MentionedMessagesView from './MentionedMessagesView';
+import NewMessageView from './NewMessageView';
import NewServerView from './NewServerView';
import OAuthView from './OAuthView';
import OnboardingView from './OnboardingView';
@@ -36,6 +37,7 @@ export const registerScreens = (store) => {
Navigation.registerComponent('LoginSignupView', () => LoginSignupView, store, Provider);
Navigation.registerComponent('LoginView', () => LoginView, store, Provider);
Navigation.registerComponent('MentionedMessagesView', () => MentionedMessagesView, store, Provider);
+ Navigation.registerComponent('NewMessageView', () => NewMessageView, store, Provider);
Navigation.registerComponent('NewServerView', () => NewServerView, store, Provider);
Navigation.registerComponent('OAuthView', () => OAuthView, store, Provider);
Navigation.registerComponent('OnboardingView', () => OnboardingView, store, Provider);
diff --git a/e2e/01-welcome.spec.js b/e2e/01-welcome.spec.js
index 5001ef68..81a6075b 100644
--- a/e2e/01-welcome.spec.js
+++ b/e2e/01-welcome.spec.js
@@ -30,14 +30,14 @@ describe('Welcome screen', () => {
await element(by.id('welcome-view-login')).tap();
await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('login-view'))).toBeVisible();
- await tapBack('Welcome');
+ await tapBack();
});
it('should navigate to register', async() => {
await element(by.id('welcome-view-register')).tap();
await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('register-view'))).toBeVisible();
- await tapBack('Welcome');
+ await tapBack();
});
afterEach(async() => {
diff --git a/e2e/04-login.spec.js b/e2e/04-login.spec.js
index c017646f..4540f91e 100644
--- a/e2e/04-login.spec.js
+++ b/e2e/04-login.spec.js
@@ -49,18 +49,18 @@ describe('Login screen', () => {
await element(by.id('login-view-register')).tap();
await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('register-view'))).toBeVisible();
- await tapBack('Login');
+ await tapBack();
});
it('should navigate to forgot password', async() => {
await element(by.id('login-view-forgot-password')).tap();
await waitFor(element(by.id('forgot-password-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('forgot-password-view'))).toBeVisible();
- await tapBack('Login');
+ await tapBack();
});
it('should navigate to welcome', async() => {
- await tapBack('Welcome');
+ await tapBack();
await waitFor(element(by.id('welcome-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('welcome-view'))).toBeVisible();
await element(by.id('welcome-view-login')).tap();
diff --git a/e2e/05-roomslist.spec.js b/e2e/05-roomslist.spec.js
index 645e3593..309ebf87 100644
--- a/e2e/05-roomslist.spec.js
+++ b/e2e/05-roomslist.spec.js
@@ -11,9 +11,9 @@ describe('Rooms list screen', () => {
await expect(element(by.id('rooms-list-view'))).toBeVisible();
});
- it('should have rooms list', async() => {
- await expect(element(by.id('rooms-list-view-list'))).toBeVisible();
- });
+ // it('should have rooms list', async() => {
+ // await expect(element(by.id('rooms-list-view-list'))).toBeVisible();
+ // });
it('should have room item', async() => {
await expect(element(by.id('rooms-list-view-item-general'))).toExist();
@@ -38,9 +38,12 @@ describe('Rooms list screen', () => {
describe('Usage', async() => {
it('should search room and navigate', async() => {
- await element(by.id('rooms-list-view-list')).swipe('down');
- await waitFor(element(by.id('rooms-list-view-search'))).toBeVisible().withTimeout(2000);
- await expect(element(by.id('rooms-list-view-search'))).toBeVisible();
+ // await element(by.id('rooms-list-view-list')).swipe('down');
+ // await waitFor(element(by.id('rooms-list-view-search'))).toBeVisible().withTimeout(2000);
+ // await expect(element(by.id('rooms-list-view-search'))).toBeVisible();
+
+ await waitFor(element(by.id('rooms-list-view-search'))).toExist().withTimeout(2000);
+
await element(by.id('rooms-list-view-search')).replaceText('rocket.cat');
await waitFor(element(by.id('rooms-list-view-item-rocket.cat'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('rooms-list-view-item-rocket.cat'))).toBeVisible();
@@ -49,7 +52,7 @@ describe('Rooms list screen', () => {
await expect(element(by.id('room-view'))).toBeVisible();
await waitFor(element(by.text('rocket.cat'))).toBeVisible().withTimeout(60000);
await expect(element(by.text('rocket.cat'))).toBeVisible();
- await tapBack('Messages');
+ await tapBack(2);
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('rooms-list-view'))).toBeVisible();
await element(by.id('rooms-list-view-search')).replaceText('');
diff --git a/e2e/06-createroom.spec.js b/e2e/06-createroom.spec.js
index 19d15354..dc056586 100644
--- a/e2e/06-createroom.spec.js
+++ b/e2e/06-createroom.spec.js
@@ -3,37 +3,70 @@ const {
} = require('detox');
const { takeScreenshot } = require('./helpers/screenshot');
const data = require('./data');
-const { tapBack } = require('./helpers/app');
+const { tapBack, sleep } = require('./helpers/app');
describe('Create room screen', () => {
before(async() => {
+ await sleep(5000);
+ await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
+ await device.reloadReactNative();
await element(by.id('rooms-list-view-create-channel')).tap();
- await waitFor(element(by.id('select-users-view'))).toBeVisible().withTimeout(2000);
+ await waitFor(element(by.id('new-message-view'))).toBeVisible().withTimeout(2000);
});
- describe('Render', async() => {
- it('should have select users screen', async() => {
- await expect(element(by.id('select-users-view'))).toBeVisible();
- });
+ describe('New Message', async() => {
+ describe('Render', async() => {
+ it('should have new message screen', async() => {
+ await expect(element(by.id('new-message-view'))).toBeVisible();
+ });
+
+ it('should have search input', async() => {
+ await waitFor(element(by.id('new-message-view-search'))).toExist().withTimeout(2000);
+ await expect(element(by.id('new-message-view-search'))).toExist();
+ });
+
+ after(async() => {
+ takeScreenshot();
+ });
+ })
- it('should have search input', async() => {
- await expect(element(by.id('select-users-view-search'))).toBeVisible();
- });
+ describe('Usage', async() => {
+ it('should back to rooms list', async() => {
+ await tapBack();
+ await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
+ await expect(element(by.id('rooms-list-view'))).toBeVisible();
+ await element(by.id('rooms-list-view-create-channel')).tap();
+ await waitFor(element(by.id('new-message-view'))).toBeVisible().withTimeout(2000);
+ });
- after(async() => {
- takeScreenshot();
- });
+ it('should search user and navigate', async() => {
+ await element(by.id('new-message-view-search')).replaceText('rocket.cat');
+ await waitFor(element(by.id('new-message-view-item-rocket.cat'))).toBeVisible().withTimeout(60000);
+ await expect(element(by.id('new-message-view-item-rocket.cat'))).toBeVisible();
+ await element(by.id('new-message-view-item-rocket.cat')).tap();
+ await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(10000);
+ await expect(element(by.id('room-view'))).toBeVisible();
+ await waitFor(element(by.text('rocket.cat'))).toBeVisible().withTimeout(60000);
+ await expect(element(by.text('rocket.cat'))).toBeVisible();
+ await tapBack(2);
+ await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
+ await element(by.id('rooms-list-view-create-channel')).tap();
+ });
+
+ it('should navigate to select users', async() => {
+ await element(by.id('new-message-view-create-channel')).tap();
+ await waitFor(element(by.id('select-users-view'))).toBeVisible().withTimeout(2000);
+ await expect(element(by.id('select-users-view'))).toBeVisible();
+ });
+
+
+ after(async() => {
+ takeScreenshot();
+ });
+ })
});
- describe('Usage', async() => {
- it('should back to rooms list', async() => {
- await tapBack('Messages');
- await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
- await expect(element(by.id('rooms-list-view'))).toBeVisible();
- await element(by.id('rooms-list-view-create-channel')).tap();
- await waitFor(element(by.id('select-users-view'))).toBeVisible().withTimeout(2000);
- });
-
+ describe('Select Users', async() => {
it('should search users', async() => {
await element(by.id('select-users-view-search')).replaceText('rocket.cat');
await waitFor(element(by.id(`select-users-view-item-rocket.cat`))).toBeVisible().withTimeout(10000);
@@ -57,52 +90,65 @@ describe('Create room screen', () => {
await element(by.id('selected-users-view-submit')).tap();
await waitFor(element(by.id('create-channel-view'))).toBeVisible().withTimeout(5000);
await expect(element(by.id('create-channel-view'))).toBeVisible();
- await expect(element(by.id('create-channel-name'))).toBeVisible();
- await expect(element(by.id('create-channel-type'))).toBeVisible();
- await expect(element(by.id('create-channel-readonly'))).toBeVisible();
- await expect(element(by.id('create-channel-broadcast'))).toExist();
- await expect(element(by.id('create-channel-submit'))).toExist();
});
+ })
- it('should get invalid room', async() => {
- await element(by.id('create-channel-name')).replaceText('general');
- await element(by.id('create-channel-submit')).tap();
- await waitFor(element(by.id('create-channel-error'))).toBeVisible().withTimeout(60000);
- await expect(element(by.id('create-channel-error'))).toBeVisible();
- });
+ describe('Create Channel', async() => {
+ describe('Render', async() => {
+ it('should render all fields', async() => {
+ await expect(element(by.id('create-channel-name'))).toBeVisible();
+ await expect(element(by.id('create-channel-type'))).toBeVisible();
+ await expect(element(by.id('create-channel-readonly'))).toBeVisible();
+ await expect(element(by.id('create-channel-broadcast'))).toExist();
+ })
+ })
- it('should create public room', async() => {
- await element(by.id('create-channel-name')).replaceText(`public${ data.random }`);
- await element(by.id('create-channel-type')).tap();
- await element(by.id('create-channel-submit')).tap();
- await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(60000);
- await expect(element(by.id('room-view'))).toBeVisible();
- await waitFor(element(by.text(`public${ data.random }`))).toBeVisible().withTimeout(60000);
- await expect(element(by.text(`public${ data.random }`))).toBeVisible();
- await tapBack('Messages');
- await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
- await waitFor(element(by.id(`rooms-list-view-item-public${ data.random }`))).toBeVisible().withTimeout(60000);
- await expect(element(by.id(`rooms-list-view-item-public${ data.random }`))).toBeVisible();
- });
-
- it('should create private room', async() => {
- await element(by.id('rooms-list-view-create-channel')).tap();
- await waitFor(element(by.id('select-users-view'))).toBeVisible().withTimeout(2000);
- await element(by.id('select-users-view-item-rocket.cat')).tap();
- await waitFor(element(by.id('selected-user-rocket.cat'))).toBeVisible().withTimeout(5000);
- await element(by.id('selected-users-view-submit')).tap();
- await waitFor(element(by.id('create-channel-view'))).toBeVisible().withTimeout(5000);
- await element(by.id('create-channel-name')).replaceText(`private${ data.random }`);
- await element(by.id('create-channel-submit')).tap();
- await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(60000);
- await expect(element(by.id('room-view'))).toBeVisible();
- await waitFor(element(by.text(`private${ data.random }`))).toBeVisible().withTimeout(60000);
- await expect(element(by.text(`private${ data.random }`))).toBeVisible();
- await tapBack('Messages');
- await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
- await waitFor(element(by.id(`rooms-list-view-item-private${ data.random }`))).toBeVisible().withTimeout(60000);
- await expect(element(by.id(`rooms-list-view-item-private${ data.random }`))).toBeVisible();
- });
+ describe('Usage', async() => {
+ it('should get invalid room', async() => {
+ await element(by.id('create-channel-name')).replaceText('general');
+ await element(by.id('create-channel-submit')).tap();
+ await waitFor(element(by.text(`A channel with name 'general' exists`))).toBeVisible().withTimeout(60000);
+ await expect(element(by.text(`A channel with name 'general' exists`))).toBeVisible();
+ await element(by.text('OK')).tap();
+ });
+
+ it('should create public room', async() => {
+ await element(by.id('create-channel-name')).replaceText(`public${ data.random }`);
+ await element(by.id('create-channel-type')).tap();
+ await element(by.id('create-channel-submit')).tap();
+ await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(60000);
+ await expect(element(by.id('room-view'))).toBeVisible();
+ await waitFor(element(by.text(`public${ data.random }`))).toBeVisible().withTimeout(60000);
+ await expect(element(by.text(`public${ data.random }`))).toBeVisible();
+ await tapBack(2);
+ await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
+ await waitFor(element(by.id(`rooms-list-view-item-public${ data.random }`))).toBeVisible().withTimeout(60000);
+ await expect(element(by.id(`rooms-list-view-item-public${ data.random }`))).toBeVisible();
+ });
+
+ it('should create private room', async() => {
+ await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
+ await device.reloadReactNative();
+ await element(by.id('rooms-list-view-create-channel')).tap();
+ await waitFor(element(by.id('new-message-view'))).toBeVisible().withTimeout(2000);
+ await element(by.id('new-message-view-create-channel')).tap();
+ await waitFor(element(by.id('select-users-view'))).toBeVisible().withTimeout(2000);
+ await element(by.id('select-users-view-item-rocket.cat')).tap();
+ await waitFor(element(by.id('selected-user-rocket.cat'))).toBeVisible().withTimeout(5000);
+ await element(by.id('selected-users-view-submit')).tap();
+ await waitFor(element(by.id('create-channel-view'))).toBeVisible().withTimeout(5000);
+ await element(by.id('create-channel-name')).replaceText(`private${ data.random }`);
+ await element(by.id('create-channel-submit')).tap();
+ await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(60000);
+ await expect(element(by.id('room-view'))).toBeVisible();
+ await waitFor(element(by.text(`private${ data.random }`))).toBeVisible().withTimeout(60000);
+ await expect(element(by.text(`private${ data.random }`))).toBeVisible();
+ await tapBack(2);
+ await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
+ await waitFor(element(by.id(`rooms-list-view-item-private${ data.random }`))).toBeVisible().withTimeout(60000);
+ await expect(element(by.id(`rooms-list-view-item-private${ data.random }`))).toBeVisible();
+ });
+ })
afterEach(async() => {
takeScreenshot();
diff --git a/e2e/07-room.spec.js b/e2e/07-room.spec.js
index 2100ad0f..69a72683 100644
--- a/e2e/07-room.spec.js
+++ b/e2e/07-room.spec.js
@@ -74,7 +74,7 @@ describe('Room screen', () => {
describe('Usage', async() => {
describe('Header', async() => {
it('should back to rooms list', async() => {
- await tapBack('Messages');
+ await tapBack(2);
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('rooms-list-view'))).toBeVisible();
await navigateToRoom();
@@ -84,7 +84,7 @@ describe('Room screen', () => {
await element(by.id('room-view-header-actions')).tap();
await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('room-actions-view'))).toBeVisible();
- await tapBack(`private${ data.random }`);
+ await tapBack();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
});
});
@@ -287,7 +287,8 @@ describe('Room screen', () => {
});
after(async() => {
- await tapBack('Messages');
+ await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
+ await tapBack(2);
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('rooms-list-view'))).toBeVisible();
});
diff --git a/e2e/08-roomactions.spec.js b/e2e/08-roomactions.spec.js
index cc314a8f..a6b939e4 100644
--- a/e2e/08-roomactions.spec.js
+++ b/e2e/08-roomactions.spec.js
@@ -21,15 +21,16 @@ async function navigateToRoomActions(type) {
await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(5000);
}
-async function backToActions() {
- await tapBack('Actions');
- await waitFor(element(by.id('rooms-actions-view'))).toBeVisible().withTimeout(2000);
+async function backToActions(index = 0) {
+ await tapBack(index);
+ await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(2000);
+ await expect(element(by.id('room-actions-view'))).toBeVisible();
}
-async function backToRoomsList(room) {
- await tapBack(room);
+async function backToRoomsList() {
+ await tapBack();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(2000);
- await tapBack('Messages');
+ await tapBack(2);
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
}
@@ -98,7 +99,7 @@ describe('Room actions screen', () => {
});
after(async() => {
- await backToRoomsList('rocket.cat');
+ await backToRoomsList();
});
});
@@ -247,10 +248,10 @@ describe('Room actions screen', () => {
await element(by.id('room-actions-search')).tap();
await waitFor(element(by.id('search-messages-view'))).toExist().withTimeout(2000);
await expect(element(by.id('search-message-view-input'))).toBeVisible();
- await element(by.id('search-message-view-input')).tap();
await element(by.id('search-message-view-input')).replaceText(`/${ data.random }message/`);
await waitFor(element(by.text(`${ data.random }message`).withAncestor(by.id('search-messages-view'))).atIndex(0)).toBeVisible().withTimeout(60000);
await expect(element(by.text(`${ data.random }message`).withAncestor(by.id('search-messages-view'))).atIndex(0)).toBeVisible();
+ await element(by.traits(['button'])).atIndex(0).tap();
await backToActions();
});
@@ -284,7 +285,7 @@ describe('Room actions screen', () => {
await expect(element(by.text('You are the last owner. Please set new owner before leaving the room.'))).toBeVisible();
await takeScreenshot();
await element(by.text('OK')).tap();
- await waitFor(element(by.id('rooms-actions-view'))).toBeVisible().withTimeout(2000);
+ await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(2000);
});
describe('Add User', async() => {
@@ -304,7 +305,7 @@ describe('Room actions screen', () => {
await element(by.id('room-members-view-toggle-status')).tap();
await waitFor(element(by.id(`room-members-view-item-${ data.alternateUser }`))).toBeVisible().withTimeout(60000);
await expect(element(by.id(`room-members-view-item-${ data.alternateUser }`))).toBeVisible();
- await backToActions();
+ await backToActions(1);
});
after(async() => {
@@ -363,8 +364,8 @@ describe('Room actions screen', () => {
await expect(element(by.id('room-view'))).toBeVisible();
await waitFor(element(by.text(data.alternateUser))).toBeVisible().withTimeout(60000);
await expect(element(by.text(data.alternateUser))).toBeVisible();
- await tapBack('Messages');
- await waitFor(element(by.id('room-list-view'))).toBeVisible().withTimeout(2000);
+ await tapBack(2);
+ await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
});
afterEach(async() => {
diff --git a/e2e/09-roominfo.spec.js b/e2e/09-roominfo.spec.js
index b2c42430..0638d012 100644
--- a/e2e/09-roominfo.spec.js
+++ b/e2e/09-roominfo.spec.js
@@ -158,7 +158,7 @@ describe('Room info screen', () => {
await expect(element(by.text('Settings succesfully changed!'))).toBeVisible();
await waitFor(element(by.text('Settings succesfully changed!'))).toBeNotVisible().withTimeout(10000);
await expect(element(by.text('Settings succesfully changed!'))).toBeNotVisible();
- await tapBack('Room Info');
+ await tapBack();
await waitFor(element(by.id('room-info-view'))).toBeVisible().withTimeout(2000);
await waitFor(element(by.id('room-info-view-name'))).toHaveText(`${ room }new`).withTimeout(60000);
await expect(element(by.id('room-info-view-name'))).toHaveText(`${ room }new`);
@@ -206,7 +206,7 @@ describe('Room info screen', () => {
await expect(element(by.text('Settings succesfully changed!'))).toBeVisible();
await waitFor(element(by.text('Settings succesfully changed!'))).toBeNotVisible().withTimeout(10000);
await expect(element(by.text('Settings succesfully changed!'))).toBeNotVisible();
- await tapBack('Room Info');
+ await tapBack();
await waitFor(element(by.id('room-info-view'))).toBeVisible().withTimeout(2000);
await waitFor(element(by.id('room-info-view-description'))).toHaveText('new description').withTimeout(60000);
await expect(element(by.id('room-info-view-description'))).toHaveText('new description');
@@ -223,7 +223,7 @@ describe('Room info screen', () => {
await expect(element(by.text('Settings succesfully changed!'))).toBeVisible();
await waitFor(element(by.text('Settings succesfully changed!'))).toBeNotVisible().withTimeout(10000);
await expect(element(by.text('Settings succesfully changed!'))).toBeNotVisible();
- await tapBack('Room Info');
+ await tapBack();
await waitFor(element(by.id('room-info-view'))).toBeVisible().withTimeout(2000);
await waitFor(element(by.id('room-info-view-topic'))).toHaveText('new topic').withTimeout(60000);
await expect(element(by.id('room-info-view-topic'))).toHaveText('new topic');
@@ -240,7 +240,7 @@ describe('Room info screen', () => {
await expect(element(by.text('Settings succesfully changed!'))).toBeVisible();
await waitFor(element(by.text('Settings succesfully changed!'))).toBeNotVisible().withTimeout(10000);
await expect(element(by.text('Settings succesfully changed!'))).toBeNotVisible();
- await tapBack('Room Info');
+ await tapBack();
await waitFor(element(by.id('room-info-view'))).toBeVisible().withTimeout(2000);
await waitFor(element(by.id('room-info-view-announcement'))).toHaveText('new announcement').withTimeout(60000);
await expect(element(by.id('room-info-view-announcement'))).toHaveText('new announcement');
diff --git a/e2e/11-broadcast.spec.js b/e2e/11-broadcast.spec.js
index d959e539..582cb893 100644
--- a/e2e/11-broadcast.spec.js
+++ b/e2e/11-broadcast.spec.js
@@ -12,6 +12,8 @@ describe('Broadcast room', () => {
it('should create broadcast room', async() => {
await element(by.id('rooms-list-view-create-channel')).tap();
+ await waitFor(element(by.id('new-message-view'))).toBeVisible().withTimeout(2000);
+ await element(by.id('new-message-view-create-channel')).tap();
await waitFor(element(by.id('select-users-view'))).toBeVisible().withTimeout(2000);
await element(by.id(`select-users-view-item-${ data.alternateUser }`)).tap();
await waitFor(element(by.id(`selected-user-${ data.alternateUser }`))).toBeVisible().withTimeout(5000);
@@ -30,14 +32,14 @@ describe('Broadcast room', () => {
await waitFor(element(by.id('room-info-view'))).toBeVisible().withTimeout(2000);
await waitFor(element(by.id('room-info-view-broadcast'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('room-info-view-broadcast'))).toBeVisible();
- await tapBack('Actions');
+ await tapBack(1);
await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(2000);
- await tapBack(`broadcast${ data.random }`);
+ await tapBack();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(2000);
- await tapBack('Messages');
+ await tapBack(2);
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
- await waitFor(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toBeVisible().withTimeout(60000);
- await expect(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toBeVisible();
+ await waitFor(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toExist().withTimeout(60000);
+ await expect(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toExist();
});
it('should send message', async() => {
@@ -51,7 +53,7 @@ describe('Broadcast room', () => {
});
it('should login as user without write message authorization and enter room', async() => {
- await tapBack('Messages');
+ await tapBack(2);
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('rooms-list-view'))).toBeVisible();
await logout();
@@ -61,7 +63,8 @@ describe('Broadcast room', () => {
await element(by.id('login-view-submit')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
// await device.reloadReactNative(); // remove after fix logout
- await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
+ // await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
+ await element(by.id('rooms-list-view-search')).replaceText(`broadcast${ data.random }`);
await waitFor(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toBeVisible().withTimeout(60000);
await expect(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toBeVisible();
await element(by.id(`rooms-list-view-item-broadcast${ data.random }`)).tap();
@@ -107,7 +110,7 @@ describe('Broadcast room', () => {
after(async() => {
// log back as main test user and left screen on RoomsListView
- await tapBack('Messages');
+ await tapBack(2);
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
await logout();
await navigateToLogin();
diff --git a/e2e/helpers/app.js b/e2e/helpers/app.js
index 873dd0be..1da062ca 100644
--- a/e2e/helpers/app.js
+++ b/e2e/helpers/app.js
@@ -36,12 +36,8 @@ async function logout() {
await expect(element(by.id('onboarding-view'))).toBeVisible();
}
-async function tapBack(label) {
- try {
- return element(by.traits(['button']).and(by.label(label || 'Back'))).atIndex(0).tap();
- } catch (err) {
- return element(by.type('_UIModernBarButton').and(by.label(label || 'Back'))).tap();
- }
+async function tapBack(index) {
+ await element(by.type('_UIModernBarButton')).atIndex(index || 0).tap();
}
async function sleep(ms) {
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/plus.imageset/Contents.json b/ios/RocketChatRN/Images.xcassets/Icons/plus.imageset/Contents.json
new file mode 100644
index 00000000..d984b4ae
--- /dev/null
+++ b/ios/RocketChatRN/Images.xcassets/Icons/plus.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "plus.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "plus@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "plus@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/plus.imageset/plus.png b/ios/RocketChatRN/Images.xcassets/Icons/plus.imageset/plus.png
new file mode 100644
index 00000000..cce62241
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/plus.imageset/plus.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/plus.imageset/plus@2x.png b/ios/RocketChatRN/Images.xcassets/Icons/plus.imageset/plus@2x.png
new file mode 100644
index 00000000..251c8d18
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/plus.imageset/plus@2x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/plus.imageset/plus@3x.png b/ios/RocketChatRN/Images.xcassets/Icons/plus.imageset/plus@3x.png
new file mode 100644
index 00000000..71db08c7
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/plus.imageset/plus@3x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/textinput_search.imageset/Contents.json b/ios/RocketChatRN/Images.xcassets/Icons/textinput_search.imageset/Contents.json
new file mode 100644
index 00000000..18d7cebf
--- /dev/null
+++ b/ios/RocketChatRN/Images.xcassets/Icons/textinput_search.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "textinput_search.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "textinput_search@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "textinput_search@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/textinput_search.imageset/textinput_search.png b/ios/RocketChatRN/Images.xcassets/Icons/textinput_search.imageset/textinput_search.png
new file mode 100644
index 00000000..4eefb38c
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/textinput_search.imageset/textinput_search.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/textinput_search.imageset/textinput_search@2x.png b/ios/RocketChatRN/Images.xcassets/Icons/textinput_search.imageset/textinput_search@2x.png
new file mode 100644
index 00000000..a8d16731
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/textinput_search.imageset/textinput_search@2x.png differ
diff --git a/ios/RocketChatRN/Images.xcassets/Icons/textinput_search.imageset/textinput_search@3x.png b/ios/RocketChatRN/Images.xcassets/Icons/textinput_search.imageset/textinput_search@3x.png
new file mode 100644
index 00000000..1c9db2d0
Binary files /dev/null and b/ios/RocketChatRN/Images.xcassets/Icons/textinput_search.imageset/textinput_search@3x.png differ
diff --git a/package-lock.json b/package-lock.json
index 7158b588..398ad21e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15999,7 +15999,7 @@
}
},
"react-native-navigation": {
- "version": "git+https://github.com/RocketChat/react-native-navigation.git#1a428f14ddda77511676d0c6d863083ce6225e32",
+ "version": "git+https://github.com/RocketChat/react-native-navigation.git#024095e7679afa0b4e9475f4ffce45d9e50ca5ad",
"from": "git+https://github.com/RocketChat/react-native-navigation.git",
"requires": {
"lodash": "4.x.x"