[MM-24426] Set previous app version in redux store (1.30) (#4196)

* Set previous app version in redux store

* Update state

* Log error
This commit is contained in:
Miguel Alatzar 2020-04-22 08:23:45 -07:00 committed by GitHub
parent a17bc4683c
commit 27411cb119
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 153 additions and 26 deletions

View file

@ -7,7 +7,7 @@ import {Provider} from 'react-redux';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
import {loadMe} from 'app/actions/views/user';
import {loadMe, logout} from 'app/actions/views/user';
import {resetToChannel, resetToSelectServer} from 'app/actions/navigation';
import {setDeepLinkURL} from 'app/actions/views/root';
@ -24,6 +24,7 @@ import EphemeralStore from 'app/store/ephemeral_store';
import telemetry from 'app/telemetry';
import {validatePreviousVersion} from 'app/utils/general';
import pushNotificationsUtils from 'app/utils/push_notifications';
import {captureJSException} from 'app/utils/sentry';
const init = async () => {
const credentials = await getAppCredentials();
@ -53,10 +54,15 @@ const launchApp = (credentials) => {
if (credentials) {
waitForHydration(store, () => {
const valid = validatePreviousVersion(store);
const {previousVersion} = store.getState().app;
const valid = validatePreviousVersion(previousVersion);
if (valid) {
store.dispatch(loadMe());
resetToChannel({skipMetrics: true});
} else {
const error = new Error(`Previous app version "${previousVersion}" is invalid.`);
captureJSException(error, false, store);
store.dispatch(logout());
}
});
} else {

View file

@ -5,8 +5,10 @@ import {combineReducers} from 'redux';
import build from './build';
import version from './version';
import previousVersion from './previousVersion';
export default combineReducers({
build,
version,
previousVersion,
});

View file

@ -0,0 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
export default function previousVersion(state = '') {
return state;
}

View file

@ -6,8 +6,6 @@ import DeviceInfo from 'react-native-device-info';
import {ViewTypes} from 'app/constants';
import initialState from 'app/initial_state';
import EphemeralStore from './ephemeral_store';
import {
captureException,
LOGGER_JAVASCRIPT_WARNING,
@ -19,21 +17,25 @@ export function messageRetention(store) {
const {app} = action.payload;
const {entities, views} = action.payload;
const build = DeviceInfo.getBuildNumber();
const version = DeviceInfo.getVersion();
const previousVersion = app?.version;
const previousBuild = app?.build;
action.payload = {
...action.payload,
app: {
build,
version,
previousVersion,
},
};
if (!entities || !views) {
const version = DeviceInfo.getVersion();
EphemeralStore.prevAppVersion = version;
action.payload = {
...action.payload,
app: {
build: DeviceInfo.getBuildNumber(),
version,
},
};
return next(action);
}
EphemeralStore.prevAppVersion = app?.version;
if (app?.version !== DeviceInfo.getVersion() || app?.build !== DeviceInfo.getBuildNumber()) {
if (previousVersion !== version || previousBuild !== build) {
// When a new version of the app has been detected
return next(resetStateForNewVersion(action));
}
@ -154,8 +156,7 @@ function resetStateForNewVersion(action) {
const nextState = {
app: {
build: DeviceInfo.getBuildNumber(),
version: DeviceInfo.getVersion(),
...payload.app,
},
entities: {
channels,

View file

@ -3,6 +3,8 @@
/* eslint-disable max-nested-callbacks */
import DeviceInfo from 'react-native-device-info';
import assert from 'assert';
import {ViewTypes} from 'app/constants';
@ -55,6 +57,122 @@ describe('messageRetention', () => {
});
});
});
describe('should add build, version, and previousVersion to payload.app on persist/REHYDRATE', () => {
const next = (a) => a;
const store = {};
const build = 'build';
const version = 'version';
const previousBuild = 'previous-build';
const previousVersion = 'previous-version';
DeviceInfo.getBuildNumber = jest.fn().mockReturnValue('build');
DeviceInfo.getVersion = jest.fn().mockReturnValue('version');
const rehydrateAction = {
type: 'persist/REHYDRATE',
payload: {
app: {
build: previousBuild,
version: previousVersion,
},
},
};
const expectedPayloadApp = {
build,
version,
previousVersion,
};
const entities = {
channels: {},
posts: {},
};
const views = {
team: {
lastChannelForTeam: {},
},
};
test('when entities is missing', () => {
const action = {...rehydrateAction};
const nextAction = messageRetention(store)(next)(action);
expect(nextAction.payload.app).toStrictEqual(expectedPayloadApp);
});
test('when views is missing', () => {
const action = {
...rehydrateAction,
payload: {
...rehydrateAction.payload,
entities,
},
};
const nextAction = messageRetention(store)(next)(action);
expect(nextAction.payload.app).toStrictEqual(expectedPayloadApp);
});
test('when previousVersion !== version', () => {
const action = {
...rehydrateAction,
payload: {
...rehydrateAction.payload,
entities,
views,
},
};
expect(action.payload.app.version).not.toEqual(DeviceInfo.getVersion());
const nextAction = messageRetention(store)(next)(action);
expect(nextAction.payload.app).toStrictEqual(expectedPayloadApp);
});
test('when previousBuild !== build', () => {
const action = {
...rehydrateAction,
payload: {
...rehydrateAction.payload,
app: {
...rehydrateAction.payload.app,
version: DeviceInfo.getVersion(),
},
entities,
views,
},
};
expect(action.payload.app.version).toEqual(DeviceInfo.getVersion());
expect(action.payload.app.build).not.toEqual(DeviceInfo.getBuildNumber());
const nextAction = messageRetention(store)(next)(action);
expect(nextAction.payload.app).toStrictEqual({
...expectedPayloadApp,
previousVersion: DeviceInfo.getVersion(),
});
});
test('when cleanUpState', () => {
const action = {
...rehydrateAction,
payload: {
...rehydrateAction.payload,
app: {
...rehydrateAction.payload.app,
version: DeviceInfo.getVersion(),
build: DeviceInfo.getBuildNumber(),
},
entities,
views,
},
};
expect(action.payload.app.version).toEqual(DeviceInfo.getVersion());
expect(action.payload.app.build).toEqual(DeviceInfo.getBuildNumber());
const nextAction = messageRetention(store)(next)(action);
expect(nextAction.payload.app).toStrictEqual({
...expectedPayloadApp,
previousVersion: DeviceInfo.getVersion(),
});
});
});
});
describe('cleanUpState', () => {

View file

@ -6,9 +6,6 @@ import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
import {Posts} from 'mattermost-redux/constants';
import {logout} from 'app/actions/views/user';
import EphemeralStore from 'app/store/ephemeral_store';
const INVALID_VERSIONS = ['1.29.0'];
export function fromAutoResponder(post) {
@ -88,12 +85,9 @@ export function isPendingPost(postId, userId) {
return postId.startsWith(userId);
}
export function validatePreviousVersion(store) {
const version = EphemeralStore.prevAppVersion;
if (!version || INVALID_VERSIONS.includes(version)) {
console.log('Previous version is no longer valid'); //eslint-disable-line no-console
store.dispatch(logout());
export function validatePreviousVersion(previousVersion) {
if (!previousVersion || INVALID_VERSIONS.includes(previousVersion)) {
console.log(`Previous version "${previousVersion}" is no longer valid`); //eslint-disable-line no-console
return false;
}