From 6df0f8b9150cf5f9450f40587d8a492189726e7c Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Thu, 22 Nov 2018 11:09:55 -0300 Subject: [PATCH] Set the user status as offline when the app loses connection (#2366) --- app/actions/views/user.js | 25 +++++++++ app/actions/views/user.test.js | 53 +++++++++++++++++++ app/components/network_indicator/index.js | 2 + .../network_indicator/network_indicator.js | 16 ++++-- 4 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 app/actions/views/user.js create mode 100644 app/actions/views/user.test.js diff --git a/app/actions/views/user.js b/app/actions/views/user.js new file mode 100644 index 000000000..fd3579a39 --- /dev/null +++ b/app/actions/views/user.js @@ -0,0 +1,25 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {UserTypes} from 'mattermost-redux/action_types'; +import {getStatus} from 'mattermost-redux/actions/users'; +import {General} from 'mattermost-redux/constants'; +import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users'; + +export function setCurrentUserStatus(isOnline) { + return (dispatch, getState) => { + const currentUserId = getCurrentUserId(getState()); + + if (isOnline) { + return dispatch(getStatus(currentUserId)); + } + + return dispatch({ + type: UserTypes.RECEIVED_STATUS, + data: { + user_id: currentUserId, + status: General.OFFLINE, + }, + }); + }; +} diff --git a/app/actions/views/user.test.js b/app/actions/views/user.test.js new file mode 100644 index 000000000..8e9a9618c --- /dev/null +++ b/app/actions/views/user.test.js @@ -0,0 +1,53 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import configureStore from 'redux-mock-store'; +import thunk from 'redux-thunk'; + +import {UserTypes} from 'mattermost-redux/action_types'; +import {General} from 'mattermost-redux/constants'; + +import {setCurrentUserStatus} from 'app/actions/views/user'; + +const mockStore = configureStore([thunk]); + +jest.mock('mattermost-redux/actions/users', () => ({ + getStatus: (...args) => ({type: 'MOCK_GET_STATUS', args}), +})); + +describe('Actions.Views.User', () => { + let store; + + beforeEach(() => { + store = mockStore({ + entities: { + users: { + currentUserId: 'current-user-id', + }, + }, + }); + }); + + test('should set the current user as offline', async () => { + const action = { + type: UserTypes.RECEIVED_STATUS, + data: { + user_id: 'current-user-id', + status: General.OFFLINE, + }, + }; + + await store.dispatch(setCurrentUserStatus(false)); + expect(store.getActions()).toEqual([action]); + }); + + test('should fetch the current user status from the server', async () => { + const action = { + type: 'MOCK_GET_STATUS', + args: ['current-user-id'], + }; + + await store.dispatch(setCurrentUserStatus(true)); + expect(store.getActions()).toEqual([action]); + }); +}); diff --git a/app/components/network_indicator/index.js b/app/components/network_indicator/index.js index f4c5dff71..ce36df7e8 100644 --- a/app/components/network_indicator/index.js +++ b/app/components/network_indicator/index.js @@ -9,6 +9,7 @@ import {init as initWebSocket, close as closeWebSocket} from 'mattermost-redux/a import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels'; import {connection} from 'app/actions/device'; +import {setCurrentUserStatus} from 'app/actions/views/user'; import {getConnection, isLandscape} from 'app/selectors/device'; import NetworkIndicator from './network_indicator'; @@ -33,6 +34,7 @@ function mapDispatchToProps(dispatch) { connection, initWebSocket, logout, + setCurrentUserStatus, startPeriodicStatusUpdates, stopPeriodicStatusUpdates, }, dispatch), diff --git a/app/components/network_indicator/network_indicator.js b/app/components/network_indicator/network_indicator.js index f4267a3c6..3d8faf170 100644 --- a/app/components/network_indicator/network_indicator.js +++ b/app/components/network_indicator/network_indicator.js @@ -46,6 +46,7 @@ export default class NetworkIndicator extends PureComponent { connection: PropTypes.func.isRequired, initWebSocket: PropTypes.func.isRequired, logout: PropTypes.func.isRequired, + setCurrentUserStatus: PropTypes.func.isRequired, startPeriodicStatusUpdates: PropTypes.func.isRequired, stopPeriodicStatusUpdates: PropTypes.func.isRequired, }).isRequired, @@ -124,21 +125,25 @@ export default class NetworkIndicator extends PureComponent { } connect = (displayBar = false) => { + const {connection} = this.props.actions; clearTimeout(this.connectionRetryTimeout); NetInfo.isConnected.fetch().then(async (isConnected) => { const {hasInternet, serverReachable} = await checkConnection(isConnected); - this.props.actions.connection(hasInternet); + connection(hasInternet); + this.hasInternet = hasInternet; + this.serverReachable = serverReachable; + if (serverReachable) { this.initializeWebSocket(); } else { - this.handleWebSocket(false); - if (displayBar) { this.show(); } + this.handleWebSocket(false); + if (hasInternet) { // try to reconnect cause we have internet this.handleReconnect(); @@ -148,6 +153,7 @@ export default class NetworkIndicator extends PureComponent { }; connected = () => { + this.props.actions.setCurrentUserStatus(true); Animated.sequence([ Animated.timing( this.backgroundColor, { @@ -299,7 +305,9 @@ export default class NetworkIndicator extends PureComponent { toValue: this.getNavBarHeight(), duration: 300, } - ).start(); + ).start(() => { + this.props.actions.setCurrentUserStatus(false); + }); }; render() {