Automated cherry pick of #4488 (#4491)

* MM-25946 prompt sys admins if they are running server versions below 5.19

* Update alert message

* Change alert buttons

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
Mattermost Build 2020-06-26 22:14:54 +02:00 committed by GitHub
parent 8af4ad793b
commit 07bc7a4c9f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 164 additions and 47 deletions

View file

@ -95,8 +95,16 @@ const ViewTypes = keyMirror({
LANDSCAPE: null,
});
const RequiredServer = {
FULL_VERSION: 5.19,
MAJOR_VERSION: 5,
MIN_VERSION: 19,
PATCH_VERSION: 0,
};
export default {
...ViewTypes,
RequiredServer,
POST_VISIBILITY_CHUNK_SIZE: 15,
FEATURE_TOGGLE_PREFIX: 'feature_enabled_',
EMBED_PREVIEW: 'embed_preview',

View file

@ -5,14 +5,16 @@ import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {intlShape} from 'react-intl';
import {
Alert,
Keyboard,
Linking,
StyleSheet,
} from 'react-native';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import {showModal, showModalOverCurrentContext} from '@actions/navigation';
import LocalConfig from '@assets/config';
import {NavigationTypes} from '@constants';
import {NavigationTypes, ViewTypes} from '@constants';
import EventEmitter from '@mm-redux/utils/event_emitter';
import EphemeralStore from '@store/ephemeral_store';
import {preventDoubleTap} from '@utils/tap';
@ -37,6 +39,7 @@ export default class ChannelBase extends PureComponent {
currentChannelId: PropTypes.string,
currentTeamId: PropTypes.string,
disableTermsModal: PropTypes.bool,
isSupportedServer: PropTypes.bool,
teamName: PropTypes.string,
theme: PropTypes.object.isRequired,
showTermsOfService: PropTypes.bool,
@ -67,31 +70,43 @@ export default class ChannelBase extends PureComponent {
}
componentDidMount() {
const {
actions,
currentChannelId,
currentTeamId,
disableTermsModal,
isSupportedServer,
showTermsOfService,
skipMetrics,
} = this.props;
EventEmitter.on(NavigationTypes.BLUR_POST_DRAFT, this.blurPostDraft);
EventEmitter.on('leave_team', this.handleLeaveTeam);
if (this.props.currentTeamId) {
this.loadChannels(this.props.currentTeamId);
if (currentTeamId) {
this.loadChannels(currentTeamId);
} else {
this.props.actions.selectDefaultTeam();
actions.selectDefaultTeam();
}
if (this.props.currentChannelId) {
PushNotifications.clearChannelNotifications(this.props.currentChannelId);
if (currentChannelId) {
PushNotifications.clearChannelNotifications(currentChannelId);
requestAnimationFrame(() => {
this.props.actions.getChannelStats(this.props.currentChannelId);
actions.getChannelStats(currentChannelId);
});
}
if (tracker.initialLoad && !this.props.skipMetrics) {
this.props.actions.recordLoadTime('Start time', 'initialLoad');
if (tracker.initialLoad && !skipMetrics) {
actions.recordLoadTime('Start time', 'initialLoad');
}
if (this.props.showTermsOfService && !this.props.disableTermsModal) {
if (showTermsOfService && !disableTermsModal) {
this.showTermsOfServiceModal();
} else if (!isSupportedServer) {
// Only display the Alert if the TOS does not need to show first
this.showUnsupportedServer();
}
if (!this.props.skipMetrics) {
if (!skipMetrics) {
telemetry.end(['start:channel_screen']);
}
}
@ -140,31 +155,6 @@ export default class ChannelBase extends PureComponent {
}
};
showTermsOfServiceModal = async () => {
const {intl} = this.context;
const {theme} = this.props;
const screen = 'TermsOfService';
const title = intl.formatMessage({id: 'mobile.tos_link', defaultMessage: 'Terms of Service'});
MaterialIcon.getImageSource('close', 20, theme.sidebarHeaderTextColor).then((closeButton) => {
const passProps = {closeButton};
const options = {
layout: {
componentBackgroundColor: theme.centerChannelBg,
},
topBar: {
visible: true,
height: null,
title: {
color: theme.sidebarHeaderTextColor,
text: title,
},
},
};
showModalOverCurrentContext(screen, passProps, options);
});
};
goToChannelInfo = preventDoubleTap(() => {
const {intl} = this.context;
const {theme} = this.props;
@ -249,6 +239,62 @@ export default class ChannelBase extends PureComponent {
return null;
}
showTermsOfServiceModal = async () => {
const {intl} = this.context;
const {isSupportedServer, theme} = this.props;
const screen = 'TermsOfService';
const title = intl.formatMessage({id: 'mobile.tos_link', defaultMessage: 'Terms of Service'});
MaterialIcon.getImageSource('close', 20, theme.sidebarHeaderTextColor).then((closeButton) => {
const passProps = {
closeButton,
isSupportedServer,
showUnsupportedServer: this.showUnsupportedServer,
};
const options = {
layout: {
componentBackgroundColor: theme.centerChannelBg,
},
topBar: {
visible: true,
height: null,
title: {
color: theme.sidebarHeaderTextColor,
text: title,
},
},
};
showModalOverCurrentContext(screen, passProps, options);
});
};
showUnsupportedServer = () => {
const {formatMessage} = this.context.intl;
const title = formatMessage({id: 'mobile.server_upgrade.title', defaultMessage: 'Server upgrade required'});
const message = formatMessage({
id: 'mobile.server_upgrade.alert_description',
defaultMessage: 'This server version is unsupported and users will be exposed to compatibility issues that cause crashes or severe bugs breaking core functionality of the app. Upgrading to server version {serverVersion} or later is required.',
}, {serverVersion: ViewTypes.RequiredServer.FULL_VERSION});
const cancel = {
text: formatMessage({id: 'mobile.server_upgrade.dismiss', defaultMessage: 'Dismiss'}),
style: 'default',
};
const learnMore = {
text: formatMessage({id: 'mobile.server_upgrade.learn_more', defaultMessage: 'Learn More'}),
style: 'cancel',
onPress: () => {
const url = 'https://mattermost.com/blog/support-for-esr-5-9-has-ended/';
if (Linking.canOpenURL(url)) {
Linking.openURL(url);
}
},
};
const buttons = [cancel, learnMore];
const options = {cancelable: false};
Alert.alert(title, message, buttons, options);
};
updateNativeScrollView = () => {
if (this.keyboardTracker?.current) {
this.keyboardTracker.current.resetScrollView(this.props.currentChannelId);

View file

@ -62,6 +62,7 @@ describe('ChannelBase', () => {
const wrapper = shallow(
<ChannelBase {...baseProps}/>,
{context: {intl: {formatMessage: jest.fn()}}},
);
expect(mergeNavigationOptions.mock.calls).toEqual([]);

View file

@ -7,20 +7,37 @@ import {connect} from 'react-redux';
import {loadChannelsForTeam, selectInitialChannel} from '@actions/views/channel';
import {recordLoadTime} from '@actions/views/root';
import {selectDefaultTeam} from '@actions/views/select_team';
import {getCurrentChannelId} from '@mm-redux/selectors/entities/channels';
import {getCurrentTeam} from '@mm-redux/selectors/entities/teams';
import {getTheme} from '@mm-redux/selectors/entities/preferences';
import {shouldShowTermsOfService} from '@mm-redux/selectors/entities/users';
import {ViewTypes} from '@constants';
import {getChannelStats} from '@mm-redux/actions/channels';
import {getCurrentChannelId} from '@mm-redux/selectors/entities/channels';
import {getServerVersion} from '@mm-redux/selectors/entities/general';
import {getTheme} from '@mm-redux/selectors/entities/preferences';
import {getCurrentTeam} from '@mm-redux/selectors/entities/teams';
import {getCurrentUserId, getCurrentUserRoles, shouldShowTermsOfService} from '@mm-redux/selectors/entities/users';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {isSystemAdmin as checkIsSystemAdmin} from '@mm-redux/utils/user_utils';
import Channel from './channel';
function mapStateToProps(state) {
const currentTeam = getCurrentTeam(state);
const roles = getCurrentUserId(state) ? getCurrentUserRoles(state) : '';
const isSystemAdmin = checkIsSystemAdmin(roles);
let isSupportedServer = true;
if (isSystemAdmin) {
isSupportedServer = isMinimumServerVersion(
getServerVersion(state),
ViewTypes.RequiredServer.MAJOR_VERSION,
ViewTypes.RequiredServer.MIN_VERSION,
ViewTypes.RequiredServer.PATCH_VERSION,
);
}
return {
currentTeamId: currentTeam?.id,
currentChannelId: getCurrentChannelId(state),
isSupportedServer,
teamName: currentTeam?.display_name,
theme: getTheme(state),
showTermsOfService: shouldShowTermsOfService(state),

View file

@ -30,6 +30,8 @@ export default class TermsOfService extends PureComponent {
}).isRequired,
componentId: PropTypes.string,
closeButton: PropTypes.object,
isSupportedServer: PropTypes.bool,
showUnsupportedServer: PropTypes.func,
siteName: PropTypes.string,
theme: PropTypes.object,
};
@ -145,11 +147,18 @@ export default class TermsOfService extends PureComponent {
};
handleAcceptTerms = () => {
const onSuccess = () => {
const {isSupportedServer, showUnsupportedServer} = this.props;
dismissModal();
if (!isSupportedServer) {
showUnsupportedServer();
}
};
this.registerUserAction(
true,
() => {
dismissModal();
},
onSuccess,
this.handleAcceptTerms,
);
};
@ -189,7 +198,6 @@ export default class TermsOfService extends PureComponent {
});
const {data} = await actions.updateMyTermsOfServiceStatus(this.state.termsId, accepted);
this.setState({
loading: false,
});

View file

@ -7,6 +7,7 @@ import {shallow} from 'enzyme';
import Preferences from '@mm-redux/constants/preferences';
import * as NavigationActions from '@actions/navigation';
import TestHelper from 'test/test_helper';
import TermsOfService from './terms_of_service.js';
@ -23,16 +24,18 @@ jest.mock('@utils/theme', () => {
describe('TermsOfService', () => {
const actions = {
getTermsOfService: jest.fn(),
updateMyTermsOfServiceStatus: jest.fn(),
updateMyTermsOfServiceStatus: jest.fn().mockResolvedValue({data: true}),
logout: jest.fn(),
};
const baseProps = {
actions,
theme: Preferences.THEMES.default,
closeButton: {},
siteName: 'Mattermost',
componentId: 'component-id',
isSupportedServer: true,
siteName: 'Mattermost',
showUnsupportedServer: jest.fn(),
theme: Preferences.THEMES.default,
};
test('should match snapshot', () => {
@ -130,4 +133,35 @@ describe('TermsOfService', () => {
wrapper.instance().closeTermsAndLogout();
expect(baseProps.actions.logout).toHaveBeenCalledTimes(1);
});
test('should NOT call showUnsupportedServer on acceptTerms if server is supported', async () => {
const wrapper = shallow(
<TermsOfService
{...baseProps}
/>,
{context: {intl: {formatMessage: jest.fn()}}},
);
wrapper.setState({loading: false, termsId: 1, termsText: 'Terms Text'});
wrapper.instance().handleAcceptTerms();
TestHelper.wait(100).then(() => {
expect(baseProps.showUnsupportedServer).not.toHaveBeenCalled();
});
});
test('should call showUnsupportedServer on acceptTerms if server not supported', async () => {
const wrapper = shallow(
<TermsOfService
{...baseProps}
isSupportedServer={false}
/>,
{context: {intl: {formatMessage: jest.fn()}}},
);
wrapper.setState({loading: false, termsId: 1, termsText: 'Terms Text'});
wrapper.instance().handleAcceptTerms();
TestHelper.wait(100).then(() => {
expect(baseProps.showUnsupportedServer).toHaveBeenCalledTimes(1);
});
});
});

View file

@ -453,8 +453,11 @@
"mobile.server_link.unreachable_team.error": "This link belongs to a deleted team or to a team to which you do not have access.",
"mobile.server_ssl.error.text": "The certificate from {host} is not trusted.\n\nPlease contact your System Administrator to resolve the certificate issues and allow connections to this server.",
"mobile.server_ssl.error.title": "Untrusted Certificate",
"mobile.server_upgrade.alert_description": "This server version is unsupported and users will be exposed to compatibility issues that cause crashes or severe bugs breaking core functionality of the app. Upgrading to server version {serverVersion} or later is required.",
"mobile.server_upgrade.button": "OK",
"mobile.server_upgrade.description": "\nA server upgrade is required to use the Mattermost app. Please ask your System Administrator for details.\n",
"mobile.server_upgrade.dismiss": "Dismiss",
"mobile.server_upgrade.learn_more": "Learn More",
"mobile.server_upgrade.title": "Server upgrade required",
"mobile.server_url.invalid_format": "URL must start with http:// or https://",
"mobile.session_expired": "Session Expired: Please log in to continue receiving notifications. Sessions for {siteName} are configured to expire every {daysCount, number} {daysCount, plural, one {day} other {days}}.",