Handle bad state (#1903)

* Upgrade mattermost-redux

* Check if url start with http before pinging

* Remove required status prop in settings sidebar

* Prevent the app to get into a bad state and if that happens try and recover
This commit is contained in:
Elias Nahum 2018-07-09 18:33:13 -04:00 committed by GitHub
parent 360c8cd2e3
commit 01792f2b54
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 69 additions and 17 deletions

View file

@ -91,12 +91,17 @@ export default class App {
const [deviceToken, currentUserId] = usernameParsed;
const [token, url] = passwordParsed;
this.deviceToken = deviceToken;
this.currentUserId = currentUserId;
this.token = token;
this.url = url;
Client4.setUrl(url);
Client4.setToken(token);
// if for any case the url and the token aren't valid proceed with re-hydration
if (url && url !== 'undefined' && token && token !== 'undefined') {
this.deviceToken = deviceToken;
this.currentUserId = currentUserId;
this.token = token;
this.url = url;
Client4.setUrl(url);
Client4.setToken(token);
} else {
this.waitForRehydration = true;
}
}
}
} catch (error) {
@ -161,7 +166,10 @@ export default class App {
this.url = url;
}
setGenericPassword(username, password);
// Only save to keychain if the url and token are set
if (url && token) {
setGenericPassword(username, password);
}
};
setStartupThemes = (toolbarBackground, toolbarTextColor, appBackground) => {

View file

@ -41,12 +41,13 @@ export default class SettingsDrawer extends PureComponent {
currentUser: PropTypes.object.isRequired,
deviceWidth: PropTypes.number.isRequired,
navigator: PropTypes.object,
status: PropTypes.string.isRequired,
status: PropTypes.string,
theme: PropTypes.object.isRequired,
};
static defaultProps = {
currentUser: {},
status: 'offline',
};
static contextTypes = {

View file

@ -5,6 +5,7 @@ import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {intlShape} from 'react-intl';
import {
Alert,
AppState,
Dimensions,
Platform,
@ -22,6 +23,7 @@ import KeyboardLayout from 'app/components/layout/keyboard_layout';
import OfflineIndicator from 'app/components/offline_indicator';
import SafeAreaView from 'app/components/safe_area_view';
import StatusBar from 'app/components/status_bar';
import {ViewTypes} from 'app/constants';
import mattermostBucket from 'app/mattermost_bucket';
import {preventDoubleTap} from 'app/utils/tap';
import {makeStyleSheetFromTheme} from 'app/utils/theme';
@ -33,7 +35,6 @@ import LocalConfig from 'assets/config';
import ChannelNavBar from './channel_nav_bar';
import ChannelPostList from './channel_post_list';
import {ViewTypes} from 'app/constants';
const {
ANDROID_TOP_LANDSCAPE,
ANDROID_TOP_PORTRAIT,
@ -50,6 +51,7 @@ export default class Channel extends PureComponent {
connection: PropTypes.func.isRequired,
loadChannelsIfNecessary: PropTypes.func.isRequired,
loadProfilesAndTeamMembersForDMSidebar: PropTypes.func.isRequired,
logout: PropTypes.func.isRequired,
selectDefaultTeam: PropTypes.func.isRequired,
selectInitialChannel: PropTypes.func.isRequired,
initWebSocket: PropTypes.func.isRequired,
@ -236,8 +238,12 @@ export default class Channel extends PureComponent {
handleConnectionChange = (isConnected) => {
const {connection} = this.props.actions;
this.handleWebSocket(isConnected);
connection(isConnected);
// Prevent for being called more than once.
if (this.isConnected !== isConnected) {
this.isConnected = isConnected;
this.handleWebSocket(isConnected);
connection(isConnected);
}
};
handleLeaveTeam = () => {
@ -245,6 +251,7 @@ export default class Channel extends PureComponent {
};
initializeWebSocket = async () => {
const {formatMessage} = this.context.intl;
const {actions} = this.props;
const {initWebSocket} = actions;
const platform = Platform.OS;
@ -253,7 +260,25 @@ export default class Channel extends PureComponent {
certificate = await mattermostBucket.getPreference('cert', LocalConfig.AppGroupId);
}
initWebSocket(platform, null, null, null, {certificate});
initWebSocket(platform, null, null, null, {certificate}).catch(() => {
// we should dispatch a failure and show the app as disconnected
Alert.alert(
formatMessage({id: 'mobile.authentication_error.title', defaultMessage: 'Authentication Error'}),
formatMessage({
id: 'mobile.authentication_error.message',
defaultMessage: 'Mattermost has encountered an error. Please re-authenticate to start a new session.',
}),
[{
text: formatMessage({
id: 'navbar_dropdown.logout',
defaultMessage: 'Logout',
}),
onPress: actions.logout,
}],
{cancelable: false}
);
this.props.actions.closeWebSocket(true);
});
};
loadChannels = (teamId) => {

View file

@ -4,7 +4,7 @@
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {startPeriodicStatusUpdates, stopPeriodicStatusUpdates} from 'mattermost-redux/actions/users';
import {startPeriodicStatusUpdates, stopPeriodicStatusUpdates, logout} from 'mattermost-redux/actions/users';
import {init as initWebSocket, close as closeWebSocket} from 'mattermost-redux/actions/websocket';
import {RequestStatus} from 'mattermost-redux/constants';
import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels';
@ -41,6 +41,7 @@ function mapDispatchToProps(dispatch) {
connection,
loadChannelsIfNecessary,
loadProfilesAndTeamMembersForDMSidebar,
logout,
selectDefaultTeam,
selectInitialChannel,
initWebSocket,

View file

@ -111,6 +111,20 @@ function resetStateForNewVersion(action) {
lastTeamId = payload.views.team.lastTeamId;
}
const currentChannelId = lastChannelForTeam[lastTeamId] && lastChannelForTeam[lastTeamId].length ? lastChannelForTeam[lastTeamId][0] : '';
let channels = initialState.entities.channels;
if (payload.entities.channels && currentChannelId) {
channels = {
currentChannelId,
channels: {
[currentChannelId]: payload.entities.channels.channels[currentChannelId],
},
myMembers: {
[currentChannelId]: payload.entities.channels.myMembers[currentChannelId],
},
};
}
let threadDrafts = initialState.views.thread.drafts;
if (payload.views.thread && payload.views.thread.drafts) {
threadDrafts = payload.views.thread.drafts;
@ -132,6 +146,7 @@ function resetStateForNewVersion(action) {
version: DeviceInfo.getVersion(),
},
entities: {
channels,
general,
teams,
users,

View file

@ -9,7 +9,7 @@ import {Client4} from 'mattermost-redux/client';
const PING_TIMEOUT = 10000;
export async function checkConnection(isConnected) {
if (Client4.getBaseRoute() === '/api/v4') {
if (!Client4.getBaseRoute().startsWith('http')) {
// If we don't have a server yet, return the default implementation
return isConnected;
}

View file

@ -2309,6 +2309,8 @@
"mobile.announcement_banner.dismiss": "Dismiss",
"mobile.announcement_banner.ok": "OK",
"mobile.announcement_banner.title": "Announcement",
"mobile.authentication_error.title": "Authentication Error",
"mobile.authentication_error.message": "Mattermost has encountered an error. Please re-authenticate to start a new session.",
"mobile.channel.markAsRead": "Mark As Read",
"mobile.channel_drawer.search": "Jump to...",
"mobile.channel_info.alertMessageDeleteChannel": "Are you sure you want to archive the {term} {name}?",

4
package-lock.json generated
View file

@ -10100,8 +10100,8 @@
}
},
"mattermost-redux": {
"version": "github:mattermost/mattermost-redux#9d922ba3f3ed1264f192cbe3c7f0030d249756e3",
"from": "github:mattermost/mattermost-redux#9d922ba3f3ed1264f192cbe3c7f0030d249756e3",
"version": "github:mattermost/mattermost-redux#ade5924a6e509ea76757bd78a2cc193043a584ce",
"from": "github:mattermost/mattermost-redux#ade5924a6e509ea76757bd78a2cc193043a584ce",
"requires": {
"deep-equal": "1.0.1",
"eslint-plugin-header": "1.2.0",

View file

@ -15,7 +15,7 @@
"intl": "1.2.5",
"jail-monkey": "1.0.0",
"jsc-android": "216113.0.3",
"mattermost-redux": "github:mattermost/mattermost-redux#9d922ba3f3ed1264f192cbe3c7f0030d249756e3",
"mattermost-redux": "github:mattermost/mattermost-redux#ade5924a6e509ea76757bd78a2cc193043a584ce",
"mime-db": "1.33.0",
"prop-types": "15.6.1",
"react": "16.3.2",