diff --git a/app/actions/views/select_team.js b/app/actions/views/select_team.js
index 3e7d69f71..23ba2e8d1 100644
--- a/app/actions/views/select_team.js
+++ b/app/actions/views/select_team.js
@@ -7,6 +7,7 @@ import {markChannelAsRead, markChannelAsViewed} from 'mattermost-redux/actions/c
import {ChannelTypes, TeamTypes} from 'mattermost-redux/action_types';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels';
+import {RequestStatus} from 'mattermost-redux/constants';
import {NavigationTypes} from 'app/constants';
@@ -56,6 +57,8 @@ export function selectDefaultTeam() {
if (defaultTeam) {
handleTeamChange(defaultTeam.id)(dispatch, getState);
+ } else if (state.requests.teams.getTeams.status === RequestStatus.FAILURE || state.requests.teams.getMyTeams.status === RequestStatus.FAILURE) {
+ EventEmitter.emit(NavigationTypes.NAVIGATION_ERROR_TEAMS);
} else {
EventEmitter.emit(NavigationTypes.NAVIGATION_NO_TEAMS);
}
diff --git a/app/components/root/root.js b/app/components/root/root.js
index a4438a0f9..5566e7335 100644
--- a/app/components/root/root.js
+++ b/app/components/root/root.js
@@ -30,6 +30,7 @@ export default class Root extends PureComponent {
EventEmitter.on(ViewTypes.NOTIFICATION_IN_APP, this.handleInAppNotification);
EventEmitter.on(ViewTypes.NOTIFICATION_TAPPED, this.handleNotificationTapped);
EventEmitter.on(NavigationTypes.NAVIGATION_NO_TEAMS, this.handleNoTeams);
+ EventEmitter.on(NavigationTypes.NAVIGATION_ERROR_TEAMS, this.errorTeamsList);
}
}
@@ -44,6 +45,7 @@ export default class Root extends PureComponent {
EventEmitter.off(ViewTypes.NOTIFICATION_IN_APP, this.handleInAppNotification);
EventEmitter.off(ViewTypes.NOTIFICATION_TAPPED, this.handleNotificationTapped);
EventEmitter.off(NavigationTypes.NAVIGATION_NO_TEAMS, this.handleNoTeams);
+ EventEmitter.off(NavigationTypes.NAVIGATION_ERROR_TEAMS, this.errorTeamsList);
}
}
@@ -69,11 +71,23 @@ export default class Root extends PureComponent {
setTimeout(this.handleNoTeams, 200);
return;
}
+ this.navigateToTeamsPage('SelectTeam');
+ };
+ errorTeamsList = () => {
+ if (!this.refs.provider) {
+ setTimeout(this.errorTeamsList, 200);
+ return;
+ }
+ this.navigateToTeamsPage('ErrorTeamsList');
+ }
+
+ navigateToTeamsPage = (screen) => {
const {currentUrl, navigator, theme} = this.props;
const {intl} = this.refs.provider.getChildContext();
let navigatorButtons;
+ let passProps = {theme};
if (Platform.OS === 'android') {
navigatorButtons = {
rightButtons: [{
@@ -93,8 +107,16 @@ export default class Root extends PureComponent {
};
}
+ if (screen === 'SelectTeam') {
+ passProps = {
+ ...passProps,
+ currentUrl,
+ userWithoutTeams: true,
+ };
+ }
+
navigator.resetTo({
- screen: 'SelectTeam',
+ screen,
title: intl.formatMessage({id: 'mobile.routes.selectTeam', defaultMessage: 'Select Team'}),
animated: false,
backButtonTitle: '',
@@ -105,13 +127,9 @@ export default class Root extends PureComponent {
screenBackgroundColor: theme.centerChannelBg,
},
navigatorButtons,
- passProps: {
- currentUrl,
- userWithoutTeams: true,
- theme,
- },
+ passProps,
});
- };
+ }
handleNotificationTapped = async () => {
const {navigator} = this.props;
diff --git a/app/constants/navigation.js b/app/constants/navigation.js
index e781be3c3..809997da3 100644
--- a/app/constants/navigation.js
+++ b/app/constants/navigation.js
@@ -8,6 +8,7 @@ const NavigationTypes = keyMirror({
NAVIGATION_CLOSE_MODAL: null,
NAVIGATION_NO_TEAMS: null,
RESTART_APP: null,
+ NAVIGATION_ERROR_TEAMS: null,
});
export default NavigationTypes;
diff --git a/app/screens/error_teams_list/__snapshots__/error_teams_list.test.js.snap b/app/screens/error_teams_list/__snapshots__/error_teams_list.test.js.snap
new file mode 100644
index 000000000..ddb3c9ca7
--- /dev/null
+++ b/app/screens/error_teams_list/__snapshots__/error_teams_list.test.js.snap
@@ -0,0 +1,166 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ErrorTeamsList should match snapshot 1`] = `
+ShallowWrapper {
+ "length": 1,
+ Symbol(enzyme.__root__): [Circular],
+ Symbol(enzyme.__unrendered__): ,
+ Symbol(enzyme.__renderer__): Object {
+ "batchedUpdates": [Function],
+ "getNode": [Function],
+ "render": [Function],
+ "simulateEvent": [Function],
+ "unmount": [Function],
+ },
+ Symbol(enzyme.__node__): Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+ ,
+ ,
+ ],
+ "style": Object {
+ "backgroundColor": undefined,
+ "flex": 1,
+ },
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {},
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "errorDescription": Object {
+ "defaultMessage": "Make sure you have an active connection and try again.",
+ "id": "mobile.failed_network_action.shortDescription",
+ },
+ "errorTitle": Object {
+ "defaultMessage": "Team Not Found",
+ "id": "error.team_not_found.title",
+ },
+ "onRetry": [Function],
+ "theme": Object {},
+ },
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ Symbol(enzyme.__nodes__): Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+ ,
+ ,
+ ],
+ "style": Object {
+ "backgroundColor": undefined,
+ "flex": 1,
+ },
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {},
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "errorDescription": Object {
+ "defaultMessage": "Make sure you have an active connection and try again.",
+ "id": "mobile.failed_network_action.shortDescription",
+ },
+ "errorTitle": Object {
+ "defaultMessage": "Team Not Found",
+ "id": "error.team_not_found.title",
+ },
+ "onRetry": [Function],
+ "theme": Object {},
+ },
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ ],
+ Symbol(enzyme.__options__): Object {
+ "adapter": ReactSixteenAdapter {
+ "options": Object {
+ "enableComponentDidUpdateOnSetState": true,
+ },
+ },
+ },
+}
+`;
diff --git a/app/screens/error_teams_list/error_teams_list.js b/app/screens/error_teams_list/error_teams_list.js
new file mode 100644
index 000000000..a01257b06
--- /dev/null
+++ b/app/screens/error_teams_list/error_teams_list.js
@@ -0,0 +1,116 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React, {PureComponent} from 'react';
+import PropTypes from 'prop-types';
+import {
+ InteractionManager,
+ View,
+} from 'react-native';
+
+import FailedNetworkAction from 'app/components/failed_network_action';
+import Loading from 'app/components/loading';
+import StatusBar from 'app/components/status_bar';
+import {makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
+
+const errorTitle = {
+ id: 'error.team_not_found.title',
+ defaultMessage: 'Team Not Found',
+};
+
+const errorDescription = {
+ id: 'mobile.failed_network_action.shortDescription',
+ defaultMessage: 'Make sure you have an active connection and try again.',
+};
+
+export default class ErrorTeamsList extends PureComponent {
+ static propTypes = {
+ actions: PropTypes.shape({
+ loadMe: PropTypes.func.isRequired,
+ connection: PropTypes.func.isRequired,
+ logout: PropTypes.func.isRequired,
+ selectDefaultTeam: PropTypes.func.isRequired,
+ }).isRequired,
+ navigator: PropTypes.object,
+ theme: PropTypes.object,
+ };
+
+ constructor(props) {
+ super(props);
+ props.navigator.setOnNavigatorEvent(this.onNavigatorEvent);
+
+ this.state = {
+ loading: false,
+ };
+ }
+
+ componentWillReceiveProps(nextProps) {
+ if (this.props.theme !== nextProps.theme) {
+ setNavigatorStyles(this.props.navigator, nextProps.theme);
+ }
+ }
+
+ goToChannelView = () => {
+ const {navigator, theme} = this.props;
+
+ navigator.resetTo({
+ screen: 'Channel',
+ animated: false,
+ navigatorStyle: {
+ navBarHidden: true,
+ statusBarHidden: false,
+ statusBarHideWithNavBar: false,
+ screenBackgroundColor: theme.centerChannelBg,
+ },
+ });
+ };
+
+ getUserInfo = async () => {
+ this.setState({loading: true});
+ this.props.actions.connection(true);
+ await this.props.actions.loadMe();
+ this.props.actions.connection(false);
+ this.setState({loading: false});
+ this.props.actions.selectDefaultTeam();
+ this.goToChannelView();
+ }
+
+ onNavigatorEvent = (event) => {
+ if (event.type === 'NavBarButtonPress') {
+ const {logout} = this.props.actions;
+ if (event.id === 'logout') {
+ InteractionManager.runAfterInteractions(logout);
+ }
+ }
+ };
+
+ render() {
+ const {theme} = this.props;
+ const styles = getStyleSheet(theme);
+
+ if (this.state.loading) {
+ return ;
+ }
+
+ return (
+
+
+
+
+ );
+ }
+}
+
+const getStyleSheet = makeStyleSheetFromTheme((theme) => {
+ return {
+ container: {
+ backgroundColor: theme.centerChannelBg,
+ flex: 1,
+ },
+ };
+});
diff --git a/app/screens/error_teams_list/error_teams_list.test.js b/app/screens/error_teams_list/error_teams_list.test.js
new file mode 100644
index 000000000..11a2bfc49
--- /dev/null
+++ b/app/screens/error_teams_list/error_teams_list.test.js
@@ -0,0 +1,65 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React from 'react';
+import {configure, shallow} from 'enzyme';
+import Adapter from 'enzyme-adapter-react-16';
+import FailedNetworkAction from 'app/components/failed_network_action';
+import ErrorTeamsList from './error_teams_list';
+
+configure({adapter: new Adapter()});
+
+describe('ErrorTeamsList', () => {
+ const navigator = {
+ setOnNavigatorEvent: () => {}, // eslint-disable-line no-empty-function
+ };
+
+ const loadMe = async () => {
+ return {
+ data: {},
+ };
+ };
+
+ const baseProps = {
+ actions: {
+ loadMe: () => {}, // eslint-disable-line no-empty-function
+ connection: () => {}, // eslint-disable-line no-empty-function
+ logout: () => {}, // eslint-disable-line no-empty-function
+ selectDefaultTeam: () => {}, // eslint-disable-line no-empty-function
+ },
+ theme: {},
+ navigator,
+ };
+
+ test('should match snapshot', () => {
+ const wrapper = shallow(
+
+ );
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ test('should call for userInfo on retry', async () => {
+ const connection = jest.fn();
+ const selectDefaultTeam = jest.fn();
+ const logout = jest.fn();
+ const actions = {
+ loadMe,
+ logout,
+ selectDefaultTeam,
+ connection,
+ };
+
+ const newProps = {
+ ...baseProps,
+ actions,
+ };
+
+ const wrapper = shallow(
+
+ );
+
+ wrapper.find(FailedNetworkAction).props().onRetry();
+ await loadMe();
+ expect(selectDefaultTeam).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/app/screens/error_teams_list/index.js b/app/screens/error_teams_list/index.js
new file mode 100644
index 000000000..31424ed03
--- /dev/null
+++ b/app/screens/error_teams_list/index.js
@@ -0,0 +1,24 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import {bindActionCreators} from 'redux';
+import {connect} from 'react-redux';
+
+import {logout, loadMe} from 'mattermost-redux/actions/users';
+import {connection} from 'app/actions/device';
+import {selectDefaultTeam} from 'app/actions/views/select_team';
+
+import ErrorTeamsList from './error_teams_list.js';
+
+function mapDispatchToProps(dispatch) {
+ return {
+ actions: bindActionCreators({
+ logout,
+ selectDefaultTeam,
+ connection,
+ loadMe,
+ }, dispatch),
+ };
+}
+
+export default connect(null, mapDispatchToProps)(ErrorTeamsList);
diff --git a/app/screens/index.js b/app/screens/index.js
index 958bc1b77..c6deec65f 100644
--- a/app/screens/index.js
+++ b/app/screens/index.js
@@ -58,5 +58,6 @@ export function registerScreens(store, Provider) {
Navigation.registerComponent('TextPreview', () => wrapWithContextProvider(require('app/screens/text_preview').default), store, Provider);
Navigation.registerComponent('Thread', () => wrapWithContextProvider(require('app/screens/thread').default), store, Provider);
Navigation.registerComponent('TimezoneSettings', () => wrapWithContextProvider(require('app/screens/timezone').default), store, Provider);
+ Navigation.registerComponent('ErrorTeamsList', () => wrapWithContextProvider(require('app/screens/error_teams_list').default), store, Provider);
Navigation.registerComponent('UserProfile', () => wrapWithContextProvider(require('app/screens/user_profile').default), store, Provider);
}