diff --git a/app/components/combined_system_message/__snapshots__/combined_system_message.test.js.snap b/app/components/combined_system_message/__snapshots__/combined_system_message.test.js.snap new file mode 100644 index 000000000..36f4f854a --- /dev/null +++ b/app/components/combined_system_message/__snapshots__/combined_system_message.test.js.snap @@ -0,0 +1,79 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CombinedSystemMessage should match snapshot 1`] = ` + + + + + + + +`; + +exports[`CombinedSystemMessage should match snapshot 2`] = ` + + + + + + . + +`; diff --git a/app/components/combined_system_message/__snapshots__/last_users.test.js.snap b/app/components/combined_system_message/__snapshots__/last_users.test.js.snap new file mode 100644 index 000000000..562d56232 --- /dev/null +++ b/app/components/combined_system_message/__snapshots__/last_users.test.js.snap @@ -0,0 +1,107 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`LastUsers should match snapshot 1`] = ` + + + + + + + +`; + +exports[`LastUsers should match snapshot 2`] = ` + + + + + + + + + + + +`; diff --git a/app/components/combined_system_message/combined_system_message.js b/app/components/combined_system_message/combined_system_message.js new file mode 100644 index 000000000..a7be1859c --- /dev/null +++ b/app/components/combined_system_message/combined_system_message.js @@ -0,0 +1,350 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import PropTypes from 'prop-types'; +import React from 'react'; +import {Text} from 'react-native'; +import {intlShape} from 'react-intl'; + +import {Posts} from 'mattermost-redux/constants'; +import {displayUsername} from 'mattermost-redux/utils/user_utils'; + +import CustomPropTypes from 'app/constants/custom_prop_types'; + +import {makeStyleSheetFromTheme} from 'app/utils/theme'; + +import FormattedText from 'app/components/formatted_text'; + +import LastUsers from './last_users'; + +const { + JOIN_CHANNEL, ADD_TO_CHANNEL, REMOVE_FROM_CHANNEL, LEAVE_CHANNEL, + JOIN_TEAM, ADD_TO_TEAM, REMOVE_FROM_TEAM, LEAVE_TEAM, +} = Posts.POST_TYPES; + +const postTypeMessage = { + [JOIN_CHANNEL]: { + one: { + id: ['mobile.combined_system_message.first_user', 'mobile.combined_system_message.joined_channel'], + defaultMessage: ['{firstUser} ', 'joined the channel'], + }, + two: { + id: ['mobile.combined_system_message.first_user_and_second_user', 'mobile.combined_system_message.joined_channel'], + defaultMessage: ['{firstUser} and {secondUser} ', 'joined the channel'], + }, + many_expanded: { + id: ['mobile.combined_system_message.users_and_last_user', 'mobile.combined_system_message.joined_channel'], + defaultMessage: ['{users} and {lastUser} ', 'joined the channel'], + }, + }, + [ADD_TO_CHANNEL]: { + one: { + id: ['mobile.combined_system_message.first_user', 'mobile.combined_system_message.added_to_channel', 'mobile.combined_system_message.by_actor'], + defaultMessage: ['{firstUser} ', 'added to the channel', ' by {actor}.'], + }, + one_you: { + id: ['mobile.combined_system_message.you_were', 'mobile.combined_system_message.added_to_channel', 'mobile.combined_system_message.by_actor'], + defaultMessage: ['You were ', 'added to the channel', ' by {actor}.'], + }, + two: { + id: ['mobile.combined_system_message.first_user_and_second_user', 'mobile.combined_system_message.added_to_channel', 'mobile.combined_system_message.by_actor'], + defaultMessage: ['{firstUser} and {secondUser} ', 'added to the channel', ' by {actor}.'], + }, + many_expanded: { + id: ['mobile.combined_system_message.users_and_last_user', 'mobile.combined_system_message.added_to_channel', 'mobile.combined_system_message.by_actor'], + defaultMessage: ['{users} and {lastUser} ', 'added to the channel', ' by {actor}.'], + }, + }, + [REMOVE_FROM_CHANNEL]: { + one: { + id: ['mobile.combined_system_message.first_user_was', 'mobile.combined_system_message.removed_from_channel'], + defaultMessage: ['{firstUser} was ', 'removed from the channel'], + }, + one_you: { + id: ['mobile.combined_system_message.you_were', 'mobile.combined_system_message.removed_from_channel'], + defaultMessage: ['You were ', 'removed from the channel'], + }, + two: { + id: ['mobile.combined_system_message.first_user_and_second_user_were', 'mobile.combined_system_message.removed_from_channel'], + defaultMessage: ['{firstUser} and {secondUser} were ', 'removed from the channel'], + }, + many_expanded: { + id: ['mobile.combined_system_message.users_and_last_user_were', 'mobile.combined_system_message.removed_from_channel'], + defaultMessage: ['{users} and {lastUser} were ', 'removed from the channel'], + }, + }, + [LEAVE_CHANNEL]: { + one: { + id: ['mobile.combined_system_message.first_user', 'mobile.combined_system_message.left_channel'], + defaultMessage: ['{firstUser} ', 'left the channel'], + }, + two: { + id: ['mobile.combined_system_message.first_user_and_second_user', 'mobile.combined_system_message.left_channel'], + defaultMessage: ['{firstUser} and {secondUser} ', 'left the channel'], + }, + many_expanded: { + id: ['mobile.combined_system_message.users_and_last_user', 'mobile.combined_system_message.left_channel'], + defaultMessage: ['{users} and {lastUser} ', 'left the channel'], + }, + }, + [JOIN_TEAM]: { + one: { + id: ['mobile.combined_system_message.first_user', 'mobile.combined_system_message.joined_team'], + defaultMessage: ['{firstUser} ', 'joined the team'], + }, + two: { + id: ['mobile.combined_system_message.first_user_and_second_user', 'mobile.combined_system_message.joined_team'], + defaultMessage: ['{firstUser} and {secondUser} ', 'joined the team'], + }, + many_expanded: { + id: ['mobile.combined_system_message.users_and_last_user', 'mobile.combined_system_message.joined_team'], + defaultMessage: ['{users} and {lastUser} ', 'joined the team'], + }, + }, + [ADD_TO_TEAM]: { + one: { + id: ['mobile.combined_system_message.first_user', 'mobile.combined_system_message.added_to_team', 'mobile.combined_system_message.by_actor'], + defaultMessage: ['{firstUser} ', 'added to the team', ' by {actor}.'], + }, + one_you: { + id: ['mobile.combined_system_message.you_were', 'mobile.combined_system_message.added_to_team', 'mobile.combined_system_message.by_actor'], + defaultMessage: ['You were ', 'added to the team', ' by {actor}.'], + }, + two: { + id: ['mobile.combined_system_message.first_user_and_second_user', 'mobile.combined_system_message.added_to_team', 'mobile.combined_system_message.by_actor'], + defaultMessage: ['{firstUser} and {secondUser} ', 'added to the team', ' by {actor}.'], + }, + many_expanded: { + id: ['mobile.combined_system_message.users_and_last_user', 'mobile.combined_system_message.added_to_team', 'mobile.combined_system_message.by_actor'], + defaultMessage: ['{users} and {lastUser} ', 'added to the team', ' by {actor}.'], + }, + }, + [REMOVE_FROM_TEAM]: { + one: { + id: ['mobile.combined_system_message.first_user_was', 'mobile.combined_system_message.removed_from_team'], + defaultMessage: ['{firstUser} was ', 'removed from the team'], + }, + one_you: { + id: ['mobile.combined_system_message.you_were', 'mobile.combined_system_message.removed_from_team'], + defaultMessage: ['You were ', 'removed from the team'], + }, + two: { + id: ['mobile.combined_system_message.first_user_and_second_user_were', 'mobile.combined_system_message.removed_from_team'], + defaultMessage: ['{firstUser} and {secondUser} were ', 'removed from the team'], + }, + many_expanded: { + id: ['mobile.combined_system_message.users_and_last_user_were', 'mobile.combined_system_message.removed_from_team'], + defaultMessage: ['{users} and {lastUser} were ', 'removed from the team'], + }, + }, + [LEAVE_TEAM]: { + one: { + id: ['mobile.combined_system_message.first_user', 'mobile.combined_system_message.left_team'], + defaultMessage: ['{firstUser} ', 'left the team'], + }, + two: { + id: ['mobile.combined_system_message.first_user_and_second_user', 'mobile.combined_system_message.left_team'], + defaultMessage: ['{firstUser} and {secondUser} ', 'left the team'], + }, + many_expanded: { + id: ['mobile.combined_system_message.users_and_last_user', 'mobile.combined_system_message.left_team'], + defaultMessage: ['{users} and {lastUser} ', 'left the team'], + }, + }, +}; + +export default class CombinedSystemMessage extends React.PureComponent { + static propTypes = { + actions: PropTypes.shape({ + getProfilesByIds: PropTypes.func.isRequired, + }).isRequired, + allUserIds: PropTypes.array.isRequired, + currentUserId: PropTypes.string.isRequired, + linkStyle: CustomPropTypes.Style, + messageData: PropTypes.array.isRequired, + teammateNameDisplay: PropTypes.string.isRequired, + theme: PropTypes.object.isRequired, + }; + + static contextTypes = { + intl: intlShape, + }; + + constructor(props) { + super(props); + + this.state = { + userProfiles: [], + }; + } + + componentDidMount() { + this.loadUserProfiles(); + } + + loadUserProfiles = async () => { + const { + actions, + allUserIds, + } = this.props; + const {data: userProfiles} = await actions.getProfilesByIds(allUserIds); + + this.setState({userProfiles}); + } + + getAllUsersDisplayName = () => { + const {userProfiles} = this.state; + const { + allUserIds, + currentUserId, + teammateNameDisplay, + } = this.props; + const {formatMessage} = this.context.intl; + const usersDisplayName = userProfiles.reduce((acc, user) => { + acc[user.id] = displayUsername(user, teammateNameDisplay, true); + return acc; + }, {}); + + const includesCurrentUser = allUserIds.includes(currentUserId); + if (includesCurrentUser) { + usersDisplayName[currentUserId] = formatMessage({id: 'mobile.combined_system_message.you', defaultMessage: 'You'}); + } + + return usersDisplayName; + } + + getDisplayNameByIds = (userIds = []) => { + const {currentUserId} = this.props; + const usersDisplayName = this.getAllUsersDisplayName(); + + const displayNames = userIds. + filter((userId) => { + return userId !== currentUserId; + }). + map((userId) => { + return usersDisplayName[userId]; + }); + + const includesCurrentUser = userIds.includes(currentUserId); + if (includesCurrentUser) { + displayNames.unshift(usersDisplayName[currentUserId]); + } + + return displayNames; + } + + renderSystemMessage(postType, userIds, actorId, style) { + const usersDisplayName = this.getDisplayNameByIds(userIds); + let actorDisplayName = actorId ? this.getDisplayNameByIds([actorId])[0] : ''; + if (actorDisplayName && actorId === this.props.currentUserId) { + actorDisplayName = actorDisplayName.toLowerCase(); + } + + const firstUser = usersDisplayName[0]; + const numOthers = usersDisplayName.length - 1; + + let formattedMessage; + if (numOthers === 0) { + formattedMessage = this.renderFormattedMessage( + postTypeMessage[postType].one, + firstUser, + null, + actorDisplayName, + style, + ); + + if ( + userIds[0] === this.props.currentUserId && + postTypeMessage[postType].one_you + ) { + formattedMessage = this.renderFormattedMessage( + postTypeMessage[postType].one_you, + null, + null, + actorDisplayName, + style, + ); + } + } else if (numOthers === 1) { + formattedMessage = this.renderFormattedMessage( + postTypeMessage[postType].two, + firstUser, + usersDisplayName[1], + actorDisplayName, + style, + ); + } else { + formattedMessage = ( + + ); + } + + return formattedMessage; + } + + renderFormattedMessage = (localeFormat, firstUser, secondUser, actor, style) => { + return ( + + + + + + {localeFormat.id[2] ? ( + + ) : ('.') + } + + ); + } + + render() { + const { + linkStyle, + messageData, + theme, + } = this.props; + const style = getStyleSheet(theme); + + return ( + + {messageData.map(({postType, userIds, actorId}) => { + return ( + + {this.renderSystemMessage(postType, userIds, actorId, {activityType: style.activityType, link: linkStyle})} + + ); + })} + + ); + } +} + +const getStyleSheet = makeStyleSheetFromTheme((theme) => { + return { + activityType: { + color: theme.centerChannelColor, + fontSize: 14, + fontWeight: 'bold', + }, + }; +}); diff --git a/app/components/combined_system_message/combined_system_message.test.js b/app/components/combined_system_message/combined_system_message.test.js new file mode 100644 index 000000000..8b02855cd --- /dev/null +++ b/app/components/combined_system_message/combined_system_message.test.js @@ -0,0 +1,64 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import React from 'react'; +import {configure} from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; +configure({adapter: new Adapter()}); + +import {shallowWithIntl} from 'test/intl-test-helper'; + +import {Posts} from 'mattermost-redux/constants'; + +import CombinedSystemMessage from './combined_system_message'; + +describe('CombinedSystemMessage', () => { + const baseProps = { + actions: { + getProfilesByIds: () => {}, // eslint-disable-line no-empty-function + }, + allUserIds: ['user_id_1', 'user_id_2', 'user_id_3'], + currentUserId: 'user_id_3', + linkStyle: 1, + messageData: [ + {postType: Posts.POST_TYPES.ADD_TO_TEAM, userIds: ['user_id_1'], actorId: 'user_id_2'}, + ], + teammateNameDisplay: 'username', + theme: {centerChannelColor: '#aaa'}, + }; + + test('should match snapshot', () => { + const props = { + ...baseProps, + actions: {getProfilesByIds: jest.fn(() => Promise.resolve({data: true}))}, + }; + const wrapper = shallowWithIntl( + + ); + wrapper.setState({userProfiles: [{id: 'user_id_1', username: 'user1'}, {id: 'user_id_2', username: 'user2'}, {id: 'user_id_3', username: 'user3'}]}); + + const {postType, userIds, actorId} = baseProps.messageData[0]; + expect(wrapper.instance().renderSystemMessage(postType, userIds, actorId, {activityType: {fontSize: 14}}, 1)).toMatchSnapshot(); + + // on componentDidMount + expect(props.actions.getProfilesByIds).toHaveBeenCalledTimes(1); + expect(props.actions.getProfilesByIds).toHaveBeenCalledWith(props.allUserIds); + }); + + test('should match snapshot', () => { + const props = { + ...baseProps, + actions: {getProfilesByIds: jest.fn(() => Promise.resolve({data: true}))}, + }; + const localeFormat = { + id: ['combined_system_message.first_user_and_second_user_were', 'combined_system_message.removed_from_team'], + defaultMessage: ['{firstUser} and {secondUser} were ', 'removed from the team'], + }; + const wrapper = shallowWithIntl( + + ); + + wrapper.setState({userProfiles: [{id: 'user_id_1', username: 'user1'}, {id: 'user_id_2', username: 'user2'}, {id: 'user_id_3', username: 'user3'}]}); + expect(wrapper.instance().renderFormattedMessage(localeFormat, 'first_user', 'second_user', 'actor', {activityType: {fontSize: 14}})).toMatchSnapshot(); + }); +}); diff --git a/app/components/combined_system_message/index.js b/app/components/combined_system_message/index.js new file mode 100644 index 000000000..873625e49 --- /dev/null +++ b/app/components/combined_system_message/index.js @@ -0,0 +1,29 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {connect} from 'react-redux'; +import {bindActionCreators} from 'redux'; + +import {getProfilesByIds} from 'mattermost-redux/actions/users'; + +import {getTeammateNameDisplaySetting} from 'mattermost-redux/selectors/entities/preferences'; +import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users'; + +import CombinedSystemMessage from './combined_system_message'; + +function mapStateToProps(state) { + return { + currentUserId: getCurrentUserId(state), + teammateNameDisplay: getTeammateNameDisplaySetting(state), + }; +} + +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + getProfilesByIds, + }, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(CombinedSystemMessage); diff --git a/app/components/combined_system_message/last_users.js b/app/components/combined_system_message/last_users.js new file mode 100644 index 000000000..e22779d8c --- /dev/null +++ b/app/components/combined_system_message/last_users.js @@ -0,0 +1,159 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import PropTypes from 'prop-types'; +import React from 'react'; +import {Text} from 'react-native'; + +import {Posts} from 'mattermost-redux/constants'; + +import FormattedText from 'app/components/formatted_text'; + +const typeMessage = { + [Posts.POST_TYPES.ADD_TO_CHANNEL]: { + id: ['mobile.combined_system_message.were', 'mobile.combined_system_message.added_to_channel', 'mobile.combined_system_message.by_actor'], + defaultMessage: ['were ', 'added to the channel', ' by {actor}.'], + }, + [Posts.POST_TYPES.JOIN_CHANNEL]: { + id: ['', 'mobile.combined_system_message.joined_channel'], + defaultMessage: ['', 'joined the channel'], + }, + [Posts.POST_TYPES.LEAVE_CHANNEL]: { + id: ['', 'mobile.combined_system_message.left_channel', ''], + defaultMessage: ['', 'left the channel'], + }, + [Posts.POST_TYPES.REMOVE_FROM_CHANNEL]: { + id: ['mobile.combined_system_message.were', 'mobile.combined_system_message.removed_from_channel'], + defaultMessage: ['were ', 'removed from the channel'], + }, + [Posts.POST_TYPES.ADD_TO_TEAM]: { + id: ['mobile.combined_system_message.were', 'mobile.combined_system_message.added_to_team', 'mobile.combined_system_message.by_actor'], + defaultMessage: ['were ', 'added to the team', ' by {actor}.'], + }, + [Posts.POST_TYPES.JOIN_TEAM]: { + id: ['', 'mobile.combined_system_message.joined_team'], + defaultMessage: ['', 'joined the team'], + }, + [Posts.POST_TYPES.LEAVE_TEAM]: { + id: ['', 'mobile.combined_system_message.left_team'], + defaultMessage: ['', 'left the team'], + }, + [Posts.POST_TYPES.REMOVE_FROM_TEAM]: { + id: ['', 'mobile.combined_system_message.removed_from_team'], + defaultMessage: ['were ', 'removed from the team'], + }, +}; + +export default class LastUsers extends React.PureComponent { + static propTypes = { + actor: PropTypes.string, + expandedLocale: PropTypes.object.isRequired, + postType: PropTypes.string.isRequired, + style: PropTypes.object.isRequired, + userDisplayNames: PropTypes.array.isRequired, + }; + + constructor(props) { + super(props); + + this.state = { + expand: false, + }; + } + + handleOnClick = (e) => { + e.preventDefault(); + + this.setState({expand: true}); + } + + renderExpandedView = (expandedLocale, userDisplayNames, actor, lastIndex, style) => { + return ( + + + + + + {expandedLocale.id[2] ? ( + + ) : ('.') + } + + ); + } + + renderCollapsedView = (postType, userDisplayNames, actor, lastIndex, style) => { + return ( + + + + + + {typeMessage[postType].id[0] && + + } + + + + {typeMessage[postType].id[2] ? ( + + ) : ('.') + } + + ); + } + + render() { + const {expand} = this.state; + const { + actor, + expandedLocale, + postType, + userDisplayNames, + style, + } = this.props; + + const lastIndex = userDisplayNames.length - 1; + + if (expand) { + return this.renderExpandedView(expandedLocale, userDisplayNames, actor, lastIndex, style); + } + + return this.renderCollapsedView(postType, userDisplayNames, actor, lastIndex, style); + } +} diff --git a/app/components/combined_system_message/last_users.test.js b/app/components/combined_system_message/last_users.test.js new file mode 100644 index 000000000..ed48ae71d --- /dev/null +++ b/app/components/combined_system_message/last_users.test.js @@ -0,0 +1,45 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import React from 'react'; +import {configure} from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; +configure({adapter: new Adapter()}); + +import {Posts} from 'mattermost-redux/constants'; + +import {shallowWithIntl} from 'test/intl-test-helper'; + +import LastUsers from './last_users'; + +describe('LastUsers', () => { + const baseProps = { + actor: 'actor', + expandedLocale: {id: 'expanded_locale_id', defaultMessage: 'Expanded Locale'}, + postType: Posts.POST_TYPES.ADD_TO_TEAM, + style: {activityType: {fontSize: 14}, link: 1}, + userDisplayNames: ['User One', 'User Two'], + }; + + test('should match snapshot', () => { + const wrapper = shallowWithIntl( + + ); + + const expanded = wrapper.instance().renderExpandedView(baseProps.expandedLocale, baseProps.userDisplayNames, baseProps.actor, 1, baseProps.style); + expect(expanded).toMatchSnapshot(); + + const collapsed = wrapper.instance().renderCollapsedView(baseProps.postType, baseProps.userDisplayNames, baseProps.actor, 1, baseProps.style); + expect(collapsed).toMatchSnapshot(); + }); + + test('should match state on handleOnClick', () => { + const wrapper = shallowWithIntl( + + ); + + wrapper.setState({expand: false}); + wrapper.instance().handleOnClick({preventDefault: jest.fn()}); + expect(wrapper.state('expand')).toEqual(true); + }); +}); diff --git a/app/components/post_body/index.js b/app/components/post_body/index.js index dead936e5..484d0d76e 100644 --- a/app/components/post_body/index.js +++ b/app/components/post_body/index.js @@ -66,6 +66,7 @@ function mapStateToProps(state, ownProps) { return { postProps: post.props || {}, + postType: post.type || '', fileIds: post.file_ids, hasBeenDeleted: post.state === Posts.POST_DELETED, hasBeenEdited: isEdited(post), diff --git a/app/components/post_body/post_body.js b/app/components/post_body/post_body.js index e60f51913..e953f1ee6 100644 --- a/app/components/post_body/post_body.js +++ b/app/components/post_body/post_body.js @@ -15,6 +15,9 @@ import {intlShape} from 'react-intl'; import Icon from 'react-native-vector-icons/Ionicons'; import LinearGradient from 'react-native-linear-gradient'; +import {Posts} from 'mattermost-redux/constants'; + +import CombinedSystemMessage from 'app/components/combined_system_message'; import FormattedText from 'app/components/formatted_text'; import Markdown from 'app/components/markdown'; import OptionsContext from 'app/components/options_context'; @@ -65,6 +68,7 @@ export default class PostBody extends PureComponent { onPress: PropTypes.func, postId: PropTypes.string.isRequired, postProps: PropTypes.object, + postType: PropTypes.string, renderReplyBar: PropTypes.func, showAddReaction: PropTypes.bool, showLongPost: PropTypes.bool.isRequired, @@ -401,6 +405,8 @@ export default class PostBody extends PureComponent { onFailedPostPress, onPermalinkPress, onPress, + postProps, + postType, renderReplyBar, theme, toggleSelected, @@ -434,6 +440,20 @@ export default class PostBody extends PureComponent { body = ({messageComponent}); } else if (isPostAddChannelMember) { messageComponent = this.renderAddChannelMember(style, textStyles); + } else if (postType === Posts.POST_TYPES.COMBINED_USER_ACTIVITY) { + const {allUserIds, messageData} = postProps.user_activity; + messageComponent = ( + + + + + + ); } else if (message.length) { messageComponent = ( diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index 82a9d75e2..e5b021516 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -2125,6 +2125,26 @@ "mobile.client_upgrade.no_upgrade_subtitle": "You already have the latest version.", "mobile.client_upgrade.no_upgrade_title": "Your App Is Up to Date", "mobile.client_upgrade.upgrade": "Update", + "mobile.combined_system_message.first_user": "{firstUser} ", + "mobile.combined_system_message.first_user_was": "{firstUser} was ", + "mobile.combined_system_message.first_user_and_second_user": "{firstUser} and {secondUser} ", + "mobile.combined_system_message.first_user_and_second_user_were": "{firstUser} and {secondUser} were ", + "mobile.combined_system_message.users_and_last_user": "{users} and {lastUser} ", + "mobile.combined_system_message.users_and_last_user_were": "{users} and {lastUser} were ", + "mobile.combined_system_message.by_actor": " by {actor}.", + "mobile.combined_system_message.you": "You", + "mobile.combined_system_message.you_were": "You were ", + "mobile.combined_system_message.were": "were ", + "mobile.combined_system_message.first_user_and": "{firstUser} and ", + "mobile.combined_system_message.others": "{numOthers} others ", + "mobile.combined_system_message.joined_channel": "joined the channel", + "mobile.combined_system_message.added_to_channel": "added to the channel", + "mobile.combined_system_message.removed_from_channel": "removed from the channel", + "mobile.combined_system_message.left_channel": "left the channel", + "mobile.combined_system_message.joined_team": "joined the team", + "mobile.combined_system_message.added_to_team": "added to the team", + "mobile.combined_system_message.removed_from_team": "removed from the team", + "mobile.combined_system_message.left_team": "left the team", "mobile.commands.error_title": "Error Executing Command", "mobile.components.channels_list_view.yourChannels": "Your channels:", "mobile.components.error_list.dismiss_all": "Dismiss All", diff --git a/test/intl-test-helper.js b/test/intl-test-helper.js new file mode 100644 index 000000000..cd575ba69 --- /dev/null +++ b/test/intl-test-helper.js @@ -0,0 +1,22 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {IntlProvider, intlShape} from 'react-intl'; +import {mount, shallow} from 'enzyme'; + +const intlProvider = new IntlProvider({locale: 'en'}, {}); +const {intl} = intlProvider.getChildContext(); + +export function shallowWithIntl(node, {context} = {}) { + return shallow(React.cloneElement(node, {intl}), { + context: Object.assign({}, context, {intl}), + }); +} + +export function mountWithIntl(node, {context, childContextTypes} = {}) { + return mount(React.cloneElement(node, {intl}), { + context: Object.assign({}, context, {intl}), + childContextTypes: Object.assign({}, {intl: intlShape}, childContextTypes), + }); +}