Commit 4a77fd04 authored by Saturnino Abril's avatar Saturnino Abril Committed by GitHub
Browse files

[PLT-7886] Move link previews to Account Settings > Display and refactor UserSettingsDisplay (#156)

* move link previews to Account Settings > Display, refactor UserSettingsDisplay

* update help text, set default to true, refactor and add unit test

* use getBool selector where necessary
parent 3814ce3c
......@@ -52,7 +52,7 @@ export default class LinkPreviewsSettings extends AdminSettings {
helpText={
<FormattedMessage
id='admin.customization.enableLinkPreviewsDesc'
defaultMessage='Enable users to display a preview of website content below the message, if available. When true, website previews can be enabled from Account Settings > Advanced > Preview pre-release features.'
defaultMessage='Display a preview of website content below messages, when available. Users can disable these previews from Account Settings > Display > Website Link Previews.'
/>
}
value={this.state.enableLinkPreviews}
......
......@@ -4,7 +4,7 @@
import {connect} from 'react-redux';
import {getPost} from 'mattermost-redux/selectors/entities/posts';
import {get} from 'mattermost-redux/selectors/entities/preferences';
import {get, getBool} from 'mattermost-redux/selectors/entities/preferences';
import {getUser} from 'mattermost-redux/selectors/entities/users';
import {Preferences} from 'utils/constants.jsx';
......@@ -23,7 +23,8 @@ function mapStateToProps(state, ownProps) {
...ownProps,
parentPost,
parentPostUser,
previewCollapsed: get(state, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.COLLAPSE_DISPLAY, 'false')
previewCollapsed: get(state, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.COLLAPSE_DISPLAY, 'false'),
previewEnabled: getBool(state, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.LINK_PREVIEW_DISPLAY, true)
};
}
......
......@@ -57,6 +57,11 @@ export default class PostBody extends React.PureComponent {
*/
previewCollapsed: PropTypes.string,
/**
* User's preference to link previews
*/
previewEnabled: PropTypes.bool,
/**
* Post identifiers for selenium tests
*/
......@@ -176,6 +181,7 @@ export default class PostBody extends React.PureComponent {
<PostBodyAdditionalContent
post={this.props.post}
previewCollapsed={this.props.previewCollapsed}
previewEnabled={this.props.previewEnabled}
>
{messageWrapper}
</PostBodyAdditionalContent>
......
......@@ -6,7 +6,6 @@ import React from 'react';
import BrowserStore from 'stores/browser_store.jsx';
import Constants from 'utils/constants.jsx';
import * as Utils from 'utils/utils.jsx';
import YoutubeVideo from 'components/youtube_video';
......@@ -31,11 +30,17 @@ export default class PostBodyAdditionalContent extends React.PureComponent {
/**
* Set to collapse image and video previews
*/
previewCollapsed: PropTypes.string
previewCollapsed: PropTypes.string,
/**
* User's preference to link previews
*/
previewEnabled: PropTypes.bool
}
static defaultProps = {
previewCollapsed: ''
previewCollapsed: '',
previewEnabled: false
}
constructor(props) {
......@@ -192,11 +197,12 @@ export default class PostBodyAdditionalContent extends React.PureComponent {
}
const link = Utils.extractFirstLink(this.props.post.message);
if (link && Utils.isFeatureEnabled(Constants.PRE_RELEASE_FEATURES.EMBED_PREVIEW) && global.window.mm_config.EnableLinkPreviews === 'true') {
if (link && global.window.mm_config.EnableLinkPreviews === 'true' && this.props.previewEnabled) {
return (
<PostAttachmentOpenGraph
link={link}
previewCollapsed={this.props.previewCollapsed}
previewEnabled={this.props.previewEnabled}
post={this.props.post}
/>
);
......
......@@ -7,9 +7,9 @@ import {FormattedMessage} from 'react-intl';
import {Link} from 'react-router/es6';
import {addReaction, emitEmojiPosted} from 'actions/post_actions.jsx';
import UserStore from 'stores/user_store.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import TeamStore from 'stores/team_store.jsx';
import UserStore from 'stores/user_store.jsx';
import Constants from 'utils/constants.jsx';
import * as PostUtils from 'utils/post_utils.jsx';
......@@ -37,6 +37,7 @@ export default class RhsRootPost extends React.Component {
isFlagged: PropTypes.bool,
status: PropTypes.string,
previewCollapsed: PropTypes.string,
previewEnabled: PropTypes.bool,
isBusy: PropTypes.bool
}
......@@ -97,6 +98,10 @@ export default class RhsRootPost extends React.Component {
return true;
}
if (nextProps.previewEnabled !== this.props.previewEnabled) {
return true;
}
if (!Utils.areObjectsEqual(nextProps.post, this.props.post)) {
return true;
}
......@@ -451,6 +456,7 @@ export default class RhsRootPost extends React.Component {
<PostBodyAdditionalContent
post={post}
previewCollapsed={this.props.previewCollapsed}
previewEnabled={this.props.previewEnabled}
>
<PostMessageContainer
post={post}
......
......@@ -5,7 +5,7 @@ import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {makeGetPostsForThread} from 'mattermost-redux/selectors/entities/posts';
import {get} from 'mattermost-redux/selectors/entities/preferences';
import {get, getBool} from 'mattermost-redux/selectors/entities/preferences';
import {removePost} from 'mattermost-redux/actions/posts';
......@@ -29,7 +29,8 @@ function makeMapStateToProps() {
...ownProps,
selected,
posts,
previewCollapsed: get(state, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.COLLAPSE_DISPLAY, 'false')
previewCollapsed: get(state, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.COLLAPSE_DISPLAY, 'false'),
previewEnabled: getBool(state, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.LINK_PREVIEW_DISPLAY, true)
};
};
}
......
......@@ -62,6 +62,7 @@ export default class RhsThread extends React.Component {
toggleSize: PropTypes.func,
shrink: PropTypes.func,
previewCollapsed: PropTypes.string.isRequired,
previewEnabled: PropTypes.bool.isRequired,
actions: PropTypes.shape({
removePost: PropTypes.func.isRequired
}).isRequired
......@@ -169,6 +170,10 @@ export default class RhsThread extends React.Component {
return true;
}
if (nextProps.previewEnabled !== this.props.previewEnabled) {
return true;
}
if (!Utils.areObjectsEqual(nextState.flaggedPosts, this.state.flaggedPosts)) {
return true;
}
......@@ -473,6 +478,7 @@ export default class RhsThread extends React.Component {
isFlagged={isRootFlagged}
status={rootStatus}
previewCollapsed={this.state.previewsCollapsed}
previewEnabled={this.props.previewEnabled}
isBusy={this.state.isBusy}
/>
<div
......
......@@ -57,16 +57,11 @@ export default class AdvancedSettingsDisplay extends React.Component {
};
const webrtcEnabled = global.mm_config.EnableWebrtc === 'true';
const linkPreviewsEnabled = global.mm_config.EnableLinkPreviews === 'true';
if (!webrtcEnabled) {
preReleaseFeaturesKeys = preReleaseFeaturesKeys.filter((f) => f !== 'WEBRTC_PREVIEW');
}
if (!linkPreviewsEnabled) {
preReleaseFeaturesKeys = preReleaseFeaturesKeys.filter((f) => f !== 'EMBED_PREVIEW');
}
let enabledFeatures = 0;
for (const [name, value] of advancedSettings) {
for (const key of preReleaseFeaturesKeys) {
......@@ -345,13 +340,6 @@ export default class AdvancedSettingsDisplay extends React.Component {
defaultMessage='Show markdown preview option in message input box'
/>
);
case 'EMBED_PREVIEW':
return (
<FormattedMessage
id='user.settings.advance.embed_preview'
defaultMessage='For the first web link in a message, display a preview of website content below the message, if available'
/>
);
case 'WEBRTC_PREVIEW':
return (
<FormattedMessage
......
......@@ -234,7 +234,7 @@
"admin.customization.enableCustomEmojiTitle": "Enable Custom Emoji:",
"admin.customization.enableEmojiPickerDesc": "The emoji picker allows users to select emoji to add as reactions or use in messages. Enabling the emoji picker with a large number of custom emoji may slow down performance.",
"admin.customization.enableEmojiPickerTitle": "Enable Emoji Picker:",
"admin.customization.enableLinkPreviewsDesc": "Enable users to display a preview of website content below the message, if available. When true, website previews can be enabled from Account Settings > Advanced > Preview pre-release features.",
"admin.customization.enableLinkPreviewsDesc": "Display a preview of website content below messages, when available. Users can disable these previews from Account Settings > Display > Website Link Previews.",
"admin.customization.enableLinkPreviewsTitle": "Enable Link Previews:",
"admin.customization.iosAppDownloadLinkDesc": "Add a link to download the iOS app. Users who access the site on a mobile web browser will be prompted with a page giving them the option to download the app. Leave this field blank to prevent the page from appearing.",
"admin.customization.iosAppDownloadLinkTitle": "iOS App Download Link:",
......@@ -2467,7 +2467,6 @@
"update_oauth_app.confirm": "Edit OAuth 2.0 application",
"update_oauth_app.question": "Your changes may break the existing OAuth 2.0 application. Are you sure you would like to update it?",
"upload_overlay.info": "Drop a file to upload it.",
"user.settings.advance.embed_preview": "For the first web link in a message, display a preview of website content below the message, if available",
"user.settings.advance.embed_toggle": "Show toggle for all embed previews",
"user.settings.advance.enabledFeatures": "{count, number} {count, plural, one {Feature} other {Features}} Enabled",
"user.settings.advance.formattingDesc": "If enabled, posts will be formatted to create links, show emoji, style the text, and add line breaks. By default, this setting is enabled. Changing this setting requires the page to be refreshed.",
......@@ -2519,6 +2518,10 @@
"user.settings.display.fixedWidthCentered": "Fixed width, centered",
"user.settings.display.fullScreen": "Full width",
"user.settings.display.language": "Language",
"user.settings.display.linkPreviewDesc": "When available, the first web link in a message will show a preview of the website content below the message.",
"user.settings.display.linkPreviewDisplay": "Website Link Previews",
"user.settings.display.linkPreviewOff": "Off",
"user.settings.display.linkPreviewOn": "On",
"user.settings.display.messageDisplayClean": "Standard",
"user.settings.display.messageDisplayCleanDes": "Easy to scan and read.",
"user.settings.display.messageDisplayCompact": "Compact",
......
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import React from 'react';
import {shallow} from 'enzyme';
import {savePreferences} from 'actions/user_actions.jsx';
import {mountWithIntl} from 'tests/helpers/intl-test-helper.jsx';
import UserSettingsDisplay from 'components/user_settings/user_settings_display.jsx';
jest.mock('actions/user_actions.jsx', () => ({
savePreferences: jest.fn()
}));
describe('components/user_settings/UserSettingsDisplay', () => {
global.window.mm_config = {};
const user = {
id: 'user_id',
username: 'username',
locale: 'en'
};
const requiredProps = {
user,
updateSection: jest.fn(),
updateTab: jest.fn(),
activeSection: '',
closeModal: jest.fn(),
collapseModal: jest.fn(),
setRequireConfirm: jest.fn(),
setEnforceFocus: jest.fn()
};
afterEach(() => {
global.window.mm_config = {};
});
beforeEach(() => {
global.window.mm_config.EnableLinkPreviews = 'true';
global.window.mm_config.EnableThemeSelection = 'false';
global.window.mm_config.DefaultClientLocale = 'en';
});
test('should match snapshot, no active section', () => {
const wrapper = shallow(<UserSettingsDisplay {...requiredProps}/>);
expect(wrapper).toMatchSnapshot();
});
test('should match snapshot, collapse section', () => {
const props = {...requiredProps, activeSection: 'collapse'};
const wrapper = shallow(<UserSettingsDisplay {...props}/>);
expect(wrapper).toMatchSnapshot();
});
test('should match snapshot, link preview section with EnableLinkPreviews is false', () => {
global.window.mm_config.EnableLinkPreviews = 'false';
const props = {...requiredProps, activeSection: 'linkpreview'};
const wrapper = shallow(<UserSettingsDisplay {...props}/>);
expect(wrapper).toMatchSnapshot();
});
test('should match snapshot, link preview section with EnableLinkPreviews is true', () => {
global.window.mm_config.EnableLinkPreviews = 'true';
const props = {...requiredProps, activeSection: 'linkpreview'};
const wrapper = shallow(<UserSettingsDisplay {...props}/>);
expect(wrapper).toMatchSnapshot();
});
test('should match snapshot, clock section', () => {
const props = {...requiredProps, activeSection: 'clock'};
const wrapper = shallow(<UserSettingsDisplay {...props}/>);
expect(wrapper).toMatchSnapshot();
});
test('should match snapshot, message display section', () => {
const props = {...requiredProps, activeSection: 'message_display'};
const wrapper = shallow(<UserSettingsDisplay {...props}/>);
expect(wrapper).toMatchSnapshot();
});
test('should match snapshot, channel display mode section', () => {
const props = {...requiredProps, activeSection: 'channel_display_mode'};
const wrapper = shallow(<UserSettingsDisplay {...props}/>);
expect(wrapper).toMatchSnapshot();
});
test('should match snapshot, languages section', () => {
const props = {...requiredProps, activeSection: 'languages'};
const wrapper = shallow(<UserSettingsDisplay {...props}/>);
expect(wrapper).toMatchSnapshot();
});
test('should match snapshot, theme section with EnableThemeSelection is false', () => {
global.window.mm_config.EnableThemeSelection = 'false';
const props = {...requiredProps, activeSection: 'theme'};
const wrapper = shallow(<UserSettingsDisplay {...props}/>);
expect(wrapper).toMatchSnapshot();
});
test('should match snapshot, theme section with EnableThemeSelection is true', () => {
global.window.mm_config.EnableThemeSelection = 'true';
const props = {...requiredProps, activeSection: 'theme'};
const wrapper = shallow(<UserSettingsDisplay {...props}/>);
expect(wrapper).toMatchSnapshot();
});
test('should have called handleSubmit', () => {
const updateSection = jest.fn();
const props = {...requiredProps, updateSection};
const wrapper = mountWithIntl(<UserSettingsDisplay {...props}/>);
savePreferences.mockImplementation((args, cb) => {
cb();
});
wrapper.instance().handleSubmit();
expect(updateSection).toHaveBeenCalledWith('');
});
test('should have called updateSection', () => {
const updateSection = jest.fn();
const props = {...requiredProps, updateSection};
const wrapper = mountWithIntl(<UserSettingsDisplay {...props}/>);
wrapper.instance().updateSection('');
expect(updateSection).toHaveBeenCalledWith('');
wrapper.instance().updateSection('linkpreview');
expect(updateSection).toHaveBeenCalledWith('linkpreview');
});
test('should have called closeModal', () => {
const closeModal = jest.fn();
const props = {...requiredProps, closeModal};
const wrapper = mountWithIntl(<UserSettingsDisplay {...props}/>);
wrapper.find('#closeButton').simulate('click');
expect(closeModal).toHaveBeenCalled();
});
test('should have called collapseModal', () => {
const collapseModal = jest.fn();
const props = {...requiredProps, collapseModal};
const wrapper = mountWithIntl(<UserSettingsDisplay {...props}/>);
wrapper.find('.fa-angle-left').simulate('click');
expect(collapseModal).toHaveBeenCalled();
});
test('should update militaryTime state', () => {
const wrapper = mountWithIntl(<UserSettingsDisplay {...requiredProps}/>);
wrapper.instance().handleClockRadio('false');
expect(wrapper.state('militaryTime')).toBe('false');
wrapper.instance().handleClockRadio('true');
expect(wrapper.state('militaryTime')).toBe('true');
});
test('should update channelDisplayMode state', () => {
const wrapper = mountWithIntl(<UserSettingsDisplay {...requiredProps}/>);
wrapper.instance().handleChannelDisplayModeRadio('full');
expect(wrapper.state('channelDisplayMode')).toBe('full');
wrapper.instance().handleChannelDisplayModeRadio('centered');
expect(wrapper.state('channelDisplayMode')).toBe('centered');
});
test('should update messageDisplay state', () => {
const wrapper = mountWithIntl(<UserSettingsDisplay {...requiredProps}/>);
wrapper.instance().handlemessageDisplayRadio('clean');
expect(wrapper.state('messageDisplay')).toBe('clean');
wrapper.instance().handlemessageDisplayRadio('compact');
expect(wrapper.state('messageDisplay')).toBe('compact');
});
test('should update collapseDisplay state', () => {
const wrapper = mountWithIntl(<UserSettingsDisplay {...requiredProps}/>);
wrapper.instance().handleCollapseRadio('false');
expect(wrapper.state('collapseDisplay')).toBe('false');
wrapper.instance().handleCollapseRadio('true');
expect(wrapper.state('collapseDisplay')).toBe('true');
});
test('should update linkPreviewDisplay state', () => {
const wrapper = mountWithIntl(<UserSettingsDisplay {...requiredProps}/>);
wrapper.instance().handleLinkPreviewRadio('false');
expect(wrapper.state('linkPreviewDisplay')).toBe('false');
wrapper.instance().handleLinkPreviewRadio('true');
expect(wrapper.state('linkPreviewDisplay')).toBe('true');
});
test('should update display state', () => {
const wrapper = mountWithIntl(<UserSettingsDisplay {...requiredProps}/>);
wrapper.instance().handleOnChange({display: 'linkPreviewDisplay'});
expect(wrapper.state('display')).toBe('linkPreviewDisplay');
wrapper.instance().handleOnChange({display: 'collapseDisplay'});
expect(wrapper.state('display')).toBe('collapseDisplay');
});
});
......@@ -44,6 +44,8 @@ export const Preferences = {
MESSAGE_DISPLAY_CLEAN: 'clean',
MESSAGE_DISPLAY_COMPACT: 'compact',
MESSAGE_DISPLAY_DEFAULT: 'clean',
LINK_PREVIEW_DISPLAY: 'link_previews',
LINK_PREVIEW_DISPLAY_DEFAULT: 'true',
COLLAPSE_DISPLAY: 'collapse_previews',
COLLAPSE_DISPLAY_DEFAULT: 'false',
USE_MILITARY_TIME: 'use_military_time',
......@@ -946,10 +948,6 @@ export const Constants = {
label: 'markdown_preview', // github issue: https://github.com/mattermost/platform/pull/1389
description: 'Show markdown preview option in message input box'
},
EMBED_PREVIEW: {
label: 'embed_preview',
description: 'Show preview snippet of links below message'
},
WEBRTC_PREVIEW: {
label: 'webrtc_preview',
description: 'Enable WebRTC one on one calls'
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment