[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:
parent
a17bc4683c
commit
27411cb119
6 changed files with 153 additions and 26 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
});
|
||||
|
|
|
|||
6
app/reducers/app/previousVersion.js
Normal file
6
app/reducers/app/previousVersion.js
Normal 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;
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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', () => {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue