diff --git a/app/index.js b/app/index.js
index 289e8fbd9..e916e59d2 100644
--- a/app/index.js
+++ b/app/index.js
@@ -18,26 +18,17 @@ const startLogged = () => {
Navigation.loadView('RoomHeaderView');
Navigation.loadView('SettingsView');
Navigation.loadView('SidebarView');
+
Navigation.setRoot({
root: {
- sideMenu: {
- left: {
+ stack: {
+ id: 'AppRoot',
+ children: [{
component: {
- id: 'SidebarView',
- name: 'SidebarView'
+ id: 'RoomsListView',
+ name: 'RoomsListView'
}
- },
- center: {
- stack: {
- id: 'AppRoot',
- children: [{
- component: {
- id: 'RoomsListView',
- name: 'RoomsListView'
- }
- }]
- }
- }
+ }]
}
}
});
diff --git a/app/sagas/login.js b/app/sagas/login.js
index 5ccfeeb5d..78a17d9e6 100644
--- a/app/sagas/login.js
+++ b/app/sagas/login.js
@@ -6,11 +6,12 @@ import {
import Navigation from '../lib/Navigation';
import * as types from '../actions/actionsTypes';
import { appStart } from '../actions';
-import { serverFinishAdd } from '../actions/server';
+import { serverFinishAdd, selectServerRequest } from '../actions/server';
import { loginFailure, loginSuccess } from '../actions/login';
import RocketChat from '../lib/rocketchat';
import log from '../utils/log';
import I18n from '../i18n';
+import database from '../lib/realm';
const getServer = state => state.server.server;
const loginWithPasswordCall = args => RocketChat.loginWithPassword(args);
@@ -60,8 +61,26 @@ const handleLogout = function* handleLogout() {
if (server) {
try {
yield call(logoutCall, { server });
+ const { serversDB } = database.databases;
+ // all servers
+ const servers = yield serversDB.objects('servers');
+ // filter logging out server and delete it
+ const serverRecord = servers.filtered('id = $0', server);
+ serversDB.write(() => {
+ serversDB.delete(serverRecord);
+ });
+ // see if there's other logged in servers and selects first one
+ if (servers.length > 0) {
+ const newServer = servers[0].id;
+ const token = yield AsyncStorage.getItem(`${ RocketChat.TOKEN_KEY }-${ newServer }`);
+ if (token) {
+ return yield put(selectServerRequest(newServer));
+ }
+ }
+ // if there's no servers, go outside
yield put(appStart('outside'));
} catch (e) {
+ yield put(appStart('outside'));
log('handleLogout', e);
}
}
diff --git a/app/views/ProfileView/index.js b/app/views/ProfileView/index.js
index 27f00ea84..c9199d9de 100644
--- a/app/views/ProfileView/index.js
+++ b/app/views/ProfileView/index.js
@@ -1,8 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import {
- View, ScrollView, Keyboard, BackHandler
-} from 'react-native';
+import { View, ScrollView, Keyboard } from 'react-native';
import { connect } from 'react-redux';
import Dialog from 'react-native-dialog';
import SHA256 from 'js-sha256';
@@ -12,7 +10,6 @@ import RNPickerSelect from 'react-native-picker-select';
import SafeAreaView from 'react-native-safe-area-view';
import equal from 'deep-equal';
-import Navigation from '../../lib/Navigation';
import LoggedView from '../View';
import KeyboardView from '../../presentation/KeyboardView';
import sharedStyles from '../Styles';
@@ -26,9 +23,7 @@ import I18n from '../../i18n';
import Button from '../../containers/Button';
import Avatar from '../../containers/Avatar';
import Touch from '../../utils/touch';
-import { appStart as appStartAction } from '../../actions';
import { setUser as setUserAction } from '../../actions/login';
-import Icons from '../../lib/Icons';
@connect(state => ({
user: {
@@ -42,7 +37,6 @@ import Icons from '../../lib/Icons';
Accounts_CustomFields: state.settings.Accounts_CustomFields,
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
}), dispatch => ({
- appStart: () => dispatch(appStartAction()),
setUser: params => dispatch(setUserAction(params))
}))
/** @extends React.Component */
@@ -50,32 +44,17 @@ export default class ProfileView extends LoggedView {
static options() {
return {
topBar: {
- leftButtons: [{
- id: 'settings',
- icon: Icons.getSource('settings'),
- testID: 'rooms-list-view-sidebar'
- }],
title: {
text: I18n.t('Profile')
}
- },
- sideMenu: {
- left: {
- enabled: true
- },
- right: {
- enabled: true
- }
}
};
}
static propTypes = {
baseUrl: PropTypes.string,
- componentId: PropTypes.string,
user: PropTypes.object,
Accounts_CustomFields: PropTypes.string,
- appStart: PropTypes.func,
setUser: PropTypes.func
}
@@ -94,8 +73,6 @@ export default class ProfileView extends LoggedView {
avatarSuggestions: {},
customFields: {}
};
- Navigation.events().bindComponent(this);
- BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
}
async componentDidMount() {
@@ -126,22 +103,6 @@ export default class ProfileView extends LoggedView {
return false;
}
- componentWillUnmount() {
- BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress);
- }
-
- navigationButtonPressed = ({ buttonId }) => {
- if (buttonId === 'settings') {
- Navigation.toggleDrawer();
- }
- }
-
- handleBackPress = () => {
- const { appStart } = this.props;
- appStart('background');
- return false;
- }
-
setAvatar = (avatar) => {
this.setState({ avatar });
}
diff --git a/app/views/RoomsListView/ServerDropdown.js b/app/views/RoomsListView/ServerDropdown.js
index 3f4508819..34db1f007 100644
--- a/app/views/RoomsListView/ServerDropdown.js
+++ b/app/views/RoomsListView/ServerDropdown.js
@@ -81,6 +81,13 @@ export default class ServerDropdown extends Component {
}
}
+ componentWillUnmount() {
+ if (this.newServerTimeout) {
+ clearTimeout(this.newServerTimeout);
+ this.newServerTimeout = false;
+ }
+ }
+
updateState = () => {
const { servers } = this;
this.setState({ servers });
@@ -137,12 +144,7 @@ export default class ServerDropdown extends Component {
const token = await AsyncStorage.getItem(`${ RocketChat.TOKEN_KEY }-${ server }`);
if (!token) {
appStart();
- try {
- this.sdk.disconnect();
- } catch (error) {
- console.warn(error);
- }
- setTimeout(() => {
+ this.newServerTimeout = setTimeout(() => {
EventEmitter.emit('NewServer', { server });
}, 1000);
} else {
diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js
index 8d78aa8b4..182344753 100644
--- a/app/views/RoomsListView/index.js
+++ b/app/views/RoomsListView/index.js
@@ -270,7 +270,22 @@ export default class RoomsListView extends LoggedView {
}
});
} else if (buttonId === 'settings') {
- Navigation.toggleDrawer();
+ Navigation.showModal({
+ stack: {
+ children: [{
+ component: {
+ name: 'SidebarView',
+ options: {
+ topBar: {
+ title: {
+ text: I18n.t('Settings')
+ }
+ }
+ }
+ }
+ }]
+ }
+ });
} else if (buttonId === 'search') {
this.initSearchingAndroid();
} else if (buttonId === 'back') {
diff --git a/app/views/SettingsView/index.js b/app/views/SettingsView/index.js
index 6ca848e70..a53ae7ab5 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, BackHandler } from 'react-native';
+import { View, ScrollView } from 'react-native';
import RNPickerSelect from 'react-native-picker-select';
import { connect } from 'react-redux';
import SafeAreaView from 'react-native-safe-area-view';
@@ -18,36 +18,20 @@ import Loading from '../../containers/Loading';
import { showErrorAlert, showToast } from '../../utils/info';
import log from '../../utils/log';
import { setUser as setUserAction } from '../../actions/login';
-import { appStart as appStartAction } from '../../actions';
-import Icons from '../../lib/Icons';
@connect(state => ({
userLanguage: state.login.user && state.login.user.language
}), dispatch => ({
- setUser: params => dispatch(setUserAction(params)),
- appStart: () => dispatch(appStartAction())
+ setUser: params => dispatch(setUserAction(params))
}))
/** @extends React.Component */
export default class SettingsView extends LoggedView {
static options() {
return {
topBar: {
- leftButtons: [{
- id: 'settings',
- icon: Icons.getSource('settings'),
- testID: 'rooms-list-view-sidebar'
- }],
title: {
text: I18n.t('Settings')
}
- },
- sideMenu: {
- left: {
- enabled: true
- },
- right: {
- enabled: true
- }
}
};
}
@@ -55,8 +39,7 @@ export default class SettingsView extends LoggedView {
static propTypes = {
componentId: PropTypes.string,
userLanguage: PropTypes.string,
- setUser: PropTypes.func,
- appStart: PropTypes.func
+ setUser: PropTypes.func
}
constructor(props) {
@@ -85,8 +68,6 @@ export default class SettingsView extends LoggedView {
}],
saving: false
};
- Navigation.events().bindComponent(this);
- BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
}
shouldComponentUpdate(nextProps, nextState) {
@@ -104,22 +85,6 @@ export default class SettingsView extends LoggedView {
return false;
}
- componentWillUnmount() {
- BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress);
- }
-
- navigationButtonPressed = ({ buttonId }) => {
- if (buttonId === 'settings') {
- Navigation.toggleDrawer();
- }
- }
-
- handleBackPress = () => {
- const { appStart } = this.props;
- appStart('background');
- return false;
- }
-
getLabel = (language) => {
const { languages } = this.state;
const l = languages.find(i => i.value === language);
diff --git a/app/views/SidebarView.js b/app/views/SidebarView.js
index 0408c3e76..ed44ddf4c 100644
--- a/app/views/SidebarView.js
+++ b/app/views/SidebarView.js
@@ -1,14 +1,13 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
- ScrollView, Text, View, StyleSheet, FlatList, LayoutAnimation, SafeAreaView
+ ScrollView, Text, View, StyleSheet, FlatList, LayoutAnimation, SafeAreaView, Image
} from 'react-native';
import { connect } from 'react-redux';
import Icon from 'react-native-vector-icons/MaterialIcons';
import equal from 'deep-equal';
import Navigation from '../lib/Navigation';
-import { setStackRoot as setStackRootAction } from '../actions';
import { logout as logoutAction } from '../actions/login';
import Avatar from '../containers/Avatar';
import Status from '../containers/status';
@@ -18,7 +17,8 @@ import RocketChat from '../lib/rocketchat';
import log from '../utils/log';
import I18n from '../i18n';
import scrollPersistTaps from '../utils/scrollPersistTaps';
-import { getReadableVersion } from '../utils/deviceInfo';
+import { getReadableVersion, isIOS, isAndroid } from '../utils/deviceInfo';
+import Icons from '../lib/Icons';
const styles = StyleSheet.create({
container: {
@@ -34,14 +34,14 @@ const styles = StyleSheet.create({
width: 30,
alignItems: 'center'
},
+ itemCenter: {
+ flex: 1
+ },
itemText: {
marginVertical: 16,
fontWeight: 'bold',
color: '#292E35'
},
- itemSelected: {
- backgroundColor: '#F7F8FA'
- },
separator: {
borderBottomWidth: StyleSheet.hairlineWidth,
borderColor: '#ddd',
@@ -79,13 +79,22 @@ const styles = StyleSheet.create({
fontWeight: '600',
color: '#292E35',
fontSize: 13
+ },
+ disclosureContainer: {
+ marginLeft: 6,
+ marginRight: 9,
+ alignItems: 'center',
+ justifyContent: 'center'
+ },
+ disclosureIndicator: {
+ width: 20,
+ height: 20
}
});
const keyExtractor = item => item.id;
@connect(state => ({
Site_Name: state.settings.Site_Name,
- stackRoot: state.app.stackRoot,
user: {
id: state.login.user && state.login.user.id,
language: state.login.user && state.login.user.language,
@@ -95,18 +104,27 @@ const keyExtractor = item => item.id;
},
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
}), dispatch => ({
- logout: () => dispatch(logoutAction()),
- setStackRoot: stackRoot => dispatch(setStackRootAction(stackRoot))
+ logout: () => dispatch(logoutAction())
}))
export default class Sidebar extends Component {
+ static options() {
+ return {
+ topBar: {
+ leftButtons: [{
+ id: 'cancel',
+ icon: isAndroid ? Icons.getSource('close', false) : undefined,
+ systemItem: 'cancel'
+ }]
+ }
+ };
+ }
+
static propTypes = {
baseUrl: PropTypes.string,
componentId: PropTypes.string,
Site_Name: PropTypes.string.isRequired,
- stackRoot: PropTypes.string.isRequired,
user: PropTypes.object,
- logout: PropTypes.func.isRequired,
- setStackRoot: PropTypes.func
+ logout: PropTypes.func.isRequired
}
constructor(props) {
@@ -131,18 +149,13 @@ export default class Sidebar extends Component {
shouldComponentUpdate(nextProps, nextState) {
const { status, showStatus } = this.state;
- const {
- Site_Name, stackRoot, user, baseUrl
- } = this.props;
+ const { Site_Name, user, baseUrl } = this.props;
if (nextState.showStatus !== showStatus) {
return true;
}
if (nextProps.Site_Name !== Site_Name) {
return true;
}
- if (nextProps.stackRoot !== stackRoot) {
- return true;
- }
if (nextProps.Site_Name !== Site_Name) {
return true;
}
@@ -166,11 +179,6 @@ export default class Sidebar extends Component {
return false;
}
- handleChangeStack = (event) => {
- const { stack } = event;
- this.setStack(stack);
- }
-
navigationButtonPressed = ({ buttonId }) => {
if (buttonId === 'cancel') {
const { componentId } = this.props;
@@ -196,37 +204,30 @@ export default class Sidebar extends Component {
});
}
- setStack = async(stack) => {
- const { stackRoot, setStackRoot } = this.props;
- if (stackRoot !== stack) {
- await Navigation.setStackRoot('AppRoot', {
- component: {
- id: stack,
- name: stack
- }
- });
- setStackRoot(stack);
- }
- }
-
- closeDrawer = () => {
- Navigation.toggleDrawer();
- }
-
toggleStatus = () => {
LayoutAnimation.easeInEaseOut();
this.setState(prevState => ({ showStatus: !prevState.showStatus }));
}
- sidebarNavigate = (stack) => {
- this.closeDrawer();
- this.setStack(stack);
+ sidebarNavigate = (route) => {
+ const { componentId } = this.props;
+ Navigation.push(componentId, {
+ component: {
+ name: route
+ }
+ });
+ }
+
+ logout = () => {
+ const { componentId, logout } = this.props;
+ Navigation.dismissModal(componentId);
+ logout();
}
renderSeparator = key => ;
renderItem = ({
- text, left, onPress, testID, current
+ text, left, onPress, testID, disclosure
}) => (
-
+
{left}
-
- {text}
-
+
+
+ {text}
+
+
+ {disclosure ? this.renderDisclosure() : null}
)
@@ -254,7 +258,6 @@ export default class Sidebar extends Component {
left: ,
current: user.status === item.id,
onPress: () => {
- this.closeDrawer();
this.toggleStatus();
if (user.status !== item.id) {
try {
@@ -268,43 +271,43 @@ export default class Sidebar extends Component {
);
}
- renderNavigation = () => {
- const { stackRoot } = this.props;
- const { logout } = this.props;
- return (
- [
- this.renderItem({
- text: I18n.t('Chats'),
- left: ,
- onPress: () => this.sidebarNavigate('RoomsListView'),
- testID: 'sidebar-chats',
- current: stackRoot === 'RoomsListView'
- }),
- this.renderItem({
- text: I18n.t('Profile'),
- left: ,
- onPress: () => this.sidebarNavigate('ProfileView'),
- testID: 'sidebar-profile',
- current: stackRoot === 'ProfileView'
- }),
- this.renderItem({
- text: I18n.t('Settings'),
- left: ,
- onPress: () => this.sidebarNavigate('SettingsView'),
- testID: 'sidebar-settings',
- current: stackRoot === 'SettingsView'
- }),
- this.renderSeparator('separator-logout'),
- this.renderItem({
- text: I18n.t('Logout'),
- left: ,
- onPress: () => logout(),
- testID: 'sidebar-logout'
- })
- ]
- );
+ // Remove it after https://github.com/RocketChat/Rocket.Chat.ReactNative/pull/643
+ renderDisclosure = () => {
+ if (isIOS) {
+ return (
+
+
+
+ );
+ }
}
+ renderNavigation = () => (
+ [
+ this.renderItem({
+ text: I18n.t('Profile'),
+ left: ,
+ onPress: () => this.sidebarNavigate('ProfileView'),
+ testID: 'sidebar-profile',
+ disclosure: true
+ }),
+ this.renderItem({
+ text: I18n.t('Settings'),
+ left: ,
+ onPress: () => this.sidebarNavigate('SettingsView'),
+ testID: 'sidebar-settings',
+ disclosure: true
+ }),
+ this.renderSeparator('separator-logout'),
+ this.renderItem({
+ text: I18n.t('Logout'),
+ left: ,
+ onPress: () => this.logout(),
+ testID: 'sidebar-logout'
+ })
+ ]
+ )
+
renderStatus = () => {
const { status } = this.state;
const { user } = this.props;
diff --git a/e2e/08-room.spec.js b/e2e/08-room.spec.js
index 6c25ee587..02fefdc52 100644
--- a/e2e/08-room.spec.js
+++ b/e2e/08-room.spec.js
@@ -169,7 +169,7 @@ describe('Room screen', () => {
await element(by.text(`${ data.random }message`)).longPress();
await waitFor(element(by.text('Message actions'))).toBeVisible().withTimeout(5000);
await expect(element(by.text('Message actions'))).toBeVisible();
- await element(by.text('Copy Permalink')).tap();
+ await element(by.text('Permalink')).tap();
await expect(element(by.text('Permalink copied to clipboard!'))).toBeVisible();
await waitFor(element(by.text('Permalink copied to clipboard!'))).toBeNotVisible().withTimeout(5000);
@@ -180,7 +180,7 @@ describe('Room screen', () => {
await element(by.text(`${ data.random }message`)).longPress();
await waitFor(element(by.text('Message actions'))).toBeVisible().withTimeout(5000);
await expect(element(by.text('Message actions'))).toBeVisible();
- await element(by.text('Copy Message')).tap();
+ await element(by.text('Copy')).tap();
await expect(element(by.text('Copied to clipboard!'))).toBeVisible();
await waitFor(element(by.text('Copied to clipboard!'))).toBeNotVisible().withTimeout(5000);
// TODO: test clipboard