From 542f0998b1c9a404041fed1ec211342b9de43e36 Mon Sep 17 00:00:00 2001 From: Saturnino Abril Date: Thu, 18 Jan 2018 01:52:28 +0800 Subject: [PATCH] ICU-584 Add support for own DM (#1357) * add support for own DM * update per comment --- .../channel_item/channel_item.js | 14 +- .../channels_list/channel_item/index.js | 9 ++ app/components/channel_intro/index.js | 2 +- .../custom_list/user_list_row/index.js | 3 +- .../user_list_row/user_list_row.js | 50 ++++-- app/screens/more_dms/index.js | 12 +- app/screens/more_dms/more_dms.js | 150 +++++++++++++----- assets/base/i18n/en.json | 1 + 8 files changed, 182 insertions(+), 59 deletions(-) diff --git a/app/components/channel_drawer/channels_list/channel_item/channel_item.js b/app/components/channel_drawer/channels_list/channel_item/channel_item.js index 261291e63..079e3cad5 100644 --- a/app/components/channel_drawer/channels_list/channel_item/channel_item.js +++ b/app/components/channel_drawer/channels_list/channel_item/channel_item.js @@ -25,6 +25,7 @@ export default class ChannelItem extends PureComponent { currentChannelId: PropTypes.string.isRequired, displayName: PropTypes.string.isRequired, fake: PropTypes.bool, + isMyUser: PropTypes.bool, isUnread: PropTypes.bool, mentions: PropTypes.number.isRequired, navigator: PropTypes.object, @@ -74,6 +75,7 @@ export default class ChannelItem extends PureComponent { channelId, currentChannelId, displayName, + isMyUser, isUnread, mentions, status, @@ -81,6 +83,16 @@ export default class ChannelItem extends PureComponent { type } = this.props; + const {intl} = this.context; + + let channelDisplayName = displayName; + if (isMyUser) { + channelDisplayName = intl.formatMessage({ + id: 'channel_header.directchannel.you', + defaultMessage: '{displayName} (you)' + }, {displayname: displayName}); + } + const style = getStyleSheet(theme); const isActive = channelId === currentChannelId; @@ -142,7 +154,7 @@ export default class ChannelItem extends PureComponent { ellipsizeMode='tail' numberOfLines={1} > - {displayName} + {channelDisplayName} {badge} diff --git a/app/components/channel_drawer/channels_list/channel_item/index.js b/app/components/channel_drawer/channels_list/channel_item/index.js index 72b071814..41ff2b4e6 100644 --- a/app/components/channel_drawer/channels_list/channel_item/index.js +++ b/app/components/channel_drawer/channels_list/channel_item/index.js @@ -3,8 +3,10 @@ import {connect} from 'react-redux'; +import {General} from 'mattermost-redux/constants'; import {getCurrentChannelId, makeGetChannel, getMyChannelMember} from 'mattermost-redux/selectors/entities/channels'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; +import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users'; import ChannelItem from './channel_item'; @@ -18,10 +20,17 @@ function makeMapStateToProps() { member = getMyChannelMember(state, ownProps.channelId); } + const currentUserId = getCurrentUserId(state); + let isMyUser = false; + if (channel.type === General.DM_CHANNEL && channel.teammate_id) { + isMyUser = channel.teammate_id === currentUserId; + } + return { currentChannelId: getCurrentChannelId(state), displayName: channel.display_name, fake: channel.fake, + isMyUser, mentions: member ? member.mention_count : 0, status: channel.status, theme: getTheme(state), diff --git a/app/components/channel_intro/index.js b/app/components/channel_intro/index.js index 2972a7eef..51900a86c 100644 --- a/app/components/channel_intro/index.js +++ b/app/components/channel_intro/index.js @@ -24,7 +24,7 @@ function makeMapStateToProps() { return ''; } - return channel.name.split('__').find((m) => m !== currentUserId); + return channel.name.split('__').find((m) => m !== currentUserId) || currentUserId; } ); diff --git a/app/components/custom_list/user_list_row/index.js b/app/components/custom_list/user_list_row/index.js index d18d4c16d..215ac72be 100644 --- a/app/components/custom_list/user_list_row/index.js +++ b/app/components/custom_list/user_list_row/index.js @@ -4,12 +4,13 @@ import {connect} from 'react-redux'; import {getTeammateNameDisplaySetting, getTheme} from 'mattermost-redux/selectors/entities/preferences'; -import {getUser} from 'mattermost-redux/selectors/entities/users'; +import {getCurrentUserId, getUser} from 'mattermost-redux/selectors/entities/users'; import UserListRow from './user_list_row'; function mapStateToProps(state, ownProps) { return { + isMyUser: getCurrentUserId(state) === ownProps.id, theme: getTheme(state), user: getUser(state, ownProps.id), teammateNameDisplay: getTeammateNameDisplaySetting(state) diff --git a/app/components/custom_list/user_list_row/user_list_row.js b/app/components/custom_list/user_list_row/user_list_row.js index e3e28bf09..8455a9e01 100644 --- a/app/components/custom_list/user_list_row/user_list_row.js +++ b/app/components/custom_list/user_list_row/user_list_row.js @@ -3,6 +3,7 @@ import React from 'react'; import PropTypes from 'prop-types'; +import {intlShape} from 'react-intl'; import { Text, View @@ -17,36 +18,63 @@ import {displayUsername} from 'mattermost-redux/utils/user_utils'; export default class UserListRow extends React.PureComponent { static propTypes = { id: PropTypes.string.isRequired, + isMyUser: PropTypes.bool.isRequired, theme: PropTypes.object.isRequired, user: PropTypes.object.isRequired, teammateNameDisplay: PropTypes.string.isRequired, ...CustomListRow.propTypes }; + static contextTypes = { + intl: intlShape + }; + onPress = () => { - this.props.onPress(this.props.id); + if (this.props.onPress) { + this.props.onPress(this.props.id); + } }; render() { - const style = getStyleFromTheme(this.props.theme); + const {formatMessage} = this.context.intl; + const { + enabled, + isMyUser, + selectable, + selected, + teammateNameDisplay, + theme, + user + } = this.props; + + const {id, username} = user; + const style = getStyleFromTheme(theme); + + let usernameDisplay = `(@${username})`; + if (isMyUser) { + usernameDisplay = formatMessage({ + id: 'mobile.more_dms.you', + defaultMessage: '(@{username} - you)' + }, {username}); + } return ( - {displayUsername(this.props.user, this.props.teammateNameDisplay)} + {displayUsername(user, teammateNameDisplay)} @@ -55,7 +83,7 @@ export default class UserListRow extends React.PureComponent { ellipsizeMode='tail' numberOfLines={1} > - {`(@${this.props.user.username})`} + {usernameDisplay} diff --git a/app/screens/more_dms/index.js b/app/screens/more_dms/index.js index ec444df67..98a71558e 100644 --- a/app/screens/more_dms/index.js +++ b/app/screens/more_dms/index.js @@ -17,10 +17,8 @@ import {getCurrentUserId, getProfilesInCurrentTeam, getUsers} from 'mattermost-r import MoreDirectMessages from './more_dms'; -function sortAndRemoveCurrentUser(profiles, currentUserId) { - const users = {...profiles}; - Reflect.deleteProperty(users, currentUserId); - return Object.values(users).sort((a, b) => { +function sortCurrentUsers(profiles) { + return Object.values(profiles).sort((a, b) => { const nameA = a.username; const nameB = b.username; @@ -30,14 +28,12 @@ function sortAndRemoveCurrentUser(profiles, currentUserId) { const getUsersInCurrentTeamForMoreDirectMessages = createSelector( getProfilesInCurrentTeam, - getCurrentUserId, - sortAndRemoveCurrentUser + sortCurrentUsers ); const getUsersForMoreDirectMessages = createSelector( getUsers, - getCurrentUserId, - sortAndRemoveCurrentUser + sortCurrentUsers ); function mapStateToProps(state) { diff --git a/app/screens/more_dms/more_dms.js b/app/screens/more_dms/more_dms.js index 6b7cceaa8..f71ad0d46 100644 --- a/app/screens/more_dms/more_dms.js +++ b/app/screens/more_dms/more_dms.js @@ -91,12 +91,16 @@ class MoreDirectMessages extends PureComponent { const {getRequest} = this.props; if (getRequest.status === RequestStatus.STARTED && nextProps.getRequest.status === RequestStatus.SUCCESS) { - const {page} = this.state; - const profiles = nextProps.profiles.slice(0, (page + 1) * General.PROFILE_CHUNK_SIZE); + const profiles = this.sliceProfiles(nextProps.profiles); this.setState({profiles, showNoResults: true}); } else if (this.state.searching && nextProps.searchRequest.status === RequestStatus.SUCCESS) { - const results = filterProfilesMatchingTerm(nextProps.profiles, this.state.term); + let results = filterProfilesMatchingTerm(nextProps.profiles, this.state.term); + + if (this.state.selectedCount > 0) { + results = this.removeCurrentUserFromProfiles(results); + } + this.setState({profiles: results, showNoResults: true}); } } @@ -112,6 +116,16 @@ class MoreDirectMessages extends PureComponent { } } + removeCurrentUserFromProfiles(profiles = []) { + return profiles.filter((profile) => { + return profile.id !== this.props.currentUserId; + }); + } + + sliceProfiles(profiles = []) { + return profiles.slice(0, (this.state.page + 1) * General.PROFILE_CHUNK_SIZE); + } + isStartEnabled = (state) => { if (state.loadingChannel) { return false; @@ -163,11 +177,20 @@ class MoreDirectMessages extends PureComponent { }; cancelSearch = () => { + const {profiles} = this.props; + + let newProfiles; + if (this.state.selectedCount > 0) { + newProfiles = this.removeCurrentUserFromProfiles(profiles); + } else { + newProfiles = this.sliceProfiles(profiles); + } + this.setState({ searching: false, term: '', page: 0, - profiles: this.props.profiles + profiles: newProfiles }); }; @@ -208,42 +231,81 @@ class MoreDirectMessages extends PureComponent { }; handleSelectUser = (id) => { - this.setState((prevState) => { - const wasSelected = prevState.selectedIds[id]; + const {currentUserId} = this.props; - // Prevent selecting too many users - if (!wasSelected && Object.keys(prevState.selectedIds).length >= General.MAX_USERS_IN_GM - 1) { - return {}; - } + if (id === currentUserId) { + const selectedId = {}; + selectedId[currentUserId] = true; - const selectedIds = {...prevState.selectedIds}; + this.startConversation(selectedId); + } else { + this.setState((prevState) => { + const { + profiles, + selectedCount, + selectedIds + } = prevState; - if (wasSelected) { - Reflect.deleteProperty(selectedIds, id); - } else { - selectedIds[id] = true; - } + const wasSelected = selectedIds[id]; - return { - selectedIds, - selectedCount: Object.keys(selectedIds).length - }; - }); + // Prevent selecting too many users + if (!wasSelected && Object.keys(selectedIds).length >= General.MAX_USERS_IN_GM - 1) { + return {}; + } + + const newSelectedIds = Object.assign({}, selectedIds); + + let newProfiles = profiles; + if (wasSelected) { + Reflect.deleteProperty(newSelectedIds, id); + if (selectedCount === 1) { + newProfiles = this.sliceProfiles(this.props.profiles); + } + } else { + newSelectedIds[id] = true; + newProfiles = this.removeCurrentUserFromProfiles(profiles); + } + + return { + profiles: newProfiles, + selectedIds: newSelectedIds, + selectedCount: Object.keys(newSelectedIds).length + }; + }); + } }; handleRemoveUser = (id) => { this.setState((prevState) => { - const selectedIds = {...prevState.selectedIds}; - Reflect.deleteProperty(selectedIds, id); + const { + profiles, + selectedCount, + selectedIds + } = prevState; + + const newSelectedIds = Object.assign({}, selectedIds); + + Reflect.deleteProperty(newSelectedIds, id); + + let newProfiles = profiles; + if (selectedCount === 1) { + newProfiles = this.sliceProfiles(this.props.profiles); + } return { - selectedIds, - selectedCount: Object.keys(selectedIds).length + profiles: newProfiles, + selectedIds: newSelectedIds, + selectedCount: Object.keys(newSelectedIds).length }; }); } - startConversation = async () => { + startConversation = async (selectedId) => { + const { + currentDisplayName, + actions + } = this.props; + if (this.state.loadingChannel) { return; } @@ -253,9 +315,9 @@ class MoreDirectMessages extends PureComponent { }); // Save the current channel display name in case it fails - const currentChannelDisplayName = this.props.currentDisplayName; + const currentChannelDisplayName = currentDisplayName; - const selectedIds = Object.keys(this.state.selectedIds); + const selectedIds = selectedId ? Object.keys(selectedId) : Object.keys(this.state.selectedIds); let success; if (selectedIds.length === 0) { success = false; @@ -275,19 +337,27 @@ class MoreDirectMessages extends PureComponent { loadingChannel: false }); - this.props.actions.setChannelDisplayName(currentChannelDisplayName); + actions.setChannelDisplayName(currentChannelDisplayName); } }; makeGroupChannel = async (ids) => { - const result = await this.props.actions.makeGroupChannel(ids); + const { + actions, + allProfiles, + currentUserId, + intl, + teammateNameDisplay + } = this.props; - const displayName = getGroupDisplayNameFromUserIds(ids, this.props.allProfiles, this.props.currentUserId, this.props.teammateNameDisplay); - this.props.actions.setChannelDisplayName(displayName); + const result = await actions.makeGroupChannel(ids); + + const displayName = getGroupDisplayNameFromUserIds(ids, allProfiles, currentUserId, teammateNameDisplay); + actions.setChannelDisplayName(displayName); if (result.error) { alertErrorWithFallback( - this.props.intl, + intl, result.error, { id: 'mobile.open_gm.error', @@ -300,16 +370,22 @@ class MoreDirectMessages extends PureComponent { }; makeDirectChannel = async (id) => { + const { + actions, + intl, + teammateNameDisplay + } = this.props; + const user = this.state.profiles[id]; - const displayName = displayUsername(user, this.props.teammateNameDisplay); - this.props.actions.setChannelDisplayName(displayName); + const displayName = displayUsername(user, teammateNameDisplay); + actions.setChannelDisplayName(displayName); - const result = await this.props.actions.makeDirectChannel(id); + const result = await actions.makeDirectChannel(id); if (result.error) { alertErrorWithFallback( - this.props.intl, + intl, result.error, { id: 'mobile.open_dm.error', diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index 0879b19a0..c45d4b590 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -2036,6 +2036,7 @@ "mobile.markdown.link.copy_url": "Copy URL", "mobile.mention.copy_mention": "Copy Mention", "mobile.more_dms.start": "Start", + "mobile.more_dms.you": "(@{username} - you)", "mobile.more_dms.title": "New Conversation", "mobile.notice_mobile_link": "mobile apps", "mobile.notice_platform_link": "platform",