From b6274da38aef31b2a7e93bf9df0f6bf0381f7ffe Mon Sep 17 00:00:00 2001 From: Chris Duarte Date: Mon, 24 Jul 2017 05:11:52 -0700 Subject: [PATCH] RN-270 "Jump to conversation" filtering (#755) * RN-270 "Jump to conversation" filtering * Review Feedback * Change reduce filter for member details * Remove duplicate email push --- .../filtered_list/filtered_list.js | 55 +++++++++++--- .../channels_list/filtered_list/index.js | 75 +++++++++++++++++-- app/components/reactions/index.js | 4 +- 3 files changed, 115 insertions(+), 19 deletions(-) diff --git a/app/components/channel_drawer/channels_list/filtered_list/filtered_list.js b/app/components/channel_drawer/channels_list/filtered_list/filtered_list.js index 4a51cc2a5..faf776404 100644 --- a/app/components/channel_drawer/channels_list/filtered_list/filtered_list.js +++ b/app/components/channel_drawer/channels_list/filtered_list/filtered_list.js @@ -35,14 +35,13 @@ class FilteredList extends Component { currentUserId: PropTypes.string, currentChannel: PropTypes.object, groupChannels: PropTypes.array, + groupChannelMemberDetails: PropTypes.object, intl: intlShape.isRequired, teammateNameDisplay: PropTypes.string, onSelectChannel: PropTypes.func.isRequired, otherChannels: PropTypes.array, - profiles: PropTypes.oneOfType( - PropTypes.object, - PropTypes.array - ), + profiles: PropTypes.object, + teamProfiles: PropTypes.object, searchOrder: PropTypes.array.isRequired, pastDirectMessages: PropTypes.array, restrictDms: PropTypes.bool.isRequired, @@ -86,6 +85,11 @@ class FilteredList extends Component { clearTimeout(this.searchTimeoutId); this.searchTimeoutId = setTimeout(() => { + // Android has a fatal error if we send a blank term + if (!term) { + return; + } + searchProfiles(term); searchChannels(currentTeam.id, term); }, General.SEARCH_TIMEOUT_MILLISECONDS); @@ -123,7 +127,17 @@ class FilteredList extends Component { const text = term.toLowerCase(); return channels.filter((c) => { - return c.display_name.toLowerCase().includes(text); + const fieldsToCheck = ['display_name', 'username', 'email', 'full_name', 'nickname']; + + let match = false; + for (const field of fieldsToCheck) { + if (c.hasOwnProperty(field) && c[field].toLowerCase().includes(text)) { + match = true; + break; + } + } + + return match; }); }; @@ -162,7 +176,7 @@ class FilteredList extends Component { } buildCurrentDMSForSearch = (props, term) => { - const {channels, teammateNameDisplay, profiles, statuses, pastDirectMessages} = props; + const {channels, teammateNameDisplay, profiles, statuses, pastDirectMessages, groupChannelMemberDetails} = props; const {favoriteChannels} = channels; const favoriteDms = favoriteChannels.filter((c) => { @@ -170,11 +184,13 @@ class FilteredList extends Component { }); const directChannelUsers = []; - const groupChannels = []; + let groupChannels = []; channels.directAndGroupChannels.forEach((c) => { if (c.type === General.DM_CHANNEL) { - directChannelUsers.push(profiles[c.teammate_id]); + if (profiles.hasOwnProperty(c.teammate_id)) { + directChannelUsers.push(profiles[c.teammate_id]); + } } else { groupChannels.push(c); } @@ -193,7 +209,16 @@ class FilteredList extends Component { email: u.email, name: displayName, type: General.DM_CHANNEL, - fake: true + fake: true, + nickname: u.nickname, + fullname: `${u.first_name} ${u.last_name}` + }; + }); + + groupChannels = groupChannels.map((channel) => { + return { + ...channel, + ...groupChannelMemberDetails[channel.id] }; }); @@ -201,7 +226,7 @@ class FilteredList extends Component { } buildMembersForSearch = (props, term) => { - const {channels, currentUserId, teammateNameDisplay, profiles, statuses, pastDirectMessages} = props; + const {channels, currentUserId, teammateNameDisplay, profiles, teamProfiles, statuses, pastDirectMessages, restrictDms} = props; const {favoriteChannels, unreadChannels} = channels; const favoriteAndUnreadDms = [...favoriteChannels, ...unreadChannels].filter((c) => { @@ -210,7 +235,9 @@ class FilteredList extends Component { const directAndGroupChannelMembers = [...channels.directAndGroupChannels, ...favoriteAndUnreadDms].filter((c) => c.type === General.DM_CHANNEL).map((c) => c.teammate_id); - const userNotInDirectOrGroupChannels = Object.values(profiles).filter((u) => directAndGroupChannelMembers.indexOf(u.id) === -1 && pastDirectMessages.indexOf(u.id) === -1 && u.id !== currentUserId); + const profilesToUse = restrictDms ? teamProfiles : profiles; + + const userNotInDirectOrGroupChannels = Object.values(profilesToUse).filter((u) => directAndGroupChannelMembers.indexOf(u.id) === -1 && pastDirectMessages.indexOf(u.id) === -1 && u.id !== currentUserId); const members = userNotInDirectOrGroupChannels.map((u) => { const displayName = displayUsername(u, teammateNameDisplay); @@ -219,9 +246,13 @@ class FilteredList extends Component { id: u.id, status: statuses[u.id], display_name: displayName, + username: u.username, + email: u.email, name: displayName, type: General.DM_CHANNEL, - fake: true + fake: true, + nickname: u.nickname, + fullname: `${u.first_name} ${u.last_name}` }; }); diff --git a/app/components/channel_drawer/channels_list/filtered_list/index.js b/app/components/channel_drawer/channels_list/filtered_list/index.js index f2beebc7e..adb30a803 100644 --- a/app/components/channel_drawer/channels_list/filtered_list/index.js +++ b/app/components/channel_drawer/channels_list/filtered_list/index.js @@ -11,7 +11,7 @@ import {makeGroupMessageVisibleIfNecessary} from 'mattermost-redux/actions/prefe import {General} from 'mattermost-redux/constants'; import {getGroupChannels, getOtherChannels} from 'mattermost-redux/selectors/entities/channels'; import {getConfig} from 'mattermost-redux/selectors/entities/general'; -import {getProfilesInCurrentTeam, getUsers, getUserStatuses} from 'mattermost-redux/selectors/entities/users'; +import {getCurrentUserId, getProfilesInCurrentTeam, getUsers, getUserIdsInChannels, getUserStatuses} from 'mattermost-redux/selectors/entities/users'; import {getDirectShowPreferences, getTeammateNameDisplaySetting} from 'mattermost-redux/selectors/entities/preferences'; import Config from 'assets/config'; @@ -25,15 +25,78 @@ const pastDirectMessages = createSelector( (directChannelsFromPreferences) => directChannelsFromPreferences.filter((d) => d.value === 'false').map((d) => d.name) ); +const getTeamProfiles = createSelector( + getProfilesInCurrentTeam, + (members) => { + return members.reduce((memberProfiles, member) => { + memberProfiles[member.id] = member; + + return memberProfiles; + }, {}); + } +); + +// Fill an object for each group channel with concatenated strings for username, email, fullname, and nickname +function getGroupDetails(currentUserId, userIdsInChannels, profiles, groupChannels) { + return groupChannels.reduce((groupMemberDetails, channel) => { + if (!userIdsInChannels.hasOwnProperty(channel.id)) { + return groupMemberDetails; + } + + const members = Array.from(userIdsInChannels[channel.id]).reduce((memberDetails, member) => { + if (member === currentUserId) { + return memberDetails; + } + + const details = {...memberDetails}; + + const profile = profiles[member]; + details.username.push(profile.username); + if (profile.email) { + details.email.push(profile.email); + } + if (profile.nickname) { + details.nickname.push(profile.nickname); + } + if (profile.fullname) { + details.fullname.push(`${profile.first_name} ${profile.last_name}`); + } + + return details; + }, { + email: [], + fullname: [], + nickname: [], + username: [] + }); + + groupMemberDetails[channel.id] = { + email: members.email.join(','), + fullname: members.fullname.join(','), + nickname: members.nickname.join(','), + username: members.username.join(',') + }; + + return groupMemberDetails; + }, {}); +} + +const getGroupChannelMemberDetails = createSelector( + getCurrentUserId, + getUserIdsInChannels, + getUsers, + getGroupChannels, + getGroupDetails +); + function mapStateToProps(state, ownProps) { const {currentUserId} = state.entities.users; - let profiles; + const profiles = getUsers(state); + let teamProfiles = {}; const restrictDms = getConfig(state).RestrictDirectMessage !== General.RESTRICT_DIRECT_MESSAGE_ANY; if (restrictDms) { - profiles = getProfilesInCurrentTeam(state); - } else { - profiles = getUsers(state); + teamProfiles = getTeamProfiles(state); } const searchOrder = Config.DrawerSearchOrder ? Config.DrawerSearchOrder : DEFAULT_SEARCH_ORDER; @@ -42,7 +105,9 @@ function mapStateToProps(state, ownProps) { currentUserId, otherChannels: getOtherChannels(state), groupChannels: getGroupChannels(state), + groupChannelMemberDetails: getGroupChannelMemberDetails(state), profiles, + teamProfiles, teammateNameDisplay: getTeammateNameDisplaySetting(state), statuses: getUserStatuses(state), searchOrder, diff --git a/app/components/reactions/index.js b/app/components/reactions/index.js index 5c156e462..9921d3bd4 100644 --- a/app/components/reactions/index.js +++ b/app/components/reactions/index.js @@ -6,7 +6,7 @@ import {bindActionCreators} from 'redux'; import {addReaction, getReactionsForPost, removeReaction} from 'mattermost-redux/actions/posts'; import {makeGetReactionsForPost} from 'mattermost-redux/selectors/entities/posts'; -import {getCurrentUser} from 'mattermost-redux/selectors/entities/users'; +import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users'; import {getTheme} from 'app/selectors/preferences'; @@ -15,7 +15,7 @@ import Reactions from './reactions'; function makeMapStateToProps() { const getReactionsForPostSelector = makeGetReactionsForPost(); return function mapStateToProps(state, ownProps) { - const currentUserId = getCurrentUser(state); + const currentUserId = getCurrentUserId(state); const reactionsForPost = getReactionsForPostSelector(state, ownProps.postId); const highlightedReactions = [];