forked from Ivasoft/mattermost-mobile
* Gracefully handle different x-version-id being reported
* feedback review
* Fix access to general entities
(cherry picked from commit 4dc164a679)
Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import deepEqual from 'deep-equal';
|
||||
import {batchActions} from 'redux-batched-actions';
|
||||
|
||||
import {Client4} from '@client/rest';
|
||||
@@ -31,7 +32,10 @@ export function startDataCleanup() {
|
||||
|
||||
export function loadConfigAndLicense() {
|
||||
return async (dispatch, getState) => {
|
||||
const {currentUserId} = getState().entities.users;
|
||||
const state = getState();
|
||||
const {currentUserId} = state.entities.users;
|
||||
const {general} = state.entities;
|
||||
const actions = [];
|
||||
|
||||
try {
|
||||
const [config, license] = await Promise.all([
|
||||
@@ -39,23 +43,31 @@ export function loadConfigAndLicense() {
|
||||
Client4.getClientLicenseOld(),
|
||||
]);
|
||||
|
||||
const actions = [{
|
||||
type: GeneralTypes.CLIENT_CONFIG_RECEIVED,
|
||||
data: config,
|
||||
}, {
|
||||
type: GeneralTypes.CLIENT_LICENSE_RECEIVED,
|
||||
data: license,
|
||||
}];
|
||||
if (!deepEqual(general.config, config)) {
|
||||
actions.push({
|
||||
type: GeneralTypes.CLIENT_CONFIG_RECEIVED,
|
||||
data: config,
|
||||
});
|
||||
}
|
||||
|
||||
if (currentUserId) {
|
||||
if (license?.IsLicensed === 'true' && license?.DataRetention === 'true') {
|
||||
dispatch(getDataRetentionPolicy());
|
||||
} else {
|
||||
actions.push({type: GeneralTypes.RECEIVED_DATA_RETENTION_POLICY, data: {}});
|
||||
if (!deepEqual(general.license, license)) {
|
||||
actions.push({
|
||||
type: GeneralTypes.CLIENT_LICENSE_RECEIVED,
|
||||
data: license,
|
||||
});
|
||||
|
||||
if (currentUserId) {
|
||||
if (license?.IsLicensed === 'true' && license?.DataRetention === 'true') {
|
||||
dispatch(getDataRetentionPolicy());
|
||||
} else {
|
||||
actions.push({type: GeneralTypes.RECEIVED_DATA_RETENTION_POLICY, data: {}});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dispatch(batchActions(actions, 'BATCH_LOAD_CONFIG_AND_LICENSE'));
|
||||
if (actions.length) {
|
||||
dispatch(batchActions(actions, 'BATCH_LOAD_CONFIG_AND_LICENSE'));
|
||||
}
|
||||
|
||||
return {config, license};
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import deepEqual from 'deep-equal';
|
||||
import {batchActions} from 'redux-batched-actions';
|
||||
|
||||
import {handleCRTPreferenceChange} from '@actions/views/crt';
|
||||
@@ -121,9 +122,12 @@ export function loadMe(user, deviceToken, skipDispatch = false) {
|
||||
data.teams = teams;
|
||||
data.teamMembers = teamMembers;
|
||||
data.teamUnreads = teamUnreads;
|
||||
data.config = config;
|
||||
data.url = Client4.getUrl();
|
||||
|
||||
if (!deepEqual(state.entities?.general?.config, config)) {
|
||||
data.config = config;
|
||||
}
|
||||
|
||||
actions.push({
|
||||
type: UserTypes.LOGIN,
|
||||
data,
|
||||
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
handleCallScreenOff,
|
||||
} from '@mmproducts/calls/store/actions/websockets';
|
||||
import {getChannelSinceValue} from '@utils/channels';
|
||||
import {semverFromServerVersion} from '@utils/general';
|
||||
import websocketClient from '@websocket';
|
||||
|
||||
import {handleRefreshAppsBindings} from './apps';
|
||||
@@ -474,11 +475,8 @@ function handleEvent(msg: WebSocketMessage) {
|
||||
}
|
||||
|
||||
function handleHelloEvent(msg: WebSocketMessage) {
|
||||
const serverVersion = msg.data.server_version;
|
||||
if (serverVersion && Client4.serverVersion !== serverVersion) {
|
||||
Client4.serverVersion = serverVersion;
|
||||
EventEmitter.emit(General.SERVER_VERSION_CHANGED, serverVersion);
|
||||
}
|
||||
const serverVersion = semverFromServerVersion(msg.data.server_version);
|
||||
EventEmitter.emit(General.SERVER_VERSION_CHANGED, serverVersion);
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
@@ -13,6 +13,7 @@ import ClientError from '@client/rest/error';
|
||||
import mattermostManaged from '@mattermost-managed';
|
||||
import {General} from '@mm-redux/constants';
|
||||
import EventEmitter from '@mm-redux/utils/event_emitter';
|
||||
import {semverFromServerVersion} from '@utils/general';
|
||||
import {t} from '@utils/i18n';
|
||||
|
||||
import mattermostBucket from 'app/mattermost_bucket';
|
||||
@@ -116,7 +117,7 @@ Client4.doFetchWithResponse = async (url, options) => {
|
||||
Client4.setToken(token);
|
||||
}
|
||||
|
||||
const serverVersion = headers[HEADER_X_VERSION_ID] || headers[HEADER_X_VERSION_ID.toLowerCase()];
|
||||
const serverVersion = semverFromServerVersion(headers[HEADER_X_VERSION_ID] || headers[HEADER_X_VERSION_ID.toLowerCase()]);
|
||||
if (serverVersion && !headers['Cache-Control'] && Client4.serverVersion !== serverVersion) {
|
||||
Client4.serverVersion = serverVersion; /* eslint-disable-line require-atomic-updates */
|
||||
EventEmitter.emit(General.SERVER_VERSION_CHANGED, serverVersion);
|
||||
|
||||
@@ -29,7 +29,7 @@ describe('Fetch', () => {
|
||||
test('doFetchWithResponse handles title case headers', async () => {
|
||||
const setToken = jest.spyOn(Client4, 'setToken');
|
||||
const headers = {
|
||||
[HEADER_X_VERSION_ID]: 'VersionId',
|
||||
[HEADER_X_VERSION_ID]: '6.1.0',
|
||||
[HEADER_X_CLUSTER_ID]: 'ClusterId',
|
||||
[HEADER_TOKEN]: 'Token',
|
||||
};
|
||||
@@ -48,7 +48,7 @@ describe('Fetch', () => {
|
||||
test('doFetchWithResponse handles lower case headers', async () => {
|
||||
const setToken = jest.spyOn(Client4, 'setToken');
|
||||
const headers = {
|
||||
[HEADER_X_VERSION_ID.toLowerCase()]: 'versionid',
|
||||
[HEADER_X_VERSION_ID.toLowerCase()]: '6.2.0',
|
||||
[HEADER_X_CLUSTER_ID.toLowerCase()]: 'clusterid',
|
||||
[HEADER_TOKEN.toLowerCase()]: 'token',
|
||||
};
|
||||
@@ -66,7 +66,7 @@ describe('Fetch', () => {
|
||||
|
||||
test('doFetchWithResponse handles server version change', async () => {
|
||||
const emit = jest.spyOn(EventEmitter, 'emit');
|
||||
const serverVersion1 = 'version1';
|
||||
const serverVersion1 = '6.3.0';
|
||||
const response = {
|
||||
json: () => Promise.resolve('data'),
|
||||
ok: true,
|
||||
|
||||
@@ -7,6 +7,7 @@ import CookieManager from '@react-native-cookies/cookies';
|
||||
import {AppState, Dimensions, Keyboard, Linking, Platform} from 'react-native';
|
||||
import DeviceInfo from 'react-native-device-info';
|
||||
import {getLocales} from 'react-native-localize';
|
||||
import {valid as validVersion} from 'semver';
|
||||
|
||||
import {setDeviceDimensions, setDeviceOrientation, setDeviceAsTablet} from '@actions/device';
|
||||
import {dismissAllModals, popToRoot, showOverlay} from '@actions/navigation';
|
||||
@@ -249,9 +250,11 @@ class GlobalEventHandler {
|
||||
onServerVersionChanged = async (serverVersion) => {
|
||||
const {dispatch, getState} = Store.redux;
|
||||
const state = getState();
|
||||
if (serverVersion && state.entities.users && state.entities.users.currentUserId) {
|
||||
const {general, users} = state.entities;
|
||||
const isValid = validVersion(serverVersion);
|
||||
const versionDidChange = general?.serverVersion !== serverVersion;
|
||||
if (isValid && serverVersion && versionDidChange && users?.currentUserId) {
|
||||
dispatch(setServerVersion(serverVersion));
|
||||
dispatch(loadConfigAndLicense());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -147,6 +147,9 @@ describe('GlobalEventHandler', () => {
|
||||
const currentUserId = 'current-user-id';
|
||||
Store.redux.getState = jest.fn().mockReturnValue({
|
||||
entities: {
|
||||
general: {
|
||||
serverVersion: '',
|
||||
},
|
||||
users: {
|
||||
currentUserId,
|
||||
profiles: {
|
||||
@@ -164,21 +167,18 @@ describe('GlobalEventHandler', () => {
|
||||
const invalidVersion = 'a.b.c';
|
||||
await GlobalEventHandler.onServerVersionChanged(invalidVersion);
|
||||
|
||||
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||
expect(dispatch).toHaveBeenCalledWith('setServerVersion');
|
||||
expect(dispatch).toHaveBeenCalledWith('loadConfigAndLicense');
|
||||
expect(dispatch).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('should dispatch on gte min server version with currentUserId', async () => {
|
||||
let version = minVersion.version;
|
||||
await GlobalEventHandler.onServerVersionChanged(version);
|
||||
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||
expect(dispatch).toHaveBeenCalledWith('setServerVersion');
|
||||
expect(dispatch).toHaveBeenCalledWith('loadConfigAndLicense');
|
||||
|
||||
version = semver.coerce(minVersion.major + 1).version;
|
||||
await GlobalEventHandler.onServerVersionChanged(version);
|
||||
expect(dispatch).toHaveBeenCalledTimes(4);
|
||||
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should not dispatch on empty, null, undefined server version', async () => {
|
||||
|
||||
@@ -13,7 +13,10 @@ function config(state: Partial<Config> = {}, action: GenericAction) {
|
||||
return Object.assign({}, state, action.data);
|
||||
case UserTypes.LOGIN: // Used by the mobile app
|
||||
case GeneralTypes.SET_CONFIG_AND_LICENSE:
|
||||
return Object.assign({}, state, action.data.config);
|
||||
if (action.data.config) {
|
||||
return Object.assign({}, state, action.data.config);
|
||||
}
|
||||
return state;
|
||||
case GeneralTypes.CLIENT_CONFIG_RESET:
|
||||
default:
|
||||
return state;
|
||||
|
||||
@@ -110,3 +110,17 @@ export function permalinkBadTeam(intl) {
|
||||
|
||||
alertErrorWithFallback(intl, {}, message);
|
||||
}
|
||||
|
||||
export function semverFromServerVersion(value) {
|
||||
if (!value || typeof value !== 'string') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const split = value.split('.');
|
||||
|
||||
const major = parseInt(split[0], 10);
|
||||
const minor = parseInt(split[1] || '0', 10);
|
||||
const patch = parseInt(split[2] || '0', 10);
|
||||
|
||||
return `${major}.${minor}.${patch}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user