From 23e58f2ea18752dd89bc34cadd66a747f4bb8164 Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Thu, 7 Feb 2019 18:42:26 -0300 Subject: [PATCH] MM-13830 Ensure schedule of session expired notification (#2554) * Ensure schedule of session expired notification * feedback review * passing intl instead of message --- app/actions/views/login.js | 54 ++++++++++++++-------- app/screens/login/login.js | 28 +++++------ app/screens/select_server/index.js | 4 +- app/screens/select_server/select_server.js | 27 ++++------- app/screens/sso/index.js | 4 +- app/screens/sso/sso.js | 30 +++++------- 6 files changed, 71 insertions(+), 76 deletions(-) diff --git a/app/actions/views/login.js b/app/actions/views/login.js index 8579c66b1..3f3915bfc 100644 --- a/app/actions/views/login.js +++ b/app/actions/views/login.js @@ -11,6 +11,7 @@ import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users'; import {ViewTypes} from 'app/constants'; import {app} from 'app/mattermost'; +import PushNotifications from 'app/push_notifications'; import {getDeviceTimezone, isTimezoneEnabled} from 'app/utils/timezone'; export function handleLoginIdChanged(loginId) { @@ -67,31 +68,46 @@ export function handleSuccessfulLogin() { }; } -export function getSession() { - return async (dispatch, getState) => { +export function scheduleExpiredNotification(intl) { + return (dispatch, getState) => { const state = getState(); const {currentUserId} = state.entities.users; const {deviceToken} = state.entities.general; + const message = intl.formatMessage({ + id: 'mobile.session_expired', + defaultMessage: 'Session Expired: Please log in to continue receiving notifications.', + }); - if (!currentUserId || !deviceToken) { - return 0; - } + // Once the user logs in we are going to wait for 10 seconds + // before retrieving the session that belongs to this device + // to ensure that we get the actual session without issues + // then we can schedule the local notification for the session expired + setTimeout(async () => { + let sessions; + try { + sessions = await dispatch(getSessions(currentUserId)); + } catch (e) { + console.warn('Failed to get current session', e); // eslint-disable-line no-console + return; + } - let sessions; - try { - sessions = await dispatch(getSessions(currentUserId)); - } catch (e) { - console.warn('Failed to get current session', e); // eslint-disable-line no-console - return 0; - } + if (!Array.isArray(sessions.data)) { + return; + } - if (!Array.isArray(sessions.data)) { - return 0; - } + const session = sessions.data.find((s) => s.device_id === deviceToken); + const expiresAt = session?.expires_at || 0; //eslint-disable-line camelcase - const session = sessions.data.find((s) => s.device_id === deviceToken); - - return session && session.expires_at ? session.expires_at : 0; + if (expiresAt) { + PushNotifications.localNotificationSchedule({ + date: new Date(expiresAt), + message, + userInfo: { + localNotification: true, + }, + }); + } + }, 10000); }; } @@ -99,5 +115,5 @@ export default { handleLoginIdChanged, handlePasswordChanged, handleSuccessfulLogin, - getSession, + scheduleExpiredNotification, }; diff --git a/app/screens/login/login.js b/app/screens/login/login.js index 1171c686d..3f5e03efe 100644 --- a/app/screens/login/login.js +++ b/app/screens/login/login.js @@ -22,7 +22,6 @@ import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view'; import ErrorText from 'app/components/error_text'; import FormattedText from 'app/components/formatted_text'; import StatusBar from 'app/components/status_bar'; -import PushNotifications from 'app/push_notifications'; import {GlobalStyles} from 'app/styles'; import {preventDoubleTap} from 'app/utils/tap'; import tracker from 'app/utils/time_tracker'; @@ -38,7 +37,7 @@ export default class Login extends PureComponent { handleLoginIdChanged: PropTypes.func.isRequired, handlePasswordChanged: PropTypes.func.isRequired, handleSuccessfulLogin: PropTypes.func.isRequired, - getSession: PropTypes.func.isRequired, + scheduleExpiredNotification: PropTypes.func.isRequired, checkMfa: PropTypes.func.isRequired, login: PropTypes.func.isRequired, }).isRequired, @@ -68,7 +67,7 @@ export default class Login extends PureComponent { componentWillReceiveProps(nextProps) { if (this.props.loginRequest.status === RequestStatus.STARTED && nextProps.loginRequest.status === RequestStatus.SUCCESS) { - this.props.actions.handleSuccessfulLogin().then(this.props.actions.getSession).then(this.goToChannel); + this.props.actions.handleSuccessfulLogin().then(this.goToChannel); } else if (this.props.loginRequest.status !== nextProps.loginRequest.status && nextProps.loginRequest.status !== RequestStatus.STARTED) { this.setState({isLoading: false}); } @@ -78,23 +77,11 @@ export default class Login extends PureComponent { Dimensions.removeEventListener('change', this.orientationDidChange); } - goToChannel = (expiresAt) => { - const {intl} = this.context; + goToChannel = () => { const {navigator} = this.props; tracker.initialLoad = Date.now(); - if (expiresAt) { - PushNotifications.localNotificationSchedule({ - date: new Date(expiresAt), - message: intl.formatMessage({ - id: 'mobile.session_expired', - defaultMessage: 'Session Expired: Please log in to continue receiving notifications.', - }), - userInfo: { - localNotification: true, - }, - }); - } + this.scheduleSessionExpiredNotification(); navigator.resetTo({ screen: 'Channel', @@ -208,6 +195,13 @@ export default class Login extends PureComponent { }); }); + scheduleSessionExpiredNotification = () => { + const {intl} = this.context; + const {actions} = this.props; + + actions.scheduleExpiredNotification(intl); + }; + signIn = () => { const {actions, loginId, loginRequest, password} = this.props; if (loginRequest.status !== RequestStatus.STARTED) { diff --git a/app/screens/select_server/index.js b/app/screens/select_server/index.js index 386e7fca5..99ef53694 100644 --- a/app/screens/select_server/index.js +++ b/app/screens/select_server/index.js @@ -8,7 +8,7 @@ import {getPing, resetPing, setServerVersion} from 'mattermost-redux/actions/gen import {login} from 'mattermost-redux/actions/users'; import {setLastUpgradeCheck} from 'app/actions/views/client_upgrade'; -import {getSession, handleSuccessfulLogin} from 'app/actions/views/login'; +import {handleSuccessfulLogin, scheduleExpiredNotification} from 'app/actions/views/login'; import {loadConfigAndLicense} from 'app/actions/views/root'; import {handleServerUrlChanged} from 'app/actions/views/select_server'; import getClientUpgrade from 'app/selectors/client_upgrade'; @@ -39,7 +39,7 @@ function mapDispatchToProps(dispatch) { actions: bindActionCreators({ handleSuccessfulLogin, getPing, - getSession, + scheduleExpiredNotification, handleServerUrlChanged, loadConfigAndLicense, login, diff --git a/app/screens/select_server/select_server.js b/app/screens/select_server/select_server.js index 3a29af689..aea4fd042 100644 --- a/app/screens/select_server/select_server.js +++ b/app/screens/select_server/select_server.js @@ -28,7 +28,6 @@ import FormattedText from 'app/components/formatted_text'; import {UpgradeTypes} from 'app/constants'; import fetchConfig from 'app/fetch_preconfig'; import mattermostBucket from 'app/mattermost_bucket'; -import PushNotifications from 'app/push_notifications'; import {GlobalStyles} from 'app/styles'; import checkUpgradeType from 'app/utils/client_upgrade'; import {isValidUrl, stripTrailingSlashes} from 'app/utils/url'; @@ -44,7 +43,7 @@ export default class SelectServer extends PureComponent { getPing: PropTypes.func.isRequired, handleServerUrlChanged: PropTypes.func.isRequired, handleSuccessfulLogin: PropTypes.func.isRequired, - getSession: PropTypes.func.isRequired, + scheduleExpiredNotification: PropTypes.func.isRequired, loadConfigAndLicense: PropTypes.func.isRequired, login: PropTypes.func.isRequired, resetPing: PropTypes.func.isRequired, @@ -283,26 +282,13 @@ export default class SelectServer extends PureComponent { }; loginWithCertificate = async () => { - const {intl, navigator} = this.props; + const {navigator} = this.props; tracker.initialLoad = Date.now(); await this.props.actions.login('credential', 'password'); await this.props.actions.handleSuccessfulLogin(); - const expiresAt = await this.props.actions.getSession(); - - if (expiresAt) { - PushNotifications.localNotificationSchedule({ - date: new Date(expiresAt), - message: intl.formatMessage({ - id: 'mobile.session_expired', - defaultMessage: 'Session Expired: Please log in to continue receiving notifications.', - }), - userInfo: { - localNotification: true, - }, - }); - } + this.scheduleSessionExpiredNotification(); navigator.resetTo({ screen: 'Channel', @@ -380,6 +366,13 @@ export default class SelectServer extends PureComponent { }); }; + scheduleSessionExpiredNotification = () => { + const {intl} = this.context; + const {actions} = this.props; + + actions.scheduleExpiredNotification(intl); + }; + selectCertificate = () => { const url = this.getUrl(); RNFetchBlob.cba.selectCertificate((certificate) => { diff --git a/app/screens/sso/index.js b/app/screens/sso/index.js index 95592e03b..f17246b5b 100644 --- a/app/screens/sso/index.js +++ b/app/screens/sso/index.js @@ -4,7 +4,7 @@ import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; -import {getSession, handleSuccessfulLogin} from 'app/actions/views/login'; +import {handleSuccessfulLogin, scheduleExpiredNotification} from 'app/actions/views/login'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {setStoreFromLocalData} from 'mattermost-redux/actions/general'; @@ -21,7 +21,7 @@ function mapStateToProps(state) { function mapDispatchToProps(dispatch) { return { actions: bindActionCreators({ - getSession, + scheduleExpiredNotification, handleSuccessfulLogin, setStoreFromLocalData, }, dispatch), diff --git a/app/screens/sso/sso.js b/app/screens/sso/sso.js index ac6b4d914..eaa7a33a5 100644 --- a/app/screens/sso/sso.js +++ b/app/screens/sso/sso.js @@ -18,7 +18,6 @@ import {Client4} from 'mattermost-redux/client'; import {ViewTypes} from 'app/constants'; import Loading from 'app/components/loading'; import StatusBar from 'app/components/status_bar'; -import PushNotifications from 'app/push_notifications'; import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; import tracker from 'app/utils/time_tracker'; @@ -66,7 +65,7 @@ class SSO extends PureComponent { serverUrl: PropTypes.string.isRequired, ssoType: PropTypes.string.isRequired, actions: PropTypes.shape({ - getSession: PropTypes.func.isRequired, + scheduleExpiredNotification: PropTypes.func.isRequired, handleSuccessfulLogin: PropTypes.func.isRequired, setStoreFromLocalData: PropTypes.func.isRequired, }).isRequired, @@ -104,22 +103,11 @@ class SSO extends PureComponent { }); }; - goToLoadTeam = (expiresAt) => { - const {intl, navigator} = this.props; + goToChannel = () => { + const {navigator} = this.props; tracker.initialLoad = Date.now(); - if (expiresAt) { - PushNotifications.localNotificationSchedule({ - date: new Date(expiresAt), - message: intl.formatMessage({ - id: 'mobile.session_expired', - defaultMessage: 'Session Expired: Please log in to continue receiving notifications.', - }), - userInfo: { - localNotification: true, - }, - }); - } + this.scheduleSessionExpiredNotification(); navigator.resetTo({ screen: 'Channel', @@ -182,7 +170,6 @@ class SSO extends PureComponent { if (token) { this.setState({renderWebView: false}); const { - getSession, handleSuccessfulLogin, setStoreFromLocalData, } = this.props.actions; @@ -190,8 +177,7 @@ class SSO extends PureComponent { Client4.setToken(token); setStoreFromLocalData({url: Client4.getUrl(), token}). then(handleSuccessfulLogin). - then(getSession). - then(this.goToLoadTeam). + then(this.goToChannel). catch(this.onLoadEndError); } else if (this.webView && !this.state.error) { this.webView.injectJavaScript(postMessageJS); @@ -205,6 +191,12 @@ class SSO extends PureComponent { this.setState({error: e.message}); }; + scheduleSessionExpiredNotification = () => { + const {actions, intl} = this.props; + + actions.scheduleExpiredNotification(intl); + }; + renderLoading = () => { return ; };