From 9bd89930b70919fee5321794476eca6da3c4b259 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Thu, 27 Sep 2018 18:38:07 +0200 Subject: [PATCH] MM-11567: Autocomplete search in: for DMs and GMs (#2133) * MM-11567: Autocomplete search in: for DMs and GMs * More robust behavior on older versions and a fix on autocompletion * Moving get display_name for search to a selector --- .../channel_mention/channel_mention.js | 20 +++++++++---- .../autocomplete/channel_mention/index.js | 4 +++ .../channel_mention_item.js | 25 ++++++++++++++-- .../channel_mention_item/index.js | 6 +++- app/selectors/autocomplete.js | 30 +++++++++++++++++++ app/selectors/channel.js | 10 +++++++ assets/base/i18n/en.json | 1 + 7 files changed, 88 insertions(+), 8 deletions(-) diff --git a/app/components/autocomplete/channel_mention/channel_mention.js b/app/components/autocomplete/channel_mention/channel_mention.js index 03604bc8f..1d718017a 100644 --- a/app/components/autocomplete/channel_mention/channel_mention.js +++ b/app/components/autocomplete/channel_mention/channel_mention.js @@ -34,6 +34,7 @@ export default class ChannelMention extends PureComponent { onResultCountChange: PropTypes.func.isRequired, privateChannels: PropTypes.array, publicChannels: PropTypes.array, + directAndGroupMessages: PropTypes.array, deletedPublicChannels: PropTypes.instanceOf(Set), requestStatus: PropTypes.string.isRequired, theme: PropTypes.object.isRequired, @@ -55,16 +56,15 @@ export default class ChannelMention extends PureComponent { } runSearch = debounce((currentTeamId, matchTerm) => { - if (!isMinimumServerVersion(this.props.serverVersion, 5, 4)) { - this.props.actions.searchChannels(currentTeamId, matchTerm); + if (isMinimumServerVersion(this.props.serverVersion, 5, 4)) { + this.props.actions.autocompleteChannelsForSearch(currentTeamId, matchTerm); return; } - - this.props.actions.autocompleteChannelsForSearch(currentTeamId, matchTerm); + this.props.actions.searchChannels(currentTeamId, matchTerm); }, 200); componentWillReceiveProps(nextProps) { - const {isSearch, matchTerm, myChannels, otherChannels, privateChannels, publicChannels, requestStatus, myMembers, deletedPublicChannels} = nextProps; + const {isSearch, matchTerm, myChannels, otherChannels, privateChannels, publicChannels, directAndGroupMessages, requestStatus, myMembers, deletedPublicChannels} = nextProps; if ((matchTerm !== this.props.matchTerm && matchTerm === null) || this.state.mentionComplete) { // if the term changes but is null or the mention has been completed we render this component as null @@ -91,6 +91,7 @@ export default class ChannelMention extends PureComponent { if (requestStatus !== RequestStatus.STARTED && (myChannels !== this.props.myChannels || otherChannels !== this.props.otherChannels || privateChannels !== this.props.privateChannels || publicChannels !== this.props.publicChannels || + directAndGroupMessages !== this.props.directAndGroupMessages || myMembers !== this.props.myMembers || deletedPublicChannels !== this.props.deletedPublicChannels)) { // if the request is complete and the term is not null we show the autocomplete const sections = []; @@ -112,6 +113,15 @@ export default class ChannelMention extends PureComponent { key: 'privateChannels', }); } + + if (directAndGroupMessages.length && isMinimumServerVersion(this.props.serverVersion, 5, 4)) { + sections.push({ + id: t('suggestion.search.direct'), + defaultMessage: 'Direct Messages', + data: directAndGroupMessages, + key: 'directAndGroupMessages', + }); + } } else { if (myChannels.length) { sections.push({ diff --git a/app/components/autocomplete/channel_mention/index.js b/app/components/autocomplete/channel_mention/index.js index 1ac50077a..ab988decd 100644 --- a/app/components/autocomplete/channel_mention/index.js +++ b/app/components/autocomplete/channel_mention/index.js @@ -13,6 +13,7 @@ import { filterOtherChannels, filterPublicChannels, filterPrivateChannels, + filterDirectAndGroupMessages, getDeletedPublicChannelsIds, getMatchTermForChannelMention, } from 'app/selectors/autocomplete'; @@ -30,9 +31,11 @@ function mapStateToProps(state, ownProps) { let otherChannels; let publicChannels; let privateChannels; + let directAndGroupMessages; if (isSearch) { publicChannels = filterPublicChannels(state, matchTerm); privateChannels = filterPrivateChannels(state, matchTerm); + directAndGroupMessages = filterDirectAndGroupMessages(state, matchTerm); } else { myChannels = filterMyChannels(state, matchTerm); otherChannels = filterOtherChannels(state, matchTerm); @@ -45,6 +48,7 @@ function mapStateToProps(state, ownProps) { publicChannels, deletedPublicChannels: getDeletedPublicChannelsIds(state), privateChannels, + directAndGroupMessages, currentTeamId: getCurrentTeamId(state), matchTerm, requestStatus: state.requests.channels.getChannels.status, diff --git a/app/components/autocomplete/channel_mention_item/channel_mention_item.js b/app/components/autocomplete/channel_mention_item/channel_mention_item.js index a8966a83e..4257a3c72 100644 --- a/app/components/autocomplete/channel_mention_item/channel_mention_item.js +++ b/app/components/autocomplete/channel_mention_item/channel_mention_item.js @@ -8,6 +8,8 @@ import { TouchableOpacity, } from 'react-native'; +import {General} from 'mattermost-redux/constants'; + import {makeStyleSheetFromTheme} from 'app/utils/theme'; export default class ChannelMentionItem extends PureComponent { @@ -15,13 +17,20 @@ export default class ChannelMentionItem extends PureComponent { channelId: PropTypes.string.isRequired, displayName: PropTypes.string, name: PropTypes.string, + type: PropTypes.string, onPress: PropTypes.func.isRequired, theme: PropTypes.object.isRequired, }; completeMention = () => { - const {onPress, name} = this.props; - onPress(name); + const {onPress, displayName, name, type} = this.props; + if (type === General.DM_CHANNEL) { + onPress('@' + displayName.replace(/ /g, '')); + } else if (type === General.DM_CHANNEL) { + onPress('@' + displayName.replace(/ /g, '')); + } else { + onPress(name); + } }; render() { @@ -30,10 +39,22 @@ export default class ChannelMentionItem extends PureComponent { displayName, name, theme, + type, } = this.props; const style = getStyleFromTheme(theme); + if (type === General.DM_CHANNEL || type === General.GM_CHANNEL) { + return ( + + {'@' + displayName} + + ); + } return ( state.entities.channels.channels, + (state, matchTerm) => matchTerm, + (myChannels, originalChannels, matchTerm) => { + if (matchTerm === null) { + return null; + } + + let channels; + if (matchTerm) { + channels = myChannels.filter((c) => { + if (c.type === General.DM_CHANNEL && (originalChannels[c.id].display_name.startsWith(matchTerm))) { + return true; + } + if (c.type === General.GM_CHANNEL && (c.name.startsWith(matchTerm) || c.display_name.replace(/ /g, '').startsWith(matchTerm))) { + return true; + } + return false; + }); + } else { + channels = myChannels.filter((c) => { + return c.type === General.DM_CHANNEL || c.type === General.GM_CHANNEL; + }); + } + + return channels.map((c) => c.id); + } +); + export const makeGetMatchTermForDateMention = () => { let lastMatchTerm = null; let lastValue; diff --git a/app/selectors/channel.js b/app/selectors/channel.js index 9cb6e7366..6c058bccd 100644 --- a/app/selectors/channel.js +++ b/app/selectors/channel.js @@ -27,3 +27,13 @@ export const getChannelMembersForDm = createSelector( return [otherUser]; } ); + +export const getChannelNameForSearchAutocomplete = createSelector( + (state, channelId) => state.entities.channels.channels[channelId], + (channel) => { + if (channel && channel.display_name) { + return channel.display_name; + } + return ''; + } +); diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index 32c6c5e98..e2bd74a88 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -481,6 +481,7 @@ "suggestion.mention.morechannels": "Other Channels", "suggestion.mention.nonmembers": "Not in Channel", "suggestion.mention.special": "Special Mentions", + "suggestion.search.direct": "Direct Messages", "suggestion.search.private": "Private Channels", "suggestion.search.public": "Public Channels", "user.settings.display.clockDisplay": "Clock Display",